Is is correct to use Future builder inside a streambuilder in Flutter

Viewed 3004

My Requirement is such that whenever a user verifies with a phonenumber it will check in Firebase whether the document id which is set as the phone number of the user if previously logged in ,exists or not if exists then Go to HomePage if it doesnot then Navigate to AccountVerification Page.

Also if the user is already logged in but didnot verify closes the app and then open the app then there are two situations First the user has verified the account details or has not verified during the verification process. In that case I will check the same whether the document id which is set to Phone number of the user exists or not if exist then go to HomePage else AccountVerification page. This is my code this is not working perfectly. Could anyone suggest me what should be the right approch

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  var globalNumber;
  var Number;
  Firestore firestore = Firestore.instance;


  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    getCurrentUser();



  }

  @override
  Widget build(BuildContext context) {
    print(globalNumber);
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: StreamBuilder<Object>(
        stream: FirebaseAuth.instance.onAuthStateChanged,
          builder: (BuildContext context, snapshot) {
            if (snapshot.hasData) {
              return FutureBuilder(
                  future: Future.wait([

                    firestore.collection('users').document(globalNumber).get()

                  ]),
                  builder: (BuildContext context,
                      AsyncSnapshot<List> snapshot) {
                    DocumentSnapshot doc = snapshot.data[0];
                    if(snapshot.hasData) {
                      if (doc.exists) {
                        return HomePage();
                      }
                      return AccountDetialsPage();
                    }
                    else{
                      return Container(child: Text('Loading'));
                    }
                  }
              );
            } else {
              return Phoneverification();
            }
          }),
    ) ;
  }

  getCurrentUser() async{

    final FirebaseAuth _auth = FirebaseAuth.instance;
    final FirebaseUser user = await _auth.currentUser();
    final uid = user.uid;
    globalNumber = user.phoneNumber;
    
    // Similarly we can get email as well
    //final uemail = user.email;
    print(uid);

  }

}
1 Answers

It does not work properly, as it is documented:

ā€œ The future must have been obtained earlier, e.g. during State.initState, State.didUpdateConfig, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.buildmethod call when constructing the FutureBuilder. If the future is created at the same time as the FutureBuilder, then every time the FutureBuilder's parent is rebuilt, the asynchronous task will be restarted.ā€

That means when your future method returns a value, a rebuild happens, triggering another rebuild. That creates a loop.

You should call your future function on initState() and then, with that variable, use FutureBuilder inside the build method. For example:

    void main() {
      runApp(MyApp());
    }

    class MyApp extends StatefulWidget {
      @override
      _MyAppState createState() => _MyAppState();
    }

    class _MyAppState extends State<MyApp> {

      var globalNumber;
      var Number;
      var user;
      Firestore firestore = Firestore.instance;


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


      Widget build(BuildContext context){
        return return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: StreamBuilder<Object>(
          stream: FirebaseAuth.instance.onAuthStateChanged,
          builder: (BuildContext context, snapshot) {
            if (snapshot.hasData) {
              return FutureBuilder(
                  future: user,
                  builder: (BuildContext context,
                      AsyncSnapshot<List> snapshot) {
                    DocumentSnapshot doc = snapshot.data[0];
                    if(snapshot.hasData) {
                      if (doc.exists) {
                        return HomePage();
                      }
                      return AccountDetialsPage();
                    }
                    else{
                      return Container(child: Text('Loading'));
                    }
                  }
              );
            } else {
              return Phoneverification();
            }
        }),
      );
    }

    getCurrentUser() async{

      final FirebaseAuth _auth = FirebaseAuth.instance;
      final FirebaseUser user = await _auth.currentUser();
      final uid = user.uid;
      globalNumber = user.phoneNumber;
    
      // Similarly we can get email as well
      //final uemail = user.email;
      print(uid);
    
      getUserNumber(uid);
    }

    //Make another function that you insert into the getCurrentUser(), like this:
    
    getUserNumber(int uid){
      user = Future.wait([
      firestore.collection('users').document(globalNumber).get()
    ]);
  }
Related