R Rifa Fauzi Komara
R Rifa Fauzi Komara

Reputation: 2128

Why get data from shared preferences have a delayed?

I have one screen to display data from shared preferences. I already success save and get data from shared preferences. Then I have a flow in one screen like this:

  1. If the user clicks that screen, it will check the data from shared preferences.
  2. If data is not null / not empty, it will display the data login like user profile, etc.
  3. If data is null/empty, it will show a button login.

I get the logic for that flow, but the problem is, before data showing in the screen (number 2), it shows button login first for a few milliseconds then show the data. Why did it happen? It's not got data from API / internet and I'm not using FutureBuilder, I just using Shared Preferences. How to kill this delayed? Below is my full code:

class MorePage extends StatefulWidget {
  @override
  _MorePageState createState() => _MorePageState();
}

class _MorePageState extends State<MorePage> {

  bool isLoading = false;
  SessionManager _sessionManager = SessionManager();
  int status;

  @override
  void initState() {
    super.initState();
    _sessionManager.getStatusLogin().then((value) { //i use this for get status code login success
      setState(() {
        status = value;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: color_grey_bg,
      body: SafeArea(
        child: getMorePage(),
      ),
    );
  }

  Widget getMorePage() {
    return ListView(
      physics: ClampingScrollPhysics(),
      children: <Widget>[
        Container(
          padding: EdgeInsets.only(
            left: MediaQuery.of(context).size.width / 20,
          ),
          height: MediaQuery.of(context).size.width / 4,
          width: MediaQuery.of(context).size.width,
          color: color_white,
          child: setProfile(),
        ),
      ],
    );
  }

  Widget setProfile() {
    if (status == 200) { // i use this logic to show widget with status login, but it's have delayed like show data from API. How to kill it? Because I using SharedPreferences, not hit the API
      return profileUser();
    } else {
      return notSignIn();
    }
  }

  Widget profileUser() {
    return Row(
      children: <Widget>[
        Column(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text(
              name,
              style: TextStyle(
                color: color_grey_text,
                fontSize: MediaQuery.of(context).size.width / 26,
                fontWeight: FontWeight.bold,
              ),
            ),
            Text(
              email,
              style: TextStyle(
                color: color_grey_text,
                fontSize: MediaQuery.of(context).size.width / 30,
                fontWeight: FontWeight.normal,
              ),
            ),
            Text(
              role,
              style: TextStyle(
                color: color_grey_text,
                fontSize: MediaQuery.of(context).size.width / 35,
                fontWeight: FontWeight.normal,
              ),
            ),
          ],
        ),
        Spacer(),
        IconButton(
          icon: Icon(
            Icons.arrow_forward_ios,
            size: MediaQuery.of(context).size.height / 40,
          ),
          onPressed: () {
            Navigator.push(context, MaterialPageRoute(builder: (context) => DetailUserPage()));
          },
        ),
      ],
    );
  }

  Widget notSignIn() {
    return Padding(
      padding:
          EdgeInsets.only(left: 50.0, right: 50.0, top: 30.0, bottom: 30.0),
      child: RaisedGradientButton(
        child: Text(
          'Login',
          style: TextStyle(
              color: color_white,
              fontSize: MediaQuery.of(context).size.width / 25),
        ),
        gradient: LinearGradient(
          colors: <Color>[color_blue, color_green],
        ),
        onPressed: () {
          Navigator.push(
              context, MaterialPageRoute(builder: (context) => LoginPage()));
        },
      ),
    );
  }
}

And this is class SessionManager for create function of shared_preferences:

class SessionManager {

  .....

  getStatusLogin() async {
    SharedPreferences preferences = await SharedPreferences.getInstance();
    int status = preferences.getInt("status");
    return status;
  }

  ....

}

Upvotes: 0

Views: 3289

Answers (3)

kaarimtareek
kaarimtareek

Reputation: 496

the getprofile function is actually is a future , you used the async await keywords . it's true that retrieving the data from the sharedpref doesn't take time ,but getting the instance of the sharedpref is the root of the cause . so you have to options to solve this solution .

1-getting the instance of the shared pref in the main function. you can get the instance of the shared pref and pass it as argument to the whole application.

Example :

void main ()async{
    final instance = await sharedPreference.getInstance();
    runApp(MyApp(instance));}

now in your MorePage widget

    class _MorePageState extends State<MorePage> {
  LoginStatus _loginStatus = LoginStatus.notSignIn;
  SessionManager _sessionManager = SessionManager();
  String name, email, role;
  //no need for the async keyword
  getProfile()  { //this func just for show the data

    name = widget.preferences.getString("fullname");
    email = widget.preferences.getString("email");
    role = widget.preferences.getString("role");
  }

  @override
  void initState() {
    super.initState();
    getProfile();
    _sessionManager.getLoginStatus().then((value) {  //this code for get the status of login
      setState(() {
        _loginStatus = value;
      });
    });
  }

now the getProfile function is not async which means there's no milliseconds that makes that weird behavior at the beginning .

2-Make another enum value 'busy' (simpler solution) . simply you can leave you code as it's but add a new enum value which is busy to give hint to the user that the app is checking if he has logined before or not ,you simply will give him that hint in the set profile function , you'll create another condition if ( _loginStatus == LoginStatus.busy ) return Text('checking user Info').

hope that helps !

Edit : You this package get_it to make a singleton instance of the session manager class and you can access it anywhere .

GetIt locator = GetIt();

void setUpLocator() {
  locator.registerLazySingleton(() => SessionManager());
}

void main() async {
  setUpLocator();
  await locator.get<SessionManager>().getStatusLogin();
  runApp(MyApp());
}

class MorePage extends StatefulWidget {
  @override
  _MorePageState createState() => _MorePageState();
}

class _MorePageState extends State<MorePage> {
  bool isLoading = false;
  final _sessionManager = locator.get<SessionManager>();
  int status;

  @override
  void initState() {
    super.initState();
      //make  property of statuesif you don't have property of the statues 
        //in the session manager class

    status = _sessionManager.statues;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: color_grey_bg,
      body: SafeArea(
        child: getMorePage(),
      ),
    );
  }

  Widget getMorePage() {
    return ListView(
      physics: ClampingScrollPhysics(),
      children: <Widget>[
        Container(
          padding: EdgeInsets.only(
            left: MediaQuery.of(context).size.width / 20,
          ),
          height: MediaQuery.of(context).size.width / 4,
          width: MediaQuery.of(context).size.width,
          color: color_white,
          child: setProfile(),
        ),
      ],
    );
  }

  Widget setProfile() {
    if (status == 200) {
      // i use this logic to show widget with status login, but it's have delayed like show data from API. How to kill it? Because I using SharedPreferences, not hit the API
      return profileUser();
    } else {
      return notSignIn();
    }
  }

  Widget profileUser() {
    return Row(
      children: <Widget>[
        Column(
          mainAxisSize: MainAxisSize.max,
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text(
              name,
              style: TextStyle(
                color: color_grey_text,
                fontSize: MediaQuery.of(context).size.width / 26,
                fontWeight: FontWeight.bold,
              ),
            ),
            Text(
              email,
              style: TextStyle(
                color: color_grey_text,
                fontSize: MediaQuery.of(context).size.width / 30,
                fontWeight: FontWeight.normal,
              ),
            ),
            Text(
              role,
              style: TextStyle(
                color: color_grey_text,
                fontSize: MediaQuery.of(context).size.width / 35,
                fontWeight: FontWeight.normal,
              ),
            ),
          ],
        ),
        Spacer(),
        IconButton(
          icon: Icon(
            Icons.arrow_forward_ios,
            size: MediaQuery.of(context).size.height / 40,
          ),
          onPressed: () {
            Navigator.push(context,
                MaterialPageRoute(builder: (context) => DetailUserPage()));
          },
        ),
      ],
    );
  }

  Widget notSignIn() {
    return Padding(
      padding:
          EdgeInsets.only(left: 50.0, right: 50.0, top: 30.0, bottom: 30.0),
      child: RaisedGradientButton(
        child: Text(
          'Login',
          style: TextStyle(
              color: color_white,
              fontSize: MediaQuery.of(context).size.width / 25),
        ),
        gradient: LinearGradient(
          colors: <Color>[color_blue, color_green],
        ),
        onPressed: () {
          Navigator.push(
              context, MaterialPageRoute(builder: (context) => LoginPage()));
        },
      ),
    );
  }
}

Upvotes: 2

GJJ2019
GJJ2019

Reputation: 5182

I think you should create a class and then use reference -:

Future<void> main() async{
 ShareP.preferences = await SharedPreferences.getInstance();  
}

class ShareP {
 static SharedPreferences preferences;
}

Now you can refer it ("ShareP.preferences") and get your SharedPreference values

Upvotes: 0

Dhaval Patel
Dhaval Patel

Reputation: 409

I think show Circle Progress once getting values form shared pref then show main container.

Please check below code:-

@override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: color_grey_bg,
      body: SafeArea(
        child: LoginStatus.notSignIn ? const CircularProgressIndicator(
                        valueColor: AlwaysStoppedAnimation<Color>(
                                    Color(colorPrimary))),
                    ) : getMorePage()
      ),
    );
  }

Upvotes: 0

Related Questions