Flutter:如何在WebView加载页面前显示CircularProgressIndicator?

24

我正在使用webview_flutter插件,但是我找不到在网页加载之前显示CircularProgressIndicator的方法...

在Flutter中,等效于Android的WebViewClient onPageStarted/onPageFinished的是什么?

WebView(
  initialUrl: url,
  onWebViewCreated: (controller) {
  },
)


你解决了吗? - Strange
很遗憾,使用谷歌官方插件是不行的。很可惜谷歌没有实现这么简单的功能。 - rlecheta
你必须使用FutureBuilder,例如- https://gist.github.com/iapicca/2aae10a174310422e3ec515234054793 - Yash
8个回答

28
在0.3.5版本中,有'onPageFinished'回调函数。您可以使用IndexedStack创建WebView容器。

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class _WebViewContainerState extends State < WebViewContainer > {
  var _url;
  final _key = UniqueKey();
  _WebViewContainerState(this._url);
  num _stackToView = 1;

  void _handleLoad(String value) {
    setState(() {
      _stackToView = 0;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: IndexedStack(
        index: _stackToView,
        children: [
          Column(
            children: < Widget > [
              Expanded(
                child: WebView(
                  key: _key,
                  javascriptMode: JavascriptMode.unrestricted,
                  initialUrl: _url,
                  onPageFinished: _handleLoad,
                )
              ),
            ],
          ),
          Container(
            color: Colors.white,
            child: Center(
              child: CircularProgressIndicator(),
            ),
          ),
        ],
      )
    );
  }
}

class WebViewContainer extends StatefulWidget {
  final url;
  WebViewContainer(this.url);
  @override
  createState() => _WebViewContainerState(this.url);
}


0.3.5版本在iOS上运行正常,但在Android上出现了错误:注意:/Users/rlecheta/Dev/flutter/.pub-cache/hosted/pub.dartlang.org/webview_flutter-0.3.5+1/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebViewClient.java使用或覆盖了已弃用的API。程序类型已存在:com.google.common.util.concurrent.ListenableFuture 请访问https://developer.android.com/studio/build/dependencies#duplicate_classes了解如何解决此问题。 - rlecheta
升级了gradle版本后,我的项目成功执行了。但是当我尝试使用WebView时,我的应用程序会崩溃。只有在Android上出现这种情况。它在iOS上可以正常工作。您可以在此处查看完整的堆栈跟踪:https://livecom-chat.s3.amazonaws.com/livecomProd/b1fb97f9-2bdb-47a6-af81-e9d8df766f94/1553263145031/webview_error.rtf - rlecheta
没有查看代码很难确定问题。这段代码在我所有的iOS/Android设备上都可以运行。此外,请考虑此插件仍处于“开发者预览”阶段,可能是它们的错误。 - Oleksandr
虽然我遇到了一个奇怪的错误,它不断地重建自己,但是我的错,我已经修复了它。 - martinseal1987
工作得很好。谢谢。 - jignesh.world

15

这对我来说正常工作。

  initState() {
    isLoading = true;
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
        title: Text("Your Title",centerTitle: true
      ),
      body: Stack(
        children: <Widget>[
          new WebView(
            initialUrl: /* YourUrl*/,
            onPageFinished: (_) {
              setState(() {
                isLoading = false;
              });
            },
          ),
          isLoading ? Center( child: CircularProgressIndicator()) : Container(),
        ],
      ),
    );
  }

“Your Title” 后面不是缺少了 ) 吗? - phrogg
我认为你需要添加 super.initState(); 以使 initState() 函数正常工作。 - phrogg
抱歉有这么多注释,但是@override initState()函数也是必要的。 - phrogg

13
class _WebViewContainerState extends State<WebViewContainer> {
  var _url;
  final _key = UniqueKey();
  bool _isLoadingPage;

  Completer<WebViewController> _controller = Completer<WebViewController>();

  _WebViewContainerState(this._url);

  @override
  void initState() {
    super.initState();
    _isLoadingPage = true;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: <Widget>[
          new WebView(
            key: _key,
            initialUrl: _url,
            javascriptMode: JavascriptMode.unrestricted,
            onWebViewCreated: (webViewCreate) {
              _controller.complete(webViewCreate);
            },
            onPageFinished: (finish) {
              setState(() {
                _isLoadingPage = false;
              });
            },
          ),
          _isLoadingPage
              ? Container(
                  alignment: FractionalOffset.center,
                  child: CircularProgressIndicator(),
                )
              : Container(
                  color: Colors.transparent,
                ),
        ],
      ),
    );
  }
}

你好兄弟,不用谢。 - enobyte
1
无法再与 Web 视图进行交互了! - devDeejay

