Flutter: await a function before Build

3,412

Solution 1

Your title does not really match your question. In fact, you basically provided 3 questions:

How to await a function before/in build?: You don't, build can be called multiple times, it is not really in your control, when or how it is called, and you should not make assumptions about it. Instead, you can use FutureBuilder to build 2 different widgets, depending on whether the Future resolved or not.

How to automatically navigate on page entry?: Trigger the method to check and navigate in initState, but you can't prevent the first page from building / showing (since you navigated on that page first).

How to do conditional navigation?: You first await the future, and then navigate.

For your use case: You want to do conditional navigation, so before you navigate to your login page (presumably the first page in your app), provide something like a splash page, where you check if the user is logged in. And after the future resolved, you do the navigation to either profile or login screen. You can wrap this in a function to reuse this conditional navigation on other pages, where you want to land either in the login or the profile page.

Solution 2

You can call the LoginCheck function in the initState lifecycle method. Thus the function will be executed before the build method. see below

  • LoginCheck now returns the value of logined and GetUserNum as a list
Future<List<dynamic>> LoginCheck() async{
    SharedPreferences prefs = await SharedPreferences.getInstance();

    bool logined = prefs.getBool(_StorageLogined) ?? false;
    int GetUserNum = prefs.getInt(_StorageUserNum) ?? 0;

//     if(logined == true) {
//       Navigator.of(context).push(MaterialPageRoute(
//          builder: (context) => ShowProfile(UserNum: GetUserNum,)));
  
  return [logined,GetUserNum];
 
}
  • You then call LoginCheck in your initState method as below:

      bool logined;
      int GetUserNum;
      initState(){
        super.initState();
    
        LoginedCheck().then((results){  
            setState((){
              logined = results[0];
              GetUserNum = results[1];
            });
    
          if(logined==true)Navigator.of(context).push(
          MaterialPageRoute( builder: (context) => ShowProfile(UserNum: GetUserNum))
          );
        });
    
    
      }  
    
    
    
      Widget build(BuildContext context) {
    
      return Scaffold(
        resizeToAvoidBottomPadding: false,
        body: logined==false?Stack( //if logined is not true we display the login page else we show a loader while we naviagate to the user's page
          children: <Widget>[
            Container(
              height: double.infinity,
              width: double.infinity,
              decoration: BoxDecoration(
                  gradient: LinearGradient(
                    begin: Alignment.topCenter,
                    end: Alignment.bottomCenter,
                    colors: [
                      Color(0xFF73AEF5),
                      Color(0xFF61A4F1),
                      Color(0xFF478DE0),
                      Color(0xFF398AE5),
                    ],
                    stops: [0.1,0.4,0.7,0.9],
                  )),
              child: Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    SizedBox(
                      height: 10,
                    ),
                    Icon(
                      Icons.account_circle,
                      size: 150.0,
                    ),
                    SizedBox(height: 30),
                    Container(
                      width: 300,
                      child: Theme(
                          data: ThemeData(
                            primaryColor: Colors.white,
                            primaryColorDark: Colors.yellowAccent,
                          ),
                          child: TextField(
                            controller: _LtextFieldAccount,
                            decoration: InputDecoration(
                                border: OutlineInputBorder(),
                                hintText: '請輸入帳號',
                                prefixIcon: const Icon(Icons.person)),
                          )),
                    ),
                    SizedBox(height: 20),
                    Container(
                      width: 300,
                      child: Theme(
                          data: ThemeData(
                            primaryColor: Colors.white,
                            primaryColorDark: Colors.yellowAccent,
                          ),
                          child: TextField(
                            controller: _LtextFieldID,
                            decoration: InputDecoration(
                                border: OutlineInputBorder(),
                                hintText: '請輸入密碼',
                                prefixIcon: const Icon(Icons.lock)),
                          )),
                    ),
                    SizedBox(height: 30),
                    RaisedButton(
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(25.0)),
                      elevation: 4.0,
                      color: Colors.white,
                      child: Container(
                        padding: EdgeInsets.only(
                            left: 10,right: 10,top: 10,bottom: 10),
                        child: Text(
                          '登入',
                          style:
                          TextStyle(fontSize: 25,color: Colors.blueAccent),
                        ),
                      ),
                      onPressed: Login,
                    ),
                    SizedBox(height: 20),
                    RaisedButton(
                        shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(25.0)),
                        elevation: 4.0,
                        color: Colors.white,
                        child: Container(
                          padding: EdgeInsets.only(
                              left: 10,right: 10,top: 10,bottom: 10),
                          child: Text(
                            '新用戶註冊',
                            style:
                            TextStyle(fontSize: 25,color: Colors.blueAccent),
                          ),
                        ),
                        onPressed: (){
                          Navigator.of(context).push(MaterialPageRoute(
                              builder: (context) => RegisterPage()));
                        })
                  ],
                ),
              ),
            ),
          ],
        ):Center(child:CircularProgressIndicator()),
        bottomNavigationBar: BottomNavigationBar(
          onTap: onTabTapped,
          currentIndex: _currentIndex, //thiswillbesetwhenanewtabistapped
          items: [
            new BottomNavigationBarItem(
              icon: new Icon(Icons.school),
              title: new Text('大學'),
            ),
            new BottomNavigationBarItem(
              icon: new Icon(Icons.directions_subway),
              title: new Text('交通'),
            ),
            new BottomNavigationBarItem(
              icon: new Icon(Icons.person),
              title: new Text('個人'),
            )
          ],
        ),
      );
    }
    

