Search code examples
flutterflutter-layoutsetstate

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


I'm trying to connect a webview_flutter WebView to a BottomNavigationBar. I want the view to be reloaded with a new URL whenever a tab is tapped. In the onTap callback I update the _currentUrl to a new url and call set state with the new tab index.

Why is the tab bar updating but the web view is not rebuilt? What did I miss?

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

Solution

  • You can use the WebView's WebViewController to change url of the webview

    Add following code to your WebView widget:

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

    Now after you have assiged the WebViewController to your instance of WebViewController, you can change the url of the current webview with the loadUrl() method of the WebViewController

    So you can change the code of you onTap handler to following:

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

    The problem with WebViews and setState is that the whole Widget / Page will be rebuild including the instance of WebView, but you only want to change the url in the current WebView session...