如何在Flutter中在webview成功加载之前显示本地图像?

3
在Flutter中,在我的webview加载之前,我想显示一张本地图片。 如果用户没有打开他们的wifi,则会显示一张图片,而不是一个空白屏幕(iOS)或错误消息(Android),提示无法连接到该网页。
我正在使用此应用程序的官方webview_flutter包。
以下是我尝试过的代码,但它在iOS上运行正常,但在Android上却无法正常工作。 在Android上,当我关闭WiFi并启动应用程序时,将显示一个错误消息,其中显示Webview链接。
编辑:在WiFi连接后按下重新加载按钮后,不会从图像切换到Webview。
final webViewKey1 = GlobalKey<WebViewContainerState>();
var _url = 'http://www.google.com';
final _key = UniqueKey();
bool _isLoadingPage;

class WebViewPage extends StatefulWidget {
  @override
  WebViewPageState createState() => WebViewPageState();
}

class WebViewPageState extends State<WebViewPage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Screen 1'),
        leading: IconButton(
          icon: Icon(Icons.menu),
          onPressed: (){
            Scaffold.of(context).openDrawer();
          },
        ),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.refresh), 
            color: Colors.white,
            onPressed: () {
              webViewKey1.currentState?.reloadWebView();
            },
          ),
        ]
      ),
      body: 
          WebViewContainer(key: webViewKey1),   
    );
  }
}

class WebViewContainer extends StatefulWidget {
  WebViewContainer({Key key}) : super(key: key);

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

class WebViewContainerState extends State<WebViewContainer> {
  WebViewController _webViewController;

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

  @override
  Widget build(BuildContext context) {
    return Stack(
        children: <Widget>[
          Opacity(opacity: _isLoadingPage?0:1, child: WebView(
            key: _key,
            initialUrl: _url,
            javascriptMode: JavascriptMode.unrestricted,
            onWebViewCreated: (controller) {
              _webViewController = controller;
            },
            onPageFinished: (finish) {
              setState(() {
                _isLoadingPage = false;
              });
            },
          ),
          ),
          _isLoadingPage
            ? Container(
              decoration: BoxDecoration(
                image: DecorationImage(
                  image: AssetImage('assets/images/fail.png'),
                  fit: BoxFit.fill,
                ),
              ),
            )
            : Container(
              color: Colors.transparent,
            ),
        ],
      );
  }

  void reloadWebView() {
    _webViewController?.reload();
  }
}
3个回答

1
使用不透明度小部件使其不可见,并在完成后显示它。
Opacity(opacity: _isLoadingPage?0:1, child: WebView(
          key: _key,
          initialUrl: _url,
          javascriptMode: JavascriptMode.unrestricted,
          onWebViewCreated: (webViewCreate) {
            _controller.complete(webViewCreate);
          },
          onPageFinished: (finish) {
            setState(() {
              _isLoadingPage = false;
            });
          },
        ),)

当我在Android上尝试在wifi断开连接时重新加载webview时,仍然会显示错误消息“网页不可用”,有什么解决方法吗? - imgingerbear

0

你可以使用我的插件flutter_inappwebview,它有很多事件,包括在 WebView 加载 URL 时处理错误的事件(如 net::ERR_ADDRESS_UNREACHABLE)(onLoadError 事件),以及当它接收到 HTTP 错误,如 403、404 等时处理错误的事件 (onLoadHttpError 事件)。

你可以使用 connectivity 插件通过订阅由 connectivity 插件公开的流来监听网络状态的更改。

此外,你可以使用 IndexedStack 并在 WebView 加载完成后使用 onLoadStop 切换小部件之间的显示。

完整示例:

import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';

import 'package:connectivity/connectivity.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

Future main() async {
  runApp(new MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: InAppWebViewPage()
    );
  }
}

class InAppWebViewPage extends StatefulWidget {
  @override
  _InAppWebViewPageState createState() => new _InAppWebViewPageState();
}

class _InAppWebViewPageState extends State<InAppWebViewPage> {
  InAppWebViewController webView;
  int _page = 2;
  bool _loadError = false;
  StreamSubscription<ConnectivityResult> subscription;