Solution 3

You can copy paste run full code below
You can get logined in main() and do Navigate in initialRoute
code snippet

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  SharedPreferences prefs = await SharedPreferences.getInstance();

  logined = prefs.getBool(_StorageLogined) ?? false;
  GetUserNum = prefs.getInt(_StorageUserNum) ?? 0;

  runApp(MyApp());
}
...
initialRoute: logined == false ? "/login" : "/profile",
      routes: {
        '/profile': (context) => ShowProfile(
              title: "demo",
            ),
        "/login": (context) => Login(),
      },

full code

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

bool logined;
int GetUserNum;
String _StorageLogined = "Logined";
String _StorageUserNum = "UserNum";

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  SharedPreferences prefs = await SharedPreferences.getInstance();

  logined = prefs.getBool(_StorageLogined) ?? false;
  GetUserNum = prefs.getInt(_StorageUserNum) ?? 0;

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: logined == false ? "/login" : "/profile",
      routes: {
        '/profile': (context) => ShowProfile(
              title: "demo",
            ),
        "/login": (context) => Login(),
      },
    );
  }
}

class Login extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Login "),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Login Page',
            ),
          ],
        ),
      ),
    );
  }
}

class ShowProfile extends StatefulWidget {
  ShowProfile({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _ShowProfileState createState() => _ShowProfileState();
}

class _ShowProfileState extends State<ShowProfile> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
Share:
3,412
HoshiChen
Author by

HoshiChen

Updated on December 22, 2022

Comments

  • HoshiChen
    HoshiChen over 1 year

    I create a profile page in my APP for users which can be logged in and logged out with their own account number and passcode. I use SharedPreference to store datas in local devices, including the bool 'logined', which will be set to 'true' after users logged in (default is 'false'). Here comes the problem. I want to show users their own profile page every time they navigate to this page without going to login page again after they have logged in. So, before I build this page, I have to check whether the 'logined' variable is 'true' or 'false'. If true, navigate to profile page. If false, show the login page. But, in fact, the login page is always showed before the LoginedCheck function(which is used to check the 'logined' variable and navigate) done its work. The login page will be showed and then quickly transfer to profile page. I've tried to use 'delay' to wait for LoginedCheck(), but the screen will turn black before the profile page created. Any ideas to solve the problem? Thanks for instance.

    The Build method:

    Widget build(BuildContext context) {
        LoginedCheck();
        return Scaffold(
          resizeToAvoidBottomPadding: false,
          body: Stack(
            children: <Widget>[
              Container(
                height: double.infinity,
                width: double.infinity,
                decoration: BoxDecoration(
                    gradient: LinearGradient(
                      begin: Alignment.topCenter,
                      end: Alignment.bottomCenter,
                      colors: [
                        Color(0xFF73AEF5),
                        Color(0xFF61A4F1),
                        Color(0xFF478DE0),
                        Color(0xFF398AE5),
                      ],
                      stops: [0.1,0.4,0.7,0.9],
                    )),
                child: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      SizedBox(
                        height: 10,
                      ),
                      Icon(
                        Icons.account_circle,
                        size: 150.0,
                      ),
                      SizedBox(height: 30),
                      Container(
                        width: 300,
                        child: Theme(
                            data: ThemeData(
                              primaryColor: Colors.white,
                              primaryColorDark: Colors.yellowAccent,
                            ),
                            child: TextField(
                              controller: _LtextFieldAccount,
                              decoration: InputDecoration(
                                  border: OutlineInputBorder(),
                                  hintText: '請輸入帳號',
                                  prefixIcon: const Icon(Icons.person)),
                            )),
                      ),
                      SizedBox(height: 20),
                      Container(
                        width: 300,
                        child: Theme(
                            data: ThemeData(
                              primaryColor: Colors.white,
                              primaryColorDark: Colors.yellowAccent,
                            ),
                            child: TextField(
                              controller: _LtextFieldID,
                              decoration: InputDecoration(
                                  border: OutlineInputBorder(),
                                  hintText: '請輸入密碼',
                                  prefixIcon: const Icon(Icons.lock)),
                            )),
                      ),
                      SizedBox(height: 30),
                      RaisedButton(
                        shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(25.0)),
                        elevation: 4.0,
                        color: Colors.white,
                        child: Container(
                          padding: EdgeInsets.only(
                              left: 10,right: 10,top: 10,bottom: 10),
                          child: Text(
                            '登入',
                            style:
                            TextStyle(fontSize: 25,color: Colors.blueAccent),
                          ),
                        ),
                        onPressed: Login,
                      ),
                      SizedBox(height: 20),
                      RaisedButton(
                          shape: RoundedRectangleBorder(
                              borderRadius: BorderRadius.circular(25.0)),
                          elevation: 4.0,
                          color: Colors.white,
                          child: Container(
                            padding: EdgeInsets.only(
                                left: 10,right: 10,top: 10,bottom: 10),
                            child: Text(
                              '新用戶註冊',
                              style:
                              TextStyle(fontSize: 25,color: Colors.blueAccent),
                            ),
                          ),
                          onPressed: (){
                            Navigator.of(context).push(MaterialPageRoute(
                                builder: (context) => RegisterPage()));
                          })
                    ],
                  ),
                ),
              ),
            ],
          ),
          bottomNavigationBar: BottomNavigationBar(
            onTap: onTabTapped,
            currentIndex: _currentIndex, //thiswillbesetwhenanewtabistapped
            items: [
              new BottomNavigationBarItem(
                icon: new Icon(Icons.school),
                title: new Text('大學'),
              ),
              new BottomNavigationBarItem(
                icon: new Icon(Icons.directions_subway),
                title: new Text('交通'),
              ),
              new BottomNavigationBarItem(
                icon: new Icon(Icons.person),
                title: new Text('個人'),
              )
            ],
          ),
        );
      }
    

    The LoginedCheck() function:

    Future LoginedCheck() async{
        SharedPreferences prefs = await SharedPreferences.getInstance();
    
        bool logined = prefs.getBool(_StorageLogined) ?? false;
        int GetUserNum = prefs.getInt(_StorageUserNum) ?? 0;
    
        if(logined == true) {
          Navigator.of(context).push(MaterialPageRoute(
              builder: (context) => ShowProfile(UserNum: GetUserNum,)));
        }
      }