Search code examples
fluttersharedpreferences

How to make build function wait async function in Flutter?


Widget build function is not waiting for async function to update variable value which is essential for scaffold webview link. I have tried to move the async function to different parts of the code but nothing seems to work. When print variable to console, first it prints null and then actual value. what is wrong here?

class ProfilePage extends StatefulWidget {
  const ProfilePage({Key? key}) : super(key: key);

  @override
  State<ProfilePage> createState() => _ProfilePageState();
}

class _ProfilePageState extends State<ProfilePage> {
  var phone;
  var password;
  @override
  void initState() {
    super.initState();
    retrieveStringValue();
  }

  retrieveStringValue() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    setState(() {
      password = prefs.getString("password");
      phone = prefs.getString("phone");
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: false,
      body: SafeArea(
        child: WebView(
          javascriptMode: JavascriptMode.unrestricted,
          initialUrl:
              'https://turon.com/app_login.php?phone=$phone&password=$password',
        ),
      ),
    );
  }
}

Solution

  • You can use a FutureBuilder for this. You can also simplify it a bit and use a StatelessWidget.

    Here I'm changing your retrieveStringValue function to return a Map with the stored values that the FutureBuilder will use to display the data.

    class ProfilePage extends StatelessWidget {
      const ProfilePage({Key? key}) : super(key: key);
    
      Future<Map<String, String>?> retrieveStringValue() async {
        final SharedPreferences prefs = await SharedPreferences.getInstance();
        final password = prefs.getString("password");
        final phone = prefs.getString("phone");
    
        final bool hasData = password != null && phone != null;
    
        if (hasData) {
          return {
            'password': password,
            'phone': phone
          }; // this return value is accessed via snapshot.data in FutureBuilder
        } else {
          return null; // null tells FutureBuilder that no data is stored
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          extendBodyBehindAppBar: false,
          body: SafeArea(
            child: FutureBuilder(
              future: retrieveStringValue(), // returns a Map with the stored values
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  final map = snapshot.data! as Map<String, String>;
                  final password = map['password'];
                  final phone = map['phone'];
                  return WebView(
                    javascriptMode: JavascriptMode.unrestricted,
                    initialUrl:
                        'https://turon.com/app_login.php?phone=$phone&password=$password',
                  );
                } else {
                  return Center(child: CircularProgressIndicator()); // or an indicator that no data is stored
                }
              },
            ),
          ),
        );
      }
    }