FLUTTER: How to use navigator in streambuilder?

2,302

Solution 1

That's because Flutter is triggering a frame build when you are trying to navigate to another screen, thus, that's not possible.

You can schedule a post frame callback so you can navigate as soon as Flutter is done with tree rebuilding for that widget.

import 'package:flutter/foundation.dart';

  WidgetsBinding.instance.addPostFrameCallback(
     (_) => Navigator.push(context,
       MaterialPageRoute(
         builder: (context) => Results(),
       ),
     ),
   );

Solution 2

If navigation is the only thing happening on a button press, I wouldn't use a Bloc at all because Navigation is not business logic and should be done by the UI layer.

If you have business logic on a button press and need to navigate based on some dynamic information then I would do the navigation again in the presentation layer (widget) in response to a success state like below. You can also change navigation logic as per your requirement.

Widget loginButton(LoginBloc loginBloc) =>
      StreamBuilder<List<UserLoginResultElement>>(
          stream: loginBloc.loginStream,
          builder:
              (context, AsyncSnapshot<List<UserLoginResultElement>> snapshot) {
            print(snapshot.connectionState);
            Widget children;
            if (snapshot.hasError) {
              children = Padding(
                padding: const EdgeInsets.only(top: 16),
                child: Text('Error: ${snapshot.error}'),
              );
            } else {
              switch (snapshot.connectionState) {
                case ConnectionState.none:
                case ConnectionState.waiting:
                case ConnectionState.done:
                case ConnectionState.active:
                  children = BlockButtonWidget(
                    text: Text(
                      "LOGIN",
                      style: TextStyle(color: Theme.of(context).primaryColor),
                    ),
                    color: Theme.of(context).accentColor,
                    onPressed: () async {
                      try {
                        bloc.submit(_userNameController.value.text,
                            _passwordController.value.text, context);
                      } catch (ex) {
                        print(ex.toString());
                      }
                    },
                  );
                  break;
              }
            }
            if (snapshot.data != null && snapshot.hasData) {
              if (snapshot.data[0].code == "1") {

                SchedulerBinding.instance.addPostFrameCallback((_) {
                 Navigator.pushReplacementNamed(context, "/HomeScreen");
                });
              } else {
                print(Login Failed');
              }
            }
            return children;
          });
Share:
2,302
luc
Author by

luc

Updated on December 17, 2022

Comments

  • luc
    luc over 1 year

    I am trying to navigate inside a streambuilder but I have this error:"setState() or markNeedsBuild() called during build.". If I call navigate inside an onpressed button it works but not by just use it inside a condition. I am stuck. There is some code to show you.

    Widget build(BuildContext context) {
        return Scaffold(
          body: StreamBuilder(
            stream:
                Firestore.instance.collection('rooms').document(pinid).snapshots(),
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              if (snapshot.hasData) {
                if ((snapshot.data['Votes'][0] + snapshot.data['Votes'][1]) >=
                    snapshot.data['joueurs']) {
                  Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) => Results(),
                      ));
                }
              }
              return Center(
                child: Text('VOUS AVEZ VOTE'),
              );
            },
          ),
        );
      }