1

I am making a simple tab bar based app with Flutter. It uses CupertinoTabScaffold and CupertinoTabBar. Each CupertinoTabView has its own WebView.

In the first TabView, a button is placed that if clicked the selected tab will be changed to the next one to load a CupertinoTabView containing a WebView widget, then navigate to the certain website URL (note that the URL is not written on WebView:initialURL, but retrieved from the first tab).

The problem is that when clicking the button, while the selected tab is changed, WebView does not load a specific URL because the WebView controller is not assigned.

How to make the app 'wait' for the new CupertinoTabView widget to be fully loaded?

If I run the app, the error is the following:

Unhandled Exception: NoSuchMethodError: The method 'navigateTo' was called on null.

This seems because when clicking the button to execute the following code, before the line tabController.index = tabIndex will take effect, keyWebPage.curentState.navigateTo (which is null yet) is called.

tabController.index = tabIndex;
keyWebPage.currentState.navigateTo(url); //executed 'before' the new TabView initialization

I suspect the reason for this problem, but couldn't figure out how to solve it.

main.dart

final GlobalKey<PageWebState> keyWebPage = GlobalKey();

class _MyHomePageState extends State<MyHomePage> {
  final CupertinoTabController tabController = CupertinoTabController();
  int _currentTabIndex = 0;

  WebPage pageWeb;

  @override
  void initState() {
    super.initState();

    pageWeb = new PageWeb(
      this,
      key: keyWebPage,
    );
  }

  void navigateTab(int tabIndex, String url) {
    setState(() {
      _currentTabIndex = tabIndex;
      tabController.index = tabIndex;

      // HERE is the error occurring part
      keyWebPage.currentState.navigateTo(url);
    });
  }

  @override
  Widget build(BuildContext context) {
    final Widget scaffold = CupertinoTabScaffold(
      controller: tabController,
      tabBar: CupertinoTabBar(
        currentIndex: _currentTabIndex,
        onTap: (index) {
          setState(() {
            _currentTabIndex = index;
          });
        },
        items: const <BottomNavigationBarItem>[
          const BottomNavigationBarItem(icon: const Icon(Icons.home), title: Text('Start')),
          const BottomNavigationBarItem(icon: const Icon(Icons.web), title: Text('Webpage')),
        ],
      ),
      tabBuilder: (context, index) {
        CupertinoTabView _viewWidget;
        switch (index) {
          case 0:
            _viewWidget = CupertinoTabView(
              builder: (context) {
                return Center(
                  child: CupertinoButton(
                    child: Text('Navigate'),
                    onPressed: () {
                      navigateTab(1, 'https://stackoverflow.com/');
                    },
                  ),
                );
              },
            );
            break;
          case 1:
            _viewWidget = CupertinoTabView(
              builder: (context) {
                return pageWeb;
              },
            );
            break;
        }
        return _viewWidget;
      },
    );

    return scaffold;
  }
}

WebPage.dart

final Completer<WebViewController> _controller = Completer<WebViewController>();

class PageWeb extends StatefulWidget {
  final delegate;
  final ObjectKey key;

  PageWeb(this.delegate, this.key);

  @override
  PageWebState createState() {
    return PageWebState();
  }
}

void navigateTo(String url) { //Function that will be called on main.dart
   controller.future.then((controller) => controller.loadUrl(url));
}

class PageWebState extends State<PageWeb> {
  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
        navigationBar: CupertinoNavigationBar(
          middle: Text("Test"),
        ),
        child: SafeArea(
            child: Container(
                child: new WebView(
          key: widget.key,
          initialUrl: "https://www.google.com/",
          javascriptMode: JavascriptMode.unrestricted,
          onWebViewCreated: (WebViewController webViewController) {
            _controller.complete(webViewController);
          },
        ))));
  }
}

Please note that using constructor to passing variables is not in consideration because the URL will be changed and the clicking the button can be more than once.

klados
  • 706
  • 11
  • 33

0 Answers0