  @override
  initState() {
    super.initState();
    subscription = Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
      if (result != ConnectivityResult.none && webView != null) {
        print("reload");
        _loadError = false;
        webView.reload();
      }
    });
  }

  @override
  dispose() {
    super.dispose();
    subscription.cancel();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
            title: Text("InAppWebView")
        ),
        body: IndexedStack(
          index: _page,
          children: <Widget>[
            InAppWebView(
              initialUrl: "https://flutter.dev",
              initialHeaders: {},
              initialOptions: InAppWebViewWidgetOptions(
                  inAppWebViewOptions: InAppWebViewOptions(
                    clearCache: true,
                    debuggingEnabled: true,
                  ),
              ),
              onWebViewCreated: (InAppWebViewController controller) {
                webView = controller;
              },
              onLoadStart: (InAppWebViewController controller, String url) {

              },
              onLoadStop: (InAppWebViewController controller, String url) {
                print(url);
                setState(() {
                  if (!_loadError) {
                    _page = 0;
                  } else {
                    _page = 1;
                  }
                });
              },
              onLoadError: (InAppWebViewController controller, String url, int code, String message) async {
                print("error $url: $code, $message");
                _loadError = true;
              },
              onLoadHttpError: (InAppWebViewController controller, String url, int statusCode, String description) async {
                print("HTTP error $url: $statusCode, $description");
              },
            ),
            (Platform.isAndroid) ? Container(
                child: Text("My custom error message"),
            ) : Container(
                decoration: BoxDecoration(
                  image: DecorationImage(
                    image: AssetImage('assets/images/fail.png'),
                    fit: BoxFit.fill,
                  ),
                )
            ),
            Container(
              decoration: BoxDecoration(
                image: DecorationImage(
                  image: AssetImage('assets/images/loading.jpg'),
                  fit: BoxFit.fill,
                ),
              )
            )
          ],
        ),
    );
  }
}

0

您可以使用IndexedStack并在WebView加载完成后使用onPageFinished切换小部件。 您可以使用connectivity插件检查网络连接。

ConnectivityResult _connectionStatus;
  final Connectivity _connectivity = Connectivity();
  StreamSubscription<ConnectivityResult> _connectivitySubscription;
  int _page = 1;

  @override
  void initState() {
    super.initState();
    initConnectivity();
    _connectivitySubscription =
        _connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
  }

  @override
  void dispose() {
    _connectivitySubscription.cancel();
    super.dispose();
  }

  Future<void> initConnectivity() async {
    ConnectivityResult result;
    // Platform messages may fail, so we use a try/catch PlatformException.
    try {
      result = await _connectivity.checkConnectivity();
    } on PlatformException catch (e) {
      print(e.toString());
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) {
      return;
    }

    _updateConnectionStatus(result);
  }

  @override
  Widget build(BuildContext context) {
    print(_connectionStatus);
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: IndexedStack(
        index: _page,
        children: <Widget>[
          WebView(
            initialUrl: 'https://www.google.com/',
            onPageFinished: (_) {
              if (_connectionStatus != ConnectivityResult.none) {
                setState(() {
                  _page = 0;
                });
              }
            },
          ),
          Container(
            child: Center(
              child: Text('Some Image'),
            ),
          ),
        ],
      ),
    );
  }

  Future<void> _updateConnectionStatus(ConnectivityResult result) async {
    switch (result) {
      case ConnectivityResult.wifi:
      case ConnectivityResult.mobile:
      case ConnectivityResult.none:
        setState(() => _connectionStatus = result);
        break;
      default:
        setState(() => _connectionStatus = result);
        break;
    }
  }


当我在Android上尝试在wifi断开连接时重新加载webview时,仍然会显示错误消息“网页不可用”,有什么解决方法吗? - imgingerbear
我已经更新了答案。在这里,我正在检查连接并更新小部件。 - divyanshu bhargava
当连接了WiFi时,图像不会重新加载以显示Web视图,即使我按下重新加载按钮,图像仍然卡住并且无法显示Web视图,有任何想法为什么会发生这种情况? - imgingerbear
代码在编辑器中,我尝试了你的方法,但是即使在连接了WiFi的情况下启动应用程序时它仍然显示图像。 - imgingerbear

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