BottomNavigatonBar 的 onTap(index) 方法上的 SetState() 不会重建小部件树

SetState() on BottomNavigatonBar's onTap(index) method does not rebuild widget tree

我正在尝试将 webview_flutter WebView 连接到 BottomNavigationBar。 我希望在点击选项卡时使用新的 URL 重新加载视图。在 onTap 回调我将 _currentUrl 更新为新的 url 并使用新的选项卡索引调用设置状态。

为什么标签栏正在更新,但网页视图没有重建? 我错过了什么?

class WebViewApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return WebViewState();
  }
}

class WebViewState extends State<WebViewApp> {
  int _currentTabIndex = 0;
  String _currentUrl = URL.home;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter WebView Demo',
      home: Scaffold(
        body: WebView(initialUrl: _currentUrl),
        bottomNavigationBar: BottomNavigationBar(
          currentIndex: _currentTabIndex,
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.home),
              title: Text('Home'),
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.search),
              title: Text('Search'),
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.calendar_today),
              title: Text('Profile'),
            ),
          ],
          onTap: (index) {
            switch (index) {
              case 0:
                _currentUrl = URL.home;
                break;
              case 1:
                _currentUrl = URL.search;
                break;
              case 2:
                _currentUrl = URL.calendar;
                break;
            }
            setState(() {
              _currentTabIndex = index;
            });
          },
        ),
      ),
    );
  }
}

您可以使用 WebView 的 WebViewController 来更改 url 的 webview

将以下代码添加到您的 WebView 小部件:

body: WebView(
  initialUrl: _currentUrl,
  onWebViewCreated: (WebViewController webController) {
    _webController = webController;
  },
),

现在,在您将 WebViewController 分配给您的 WebViewController 实例后,您可以使用 WebViewController

的 loadUrl() 方法更改当前 webview 的 url

因此您可以将 onTap 处理程序的代码更改为以下内容:

onTap: (index) {
  switch (index) {
    case 0:
      _webController.loadUrl('your_home_url');
    break;
    case 1:
      _webController.loadUrl('your_search_url');
    break;
    case 2:
      _webController.loadUrl('your_calander_url');
    break;
  }
},

WebViews 和 setState 的问题是整个 Widget / Page 将被重建,包括 WebView 的实例,但您只想更改当前 WebView 会话中的 url...

感谢乔尔的评论

是的,发布一个跨平台框架很奇怪,基本组件只能在一个平台上工作,但是是的,希望他们能在 10 月之前修复它,因为他们 github...

我检查了我的 post,WebView 应该加载新的 url。我忘记的是在导航的 onTap 处理程序中更新 _currentTabIndex

但无论如何:我为你的案例做了一个简单的例子。对我来说很好用...

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

import 'package:webview_flutter/webview_flutter.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {    
    return MaterialApp(
      title: 'WebView App',
      debugShowCheckedModeBanner: false,

      home: WebPage(),
    );
  }
}

class WebPage extends StatefulWidget {
  int currentTabIndex = 0;

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

class _WebPageState extends State<WebPage> {
  List<String> _urls = [
    'https://www.zdf.de/nachrichten/heute/rubrik-politik-100.html',
    'https://www.zdf.de/nachrichten/heute/rubrik-wirtschaft-100.html',
    'https://www.zdf.de/nachrichten/heute/rubrik-panorama-100.html'
  ];

  WebViewController _webController;

  @override
  Widget build(BuildContext context) {
    print('<----- build init ----->');

    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: Text('WebView'),
      ),

      body: WebView(
        initialUrl: _urls[widget.currentTabIndex],
        javascriptMode: JavascriptMode.unrestricted,
        onWebViewCreated: (WebViewController webController) {
          _webController = webController;
        },
      ),

      bottomNavigationBar: BottomNavigationBar(
        currentIndex: widget.currentTabIndex,
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.people),
            title: Text('Politik')
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.public),
            title: Text('Wirtschaft')
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.pie_chart_outlined),
            title: Text('Panorama')
          )
        ],
        onTap: (selectedIndex) {
          _webController.loadUrl(_urls[selectedIndex]);         

          setState(() {
            widget.currentTabIndex = selectedIndex;
          });
        },
      ),
    );
  }
}

请注意,我在重建页面的 onTap 处理程序末尾调用了 setState(),但奇怪的是 WebView 不会重新初始化!?我认为一定是这样,但是 flutter 必须以某种方式处理这个问题......

另请注意,我将您的 _currentTabIndex 变量从您的 WebViewState class 移至其上层父项 class WebViewApp。问题是,如果您通过 setState() 重建页面,_currentTabIndex 将始终重新初始化为 0。为了教程的缘故,我只是将它向上移动,但也许您可以将它用作全局参数。 .

但是,是的,WebView 仍然有点令人困惑,它在 iOS 和 Android WebView 套件之间有很多差异(在一天结束时,flutter WebView 是 "only" 当前原生 WebView 套件的包装器)

希望对您有所帮助,祝您好运!