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

Flutter InAppWebView搭配IndexedStack时控制器仅对最后一个页面生效

问题原因

你只定义了单个InAppWebViewController?类型的变量存储控制器,而IndexedStack初始化时会一次性构建所有子节点,3个WebView会按顺序触发onWebViewCreated回调,每触发一次就会覆盖一次webViewController变量的值,最终变量里存的永远是最后一个加载完成的WebView实例的控制器,所以所有控制器操作只会作用在最后一个页面上。

修复方法

不需要替换IndexedStack,只需要把单控制器存储改成按索引一一对应的控制器集合,操作时始终取当前选中页面对应的控制器即可,完全保留页面不重载、状态不丢失的特性。
具体修改点:

  • 删除原来的单控制器变量,新增和网页列表长度一致的控制器数组
  • 每个WebView触发onWebViewCreated时,按循环索引把控制器存入数组对应位置
  • 所有调用控制器逻辑的位置(比如返回键拦截、JS调用、网页控制等),都通过当前选中的索引_currentIndex取对应位置的控制器操作
  • 给每个WebView添加唯一的ValueKey,避免组件重建时状态混乱

修复后可直接运行的核心代码
class _ShowItemsState extends State<ShowItems> {

List services = [
{
  'service_name': 'expample1',
  'website_link': 'https://www.example1.com/',
  'icon': const Icon(Icons.location),
  'color': const Color(0xffE23744)
},
{
  'service_name': 'expample2',
  'website_link': 'https://www.expample2.com/',
  'icon': const Icon(Icons.location_on),
  'color': const Color(0xfff18423)
},
{
  'service_name': 'expample3',
  'website_link': 'https://www.expample3.com/',
  'icon': const Icon(Icons.location),
  'color': const Color(0xff801A00)
},
];
// 按索引存储每个网页对应的控制器
final List<InAppWebViewController?> _webControllers = List.filled(3, null);
int _currentIndex = 0;

@override
Widget build(BuildContext context) {
return WillPopScope(
  onWillPop: () async {
    // 仅操作当前显示页面对应的控制器
    final currentController = _webControllers[_currentIndex];
    if (await currentController?.canGoBack() == true) {
      currentController?.goBack();
      return false;
    }
    return true;
  },
  child: LayoutBuilder(
    builder: (context, constraints) {
      final cmh = constraints.maxHeight;
      return Scaffold(
        appBar: AppBar(
          backgroundColor: services[_currentIndex]['color'],
          elevation: 0,
          title: Text(services[_currentIndex]['service_name']),
          actions: [
            IconButton(
              onPressed: () {
                Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => const SettingsScreen(),
                    ));
              },
              icon: const Icon(Icons.settings),
              splashRadius: cmh * 0.021,
            )
          ],
        ),
        body: IndexedStack(
          index: _currentIndex,
          children: [
            for (int x = 0; x < services.length; x++)
              InAppWebView(
                key: ValueKey('webview_$x'),
                initialUrlRequest: URLRequest(
                    url: Uri.parse(services[x]['website_link'])),
                onWebViewCreated: (controller) {
                  _webControllers[x] = controller;
                },
                initialOptions: InAppWebViewGroupOptions(
                  android: AndroidInAppWebViewOptions(
                    allowContentAccess: true,
                    builtInZoomControls: true,
                    thirdPartyCookiesEnabled: true,
                    allowFileAccess: true,
                    geolocationEnabled: true,
                    supportMultipleWindows: true,
                  ),
                  crossPlatform: InAppWebViewOptions(
                    useShouldOverrideUrlLoading: true,
                    useOnDownloadStart: true,
                    allowFileAccessFromFileURLs: true,
                    allowUniversalAccessFromFileURLs: true,
                  ),
                ),
              ),
          ],
        ),
        bottomNavigationBar: BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          selectedIconTheme: IconThemeData(
              color: services[_currentIndex]['color'] ?? Colors.amber),
          selectedItemColor: services[_currentIndex]['color'] ?? Colors.amber,
          currentIndex: _currentIndex,
          items: [
            for (int i = 0; i < services.length; i++)
              BottomNavigationBarItem(
                label: services[i]['service_name'],
                icon: services[i]['icon'],
              ),
          ],
          onTap: (int index) {
            setState(() {
              _currentIndex = index;
            });
          },
        ),
      );
    },
  ),
);
}
}

原代码中多余的PageController是为PageView组件设计的,和IndexedStack无关,可以直接删除避免无用代码干扰。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 15:09:33