3
可选参数hidden和initialChild可用于在等待页面加载时显示其他内容。如果将hidden设置为true,则会显示默认的CircularProgressIndicator。如果您还额外指定了一个Widget作为initialChild,那么可以让它显示任何您想要的页面加载前的内容。
请查看此页面:flutter_webview_plugin 并且您可以使用initialChild来指定要显示的内容。
return new MaterialApp(
  title: 'Flutter WebView Demo',
  theme: new ThemeData(
    primarySwatch: Colors.blue,
  ),
  routes: {
    '/': (_) => const MyHomePage(title: 'Flutter WebView Demo'),
    '/widget': (_) => new WebviewScaffold(
      url: selectedUrl,
      appBar: new AppBar(
        title: const Text('Widget webview'),
      ),
      withZoom: true,
      withLocalStorage: true,
      hidden: true,
      initialChild: Container(
        child: const Center(
          child: CircularProgressIndicator(),
        ),
      ),
    ),
  },
);

嗨,谢谢,我会尝试使用你的解决方案。但是现在我正在使用谷歌官方的Webview插件。我认为它缺少这个功能。 - rlecheta
嗨@clipi onat,不用了,因为我正在使用“webview_flutter”插件。但还是谢谢你的帮助。 - rlecheta
这不是一个好的解决方案,对于任何试图在小部件树中内联呈现Web视图的人来说,因为flutter_webview_plugin使用了一个覆盖层。 - SacWebDeveloper

1
发现了解决方案。您可以添加 initialChild 并将属性设置为 hidden 为 true。
WebviewScaffold(
hidden: true,
  url:url,initialChild: Center(
 child: Text("Plase Wait...",style: TextStyle(
  fontWeight: FontWeight.bold,
  color: Colors.deepPurpleAccent[100]
  ),)
 ) )

1

我使用webview_flutterprogress_indicators的组合。

以下是样例可用代码:

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'dart:async';
import 'package:progress_indicators/progress_indicators.dart';




class ContactUs extends StatefulWidget {
  @override
  _ContactUsState createState() => _ContactUsState();
}

class _ContactUsState extends State<ContactUs> {

  bool vis1 = true;
  Size deviceSize;

  @override
  Widget build(BuildContext context) {

    deviceSize = MediaQuery.of(context).size;

    final lindicator = Center(
      child: AnimatedOpacity(
        // If the Widget should be visible, animate to 1.0 (fully visible). If
        // the Widget should be hidden, animate to 0.0 (invisible).
        opacity: vis1 ? 1.0 : 0.0,
        duration: Duration(milliseconds: 500),
        // The green box needs to be the child of the AnimatedOpacity
        child: HeartbeatProgressIndicator(
          child: Container(
            width: 100.0,
            height: 50.0,
            padding: EdgeInsets.fromLTRB(35.0,0.0,5.0,0.0),
            child: Row(
              children: <Widget>[
                Icon(
                  Icons.all_inclusive, color: Colors.white, size: 14.0,),
                Text(
                  "Loading View", style: TextStyle(color: Colors.white, fontSize: 6.0),),
              ],
            ),
          ),
        ),
      ),
    );

    return new Scaffold(
      appBar: new AppBar(
        title: new Row(
            children:<Widget>[
              Text('THisApp'),
              lindicator,
            ]),
        backgroundColor: Colors.red,
      ),
      body: new Container(
          child:WebView(
            initialUrl: 'https://cspydo.com.ng/',
            javaScriptMode: JavaScriptMode.unrestricted,
            onWebViewCreated: (WebViewController webViewController){
              setState(() {
                vis1=false;
              });
            },
          )
      ),
    );
  }
}

1
谢谢,但是onWebViewCreated在一开始就被调用了...我需要知道WebView何时完成页面加载。我认为插件缺少这个功能。我需要的正是Android WebViewClient的onPageFinished所做的事情。 - rlecheta

0

你可以在 isLoading 上工作,并在确保数据已正确加载后进行更改。

    class X extends StatefulWidget {
      XState createState() => XState();
    }
    class XState extends State<X>{

    bool isLoading = false;

    @override
      void initState() {
        setState(() {
          isLoading = true;
        });
        super.initState();
      }

    Widget build(BuildContext context) {
    return Scaffold(
          body: Column(
              children: <Widget>[
                  isLoading ? Center(child: CircularProgressIndicator()) : WebView(...)
              ]
            )
          );
        }
    }

1
谢谢,但问题是我找不到一种方法来知道webview何时完成页面加载。 - rlecheta
你可以使用flutter_native_web [https://pub.dartlang.org/packages/flutter_native_web] @rlecheta - Sara Vaseei
我刚刚尝试了一下,WebView永远不会渲染。 - SacWebDeveloper
再次回到这里,我只想指出这个逻辑毫无意义。如果WebView没有在树中呈现,它怎么可能处于加载状态?您必须使用包含组件上的不透明度或高度设置为零来隐藏它。在onPageFinished中,执行交换可见性的逻辑。 - SacWebDeveloper

-1

我在Flutter中使用了一个栈。

  @override
  void initState() {
    isLoading = true;
    super.initState();
  }

在构建方法中

     Stack(
        children: [
          isLoading ? Loading() : Container(),
          Container(
            child: Flex(
              direction: Axis.vertical,
              children: [
                Expanded(
                  child: InAppWebView(
                    contextMenu: contextMenu,
                    initialUrl: url,

                    onLoadSop: (InAppWebViewController controller, url) {
                      setState(() {
                        isLoading = false;
                      });
                    },
                  )
                ),
              ],
            ),
          ),
        ],
      )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接