At present, I have a demand, is to develop a news APP, similar to the headlines, there are news display, user comments and likes, favorites and so on, the back-end interface has been designed, this function is mainly through accessing the URL to use webView to achieve this function, a word is not the beginning of the code:

  • Load the Flutter Webview plugin
  • Customize the webView display interface
  • Second customizations to official plug-ins
  • The URL load listening is complete

1. Load the Flutter Webview plugin

flutter_webview_plugin: ^0.3. 0+2
Copy the code

2. Customize the WebView display interface

This page is mainly composed of three parts, the top part is the native page of flutter, the middle part is the WebView page, and the bottom part is implemented by the bottomNavigationBar component, as shown in the code.

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
          title: Text('Web Page Details'),
          leading: IconButton(
            icon:Icon(Icons.arrow_back_ios,size:20) ,
            onPressed: (){
              _webviewReference.hide(a);String url = 'http://www.appshuo.club/appview/p/';
              MyRouter.pushNoParams(context, MyRouter.homePage);
            },),
          bottom: PreferredSize(
            child: _progressBar(lineProgress, context),
            preferredSize: Size.fromHeight(0.1),
          )
      ),
      body: SafeArea(
      child:  SingleChildScrollView(
              child: Padding(
          padding: EdgeInsets.all(10),
      child: Column(
          children: [
            Padding(
              padding: EdgeInsets.only(bottom: 10),
             child:Row(
              children: [
                CircleAvatar(
                  radius: 20.0,
                  backgroundImage: NetworkImage(widget.article.user.photo),
                  backgroundColor: Colors.white,
                ),
                Padding(
                  padding: const EdgeInsets.only(left: 10.0),
                  child: Text(widget.article.user.nickname),
                ),
                Padding(
                  padding: const EdgeInsets.only(left: 15.0),
                  child: Text('read',style: TextStyle(color: Colors.black38,fontSize: 13.0)),
                ),
                Padding(
                  padding: const EdgeInsets.only(left: 3.0),
                  child: Text('${widget.article.readcount}',style: TextStyle(color: Colors.black38,fontSize: 13.0)),
                ),
                Expanded(
                  child: GestureDetector(
                    child: Padding(
                        padding: EdgeInsets.only(left: ScreenUtils.screenW()/2- 50,right: 10.0),
                        child: new SizedBox(
                            width: 50,
                            child: FlatButton(
                                  shape: RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(0.0),
                                  side: BorderSide(color: Colors.green)),
                                  color: Colors.white,
                                  textColor: Colors.green,
                                  padding: EdgeInsets.all(8.0),
                                  onPressed: () {},
                                  child: Text(
                                    "+ concern",
                                    style: TextStyle(
                                      fontSize: 14.0,
                                    ),
                              ),
                            ),
                        ),
                    ),
                    onTap: () {
                      MyRouter.pushNoParams(context,MyRouter.userPage);
                    },
                  ),
                )
              ],
            )),
            Container(
              height: ScreenUtils.screenH()-ScreenUtils.padTopH() - 120.0 - 40.0,
              child: MyFlutterWebViewPage(url),
            ),
          ],
        ),
      )),
    ),
      bottomNavigationBar: BottomAppBar(
        child: talkFeild(),
      ),

    );
  }

Widget talkFeild(){
    return Row(
      children: <Widget>[
        Expanded(
          child: Container(
            margin: EdgeInsets.only(left: 20.0,top:5.0,bottom: 5.0),
            width: MediaQuery.of(context).size.width,
            alignment: AlignmentDirectional.center,
            height: this.h,
            decoration: BoxDecoration(
                color: Color.fromARGB(255.237.236.237),
                borderRadius: BorderRadius.circular(24.0)),
            child: TextField(
              // onSubmitted: onSubmitted,
              onTap: (){
                _webviewReference.hide(a); MyRouter.push(context, MyRouter.writeCommentPage, {'articleId': widget.article.id, 'article': widget.article});
              },
              cursorColor: Color.fromARGB(255.0.189.96),
              decoration: InputDecoration(
                  contentPadding: const EdgeInsets.only(left: 10.0,bottom:12.0),
                  border: InputBorder.none,
                  hintText: 'Say a word... ',
                  hintStyle: TextStyle(
                      fontSize: 15, color: Color.fromARGB(255.192.191.191)),
                  ),
              style: TextStyle(fontSize: 15),
            ),
          ),
        ),
        GestureDetector(
          child: Padding(
            padding: EdgeInsets.only(left: 20.0),
            child: IconText(context, widget.article.commentCount == 0 ? 'comments' : widget.article.commentCount, Constant.ASSETS_IMG +
                'ic_notification_tv_calendar_comments.png'),
          ),
          onTap: () {
            _webviewReference.hide(a); MyRouter.push(context, MyRouter.commentList, {'articleId': widget.article.id, 'article': widget.article});
          },
        ),
        GestureDetector(
          child: Padding(
              padding: EdgeInsets.only(left: 20.0, right: 20.0),
              child: IconText(context, widget.article.praiseCount == 0 ? 'good' : widget.article.praiseCount, Constant.ASSETS_IMG + 'ic_vote.png'), ), onTap: () { Navigator.pop(context); },)],); } Row textField() {return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      crossAxisAlignment: CrossAxisAlignment.end,
      children: <Widget>[
        Expanded(
          child: new TextField(
            decoration: InputDecoration(
              hintText: 'I look forward to your comments... ',
              border: null,
              focusedBorder: UnderlineInputBorder(
                borderSide: BorderSide(color: Colors.blue[300]),
              ),
            ),
            keyboardType: TextInputType.text,
            maxLength: 250,
            maxLines: 1,
          ),
        ),
        IconButton(
          icon: Icon(Icons.send),
          onPressed: () {
            _webviewReference.show(a); Navigator.of(context).pop(); },)],); } TextStyle getStyle(Color color,double fontSize, {bool bold = false{})returnTextStyle( color: color, fontSize: fontSize, fontWeight: bold ? FontWeight.bold : FontWeight.normal); }}Copy the code

