【问题标题】:SetState() on BottomNavigatonBar's onTap(index) method does not rebuild widget treeBottomNavigatonBar 的 onTap(index) 方法上的 SetState() 不会重建小部件树
【发布时间】:2019-06-24 15:03:01
【问题描述】:

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

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

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;
            });
          },
        ),
      ),
    );
  }
}

【问题讨论】:

    标签: flutter flutter-layout setstate


    【解决方案1】:

    你可以使用 WebView 的 WebViewController 来改变 webview 的 url

    将以下代码添加到您的 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...

    【讨论】:

    • 这就是我一直在寻找的!很遗憾webview_flutter 不支持安卓键盘输入。我知道重新加载整个布局对性能不利,但在这种情况下它根本不会重新加载!猜猜为什么?
    【解决方案2】:

    感谢乔尔的评论

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

    我检查了我的帖子,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 不会重新初始化!?我认为一定是这样,但颤振一定是在以某种方式处理这个......

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

    但是,是的,WebView 仍然有点令人困惑,它在 iOS 和 Android WebView 套件之间有很多差异(归根结底,flutter WebView 是“仅”当前本机 WebView 套件的包装器)

    我希望这会有所帮助,祝你好运!

    【讨论】:

    • 您实际上可以将 currentTabIndex 移回状态。不会在每次状态改变时重新启动。事实上,把它放在 StatelessWidget 中会产生一个警告说This class (or a class which this class inherits from) is marked as '@immutable', but one or more of its instance fields are not final: WebPage.currentTabIndex 我的评论是关于为什么当我更改 currentUrl 变量时没有重建 WebView。无论哪种方式 _webController 都是我正在寻找的答案,所以我将在正确的答案中标记第一个答案。谢谢!
    • 虽然您可以将setState(() { currentTabIndex = selectedIndex; }); 添加到 onTap 回调中以更新状态:)
    猜你喜欢
    • 2021-09-19
    • 1970-01-01
    • 2019-01-14
    • 1970-01-01
    • 1970-01-01
    • 2020-09-20
    • 1970-01-01
    • 1970-01-01
    • 2020-08-08
    相关资源
    最近更新 更多