2
votes

I'm new to Flutter and Dart. So I'm trying to build a simple authentication system in Flutter. When the User opens the app, I want to either show them the LoginScreen (where they had not previously logged in) or the HomeScreen when they are logged in.

loadWidget, which returns a Future would determine whether there is persistent data and hence fetches the user's info and displays the Home Screen. The child property would not let me assign a Future to it, I'm not sure but I suppose I would have to use FutureBuilder widget for this?

First Try

  Future<Widget> loadWidget() async{
    SharedPreferences prefs = await SharedPreferences.getInstance();

    // already logged in
    String phone = prefs.getString('phoneNumber');
    if (phone != null) {     
      helper.user = await checkUser(phone); // fetch user info
      return HomeScreen();
    }
    // Not logged in
    else {
      return Login();
    }
  }

For the build function, this does not work

Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: loadWidget(), // This does not allow me to assign a future
    )
);
  }

Second Try: Using the FutureBuilder

I read about Futurebuilder here how to assign future<> to widget in flutter? and it looks like the way to go, so I modified my build method as

Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: FutureBuilder(
          future: loadWidget(),
          builder: (BuildContext context, AsyncSnapshot<Widget> widget){
            return widget;
          },
        ),
      ),
    );
  }

But this gave me the following error:

The return type 'AsyncSnapshot' isn't a 'Widget', as defined by anonymous closure.

How can I accomplish this? Thanks!

2

2 Answers

6
votes

You are doing it right but the problem is with the returning object from the FutureBuilder, see the comments in this code:

// *NOTE: build return Widget: Widget build...
Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: FutureBuilder(
          future: loadWidget(),
          builder: (BuildContext context, AsyncSnapshot<Widget> widget){
            // But 'widget' here is NOT a widget, it is an AsyncSnapshot object,
            // return widget; // **is wrong**
            // instead:
            return widget.data;

            // and better to make it like this:
            //if (!widget.hasData) {
            //  return Center(
            //    child: CircularProgressIndicator(),
            //  );
            //}
            //return widget.data;
          },
        ),
      ),
    );
  }

that was to fix the error. An extra advice for cleaner code, keep your Authentication layer(loadWidget) clean by making it return only state(Auth or Not), then catch the state using widget.data inside the FutureBuilder, and then route based on the Auth-state from inside the FutureBuilder.

1
votes

I would move the future into the FutureBuilder and then put the logic inside it

  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: FutureBuilder(
            future: loadWidget(),
            builder: (BuildContext context, AsyncSnapshot<Widget> sharedPreference) {
              String phone = sharedPreference.getString('phoneNumber');
              if (phone != null) {
                return HomeScreen();
              }
              // Not logged in
              else {
                return Login();
              }
            }),
      ),
    );
  }