3. Secondary customization of official plug-ins

NotifyRect () is used to create a rectangle above the interface, and the _webViewReference.launch method is used to load the URL in the specified rectangle

import 'dart:async';

import 'package:duo_zui/Util/screen_utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';

class MyFlutterWebViewPage extends StatefulWidget {
  final String url;
  bool hidden = false;

  MyFlutterWebViewPage(this.url, {Key key}) : super(key: key);

  @override
  _WebViewWidgetState createState() => _WebViewWidgetState();
}

class _WebViewWidgetState extends State<MyFlutterWebViewPage>  {

  final webviewReference = FlutterWebviewPlugin();

  bool _closed = false;

  Rect _rect;
  bool needFullScreen = false;
  Timer _resizeTimer;

  StreamSubscription<WebViewStateChanged> _onStateChanged;

  @override
  void initState() {
    super.initState();
    webviewReference.close();
    if (widget.hidden) {
      _onStateChanged =
          webviewReference.onStateChanged.listen((WebViewStateChanged state) {
            if (state.type == WebViewState.finishLoad) {
              webviewReference.show(a); }}); }}@override
  void dispose() {
    super.dispose();
    webviewReference.close();
    webviewReference.dispose();
    if(widget.hidden) { _onStateChanged.cancel(); }}@override
  Widget build(BuildContext context) {
    print('build widget.url=\\\\${widget.url}');
    return _WebviewPlaceholder(onRectChanged: (Rect value) {
      if (_rect == null || _closed) {
        if(_rect ! = value){ _rect = value; }print('_webviewReference.launch');
        webviewReference.launch(widget.url,
            withJavascript: true,
            withLocalStorage: true,
            scrollBar: true,
            rect: getRect());
      } else {
        print('_webviewReference.launch else');
        // if (_rect ! = value) {
        // _rect = value;
        // }
        // webviewReference.reloadUrl(widget.url);
        if(_rect ! = value) { _rect = value; _resizeTimer? .cancel(); _resizeTimer = Timer(const Duration(milliseconds: 250), () {
            // avoid resizing to fast when build is called multiple time
            webviewReference.resize(_rect);
          });
        }
      }
    }, child: const Center(child: const CircularProgressIndicator()),);
  }

  getRect() {
    if(needFullScreen){
      return null;
    }else{
      return Rect.fromLTRB(0.0, ScreenUtils.padTopH() + 120.0,
          ScreenUtils.screenW(), ScreenUtils.screenH() - 40.0); }}}class _WebviewPlaceholder extends SingleChildRenderObjectWidget {
  const _WebviewPlaceholder({
    Key key,
    @required this.onRectChanged,
    Widget child,
  }) : super(key: key, child: child);

  final ValueChanged<Rect> onRectChanged;

  @override
  RenderObject createRenderObject(BuildContext context) {
    return _WebviewPlaceholderRender(
      onRectChanged: onRectChanged,
    );
  }

  @override
  void updateRenderObject(
      BuildContext context, _WebviewPlaceholderRender renderObject) {
    renderObject..onRectChanged = onRectChanged;
  }
}

class _WebviewPlaceholderRender extends RenderProxyBox {
  _WebviewPlaceholderRender({
    RenderBox child,
    ValueChanged<Rect> onRectChanged,
  })  : _callback = onRectChanged,
        super(child);

  ValueChanged<Rect> _callback;
  Rect _rect;

  Rect get rect => _rect;

  set onRectChanged(ValueChanged<Rect> callback) {
    if (callback != _callback) {
      _callback = callback;
      notifyRect();
    }
  }

  void notifyRect() {
    if(_callback ! =null&& _rect ! =null) { _callback(_rect); }}@override
  void paint(PaintingContext context, Offset offset) {
    super.paint(context, offset);
    final rect = offset & size;
    if(_rect ! = rect) { _rect = rect; notifyRect(); }}}Copy the code

4. Complete listening for url loading

Finally, to realize the progress bar of loading URL:

  _progressBar(double progress, BuildContext context) {
    return Container(
      child: LinearProgressIndicator(
        backgroundColor: Colors.blueAccent.withOpacity(0),
        value: progress == 1.0 ? 0 : progress,
        valueColor: new AlwaysStoppedAnimation<Color>(Colors.lightBlue),
      ),
      height: 1,); }Copy the code

The whole process of customization is finished. It is rare to customize webView materials on the Internet, and usually the whole page is loaded. The above is one of the solutions, I hope it can help you.

In the end, I put out my question, hoping that someone who sees my question can answer my question and make progress together. 1. All the pages of the Flutter webview are native widgets that cover flutter, so my comment box cannot be displayed when I want to make a comment. 2. How does the Flutter Webview append native Flutter widgets to articles, such as my comment list and the following recommendation news? My current implementation is to put the comment list into the web page.