You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter WebView:如何从Drawer触发loadUrl切换WebView页面?

解决Drawer切换WebView URL的问题

核心问题分析

你当前的实现中,父组件更新page变量后,子组件虽会重建,但initialUrlRequest仅在WebView首次创建时生效,后续重建不会重新加载URL;而在build里直接调用loadUrl会因setState触发重复build,导致循环刷新。

解决方案步骤

  1. 父组件传递可识别的目标地址
    将page变量改为存储具体URL,或约定标识对应的URL,确保子组件能获取到要加载的目标地址。
  2. 子组件通过生命周期监听参数变化
    使用didUpdateWidget方法监听widget.page的变化,在参数更新且WebViewController已初始化时调用loadUrl切换地址,避免在build中执行引发循环。

修改后的代码示例

app.dart 部分

// 初始化默认加载地址
String page = "https://google.com";

Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
      child: HeadlessInAppWebViewExampleScreen(page: page),
    ),
    drawer: Drawer(
      child: Container(
        color: Colors.black,
        child: ListView(
          children: [
            ListTile(
              title: Text("加载新站点", style: TextStyle(color: Colors.grey)),
              onTap: () {
                setState(() {
                  // 设置要切换的目标URL
                  page = "https://example.com";
                });
                // 点击后关闭Drawer
                Navigator.pop(context);
              },
            ),
          ],
        ),
      ),
    ),
  );
}

HeadlessInAppWebViewExampleScreen 部分

class HeadlessInAppWebViewExampleScreen extends StatefulWidget {
  final String page;
  
  const HeadlessInAppWebViewExampleScreen({Key? key, required this.page}) : super(key: key);

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

class _HeadlessInAppWebViewExampleScreenState
    extends State<HeadlessInAppWebViewExampleScreen> {
  InAppWebViewController? webViewController;
  final GlobalKey webViewKey = GlobalKey();
  InAppWebViewSettings settings = InAppWebViewSettings(
      useShouldOverrideUrlLoading: true,
      mediaPlaybackRequiresUserGesture: false,
      allowsInlineMediaPlayback: true,
      iframeAllow: "camera; microphone",
      iframeAllowFullscreen: true
  );
  double progress = 0;
  final urlController = TextEditingController();

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

  // 监听组件参数变化
  @override
  void didUpdateWidget(covariant HeadlessInAppWebViewExampleScreen oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 当page参数改变且Web控制器已初始化时,加载新URL
    if (oldWidget.page != widget.page && webViewController != null) {
      webViewController?.loadUrl(
        urlRequest: URLRequest(url: WebUri(widget.page))
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(children: <Widget>[
          Expanded(
            child: Stack(
              children: [
                InAppWebView(
                  key: webViewKey,
                  initialUrlRequest: URLRequest(url: WebUri(widget.page)),
                  initialSettings: settings,
                  onWebViewCreated: (controller) {
                    webViewController = controller;
                  },
                  onPermissionRequest: (controller, request) async {
                    return PermissionResponse(
                        resources: request.resources,
                        action: PermissionResponseAction.GRANT);
                  },
                  shouldOverrideUrlLoading: (controller, navigationAction) async {
                    var uri = navigationAction.request.url;
                    if (![
                      "http",
                      "https",
                      "file",
                      "chrome",
                      "data",
                      "javascript",
                      "about"
                    ].contains(uri.scheme)) {
                      if (await canLaunchUrl(uri)) {
                        await launchUrl(uri);
                        return NavigationActionPolicy.CANCEL;
                      }
                    }
                    return NavigationActionPolicy.ALLOW;
                  },
                  onProgressChanged: (controller, progress) {
                    // 重新启用进度更新,不会引发循环
                    setState(() {
                      this.progress = progress / 100;
                    });
                  },
                  onConsoleMessage: (controller, consoleMessage) {
                    print(consoleMessage);
                  },
                ),
                // 显示加载进度条
                if (progress < 1.0)
                  LinearProgressIndicator(value: progress)
              ],
            ),
          ),
        ])
      )
    );
  }
}

关键说明

  • didUpdateWidget是Flutter专门用于监听组件参数变化的生命周期方法,仅在widget属性更新时触发,避免了build方法重复执行导致的循环问题。
  • 父组件更新page后,子组件感知到变化,通过已初始化的webViewController直接调用loadUrl切换地址,无需重建整个WebView。
  • 现在可以正常启用进度更新逻辑,因为只有加载进度变化时才会触发setState,不会与URL切换逻辑形成循环。

内容的提问来源于stack exchange,提问作者Szmerd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 04:50:14