'Flutter : SharedPreferences not fetching value at app start

I am trying to store a value and based on the value I want to navigate to LandinPage or HomePage. However when my app loads I am not able to get the SharedPreferences value. Currently, the value is set on click of a button in Landing page, and when I close/minimize the app. I don't even get to see the print messages from main.dart and can't fetch values. What am I doing wrong?

Here is my code:

import 'package:credit/src/pages/landing.dart';
import 'package:flutter/material.dart';
import 'package:credit/src/pages/credit/home.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  MyApp({Key key}) : super(key: key);

  _LoadingPageState createState() => _LoadingPageState();
}

class _LoadingPageState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    getUserStatus().then((userStatus) {
      if (userStatus == null) {
        Navigator.of(context)
            .push(MaterialPageRoute<Null>(builder: (BuildContext context) {
          return LandingPage();
        }));
      } else {
        Navigator.of(context)
            .push(MaterialPageRoute<Null>(builder: (BuildContext context) {
          return HomePage();
        }));
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        child: Center(
      child: CircularProgressIndicator(),
    ));
  }
}

Future<String> getUserStatus() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  String userStatus = prefs.getString('userstatus');
  print("==On Load Check ==");
  print(userStatus);
  return userStatus;
}


Solution 1:[1]

You've declared a method main of MyApp but it never gets called. The main that starts the app is the one with runApp in it. You could move the prefs.getString() into the real main (having made it async) and then pass the value into the MyApp widget as a parameter.

Solution 2:[2]

I feel like Willie's answer may be just as good, but here's another approach.

Overall, my approach would be to load the main home page automatically, and then in the initstate of the home page, check to see if this is the user's first visit to the app. If so, pop the landing page on top immediately. I've used this approach successfully without the user having a poor experience.

Below is the default app but with your SharedPreferences code moved to the appropriate spot.

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

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

  var userStatus;

  //If user status is null, then show landing page.
  Future<void> checkUserStatus() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    userStatus = prefs.getString('userstatus');
    print("==On Load Check ==");
    print(userStatus);

    if (userStatus == null) {
      Navigator.push(context, MaterialPageRoute(builder: (context) => LandingPage()));
    }

  }


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

    //Call check for landing page in init state of your home page widget
    checkUserStatus();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        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.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}


class LandingPage extends StatefulWidget {
  @override
  _LandingPageState createState() => _LandingPageState();
}

class _LandingPageState extends State<LandingPage> {
  @override
  Widget build(BuildContext context) {
    //Build landing page here.
    return Container();
  }
}

Solution 3:[3]

I know this question is old and already been answered but for my situation, Richard Heap's answer was more suitable so I would like to add a code snippet for others.

I only cite part of it, so please modify it if you are going to use it for your app. After the Landing/Welcome page is viewed by user, update the preference by setBool and it won't show up after that.

void main() async {
  // do whatever
  SharedPreferences prefs = await SharedPreferences.getInstance();
  bool hideWelcome = prefs.getBool('hideWelcome') ?? false;
  // start your app
  runApp(MyApp(hideWelcome));
}

class MyApp extends StatelessWidget {
  final hideWelcome;

  MyApp(this.hideWelcome);

  @override
  Widget build(BuildContext context) {
      return MaterialApp(
          // other setting like theme, title
          initialRoute: hideWelcome ? '/' : '/welcome',
          routes: {
              '/': (context) => MyHomePage(),
              '/welcome': (context) => WelcomePage(),
              // other pages
          }
      );
}

Solution 4:[4]

you must add

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

  var name;
  void getUserStatus() async {
     SharedPreferences prefs= await SharedPreferences.getInstance();

     setState(() {
       userStatus = prefs.getString("userStatus");
   });
  }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Richard Heap
Solution 2 Eric Duffett
Solution 3
Solution 4 Arif Maksum