Flutter WebView:如何从Drawer触发loadUrl切换WebView页面?
解决Drawer切换WebView URL的问题
核心问题分析
你当前的实现中,父组件更新page变量后,子组件虽会重建,但initialUrlRequest仅在WebView首次创建时生效,后续重建不会重新加载URL;而在build里直接调用loadUrl会因setState触发重复build,导致循环刷新。
解决方案步骤
- 父组件传递可识别的目标地址
将page变量改为存储具体URL,或约定标识对应的URL,确保子组件能获取到要加载的目标地址。 - 子组件通过生命周期监听参数变化
使用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
相关产品推荐
相关产品推荐

