Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter set startup page based on Shared Preference

Tags:

flutter

dart

I've been trying without success to load different pages according to my Shared Preference settings.

Based on several posts found in stackoverflow, i end up with the following solution:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:testing/screens/login.dart';
import 'package:testing/screens/home.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Widget page = Login();

  Future getSharedPrefs() async {

    String user = Preferences.local.getString('user');

    if (user != null) {
      print(user);
      this.page = Home();
    }
  }

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

    this.getSharedPrefs();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: this.page);
  }
}

class Preferences {
  static SharedPreferences local;

  /// Initializes the Shared Preferences and sets the info towards a global variable
  static Future init() async {
    local = await SharedPreferences.getInstance();
  }
}

The variable user is not null because the print(user) returns a value as expected, but the login screen is always being opened.

like image 662
Linesofcode Avatar asked Mar 18 '19 01:03

Linesofcode


Video Answer


2 Answers

Your problem is that your build method returns before your getSharedPrefs future is complete. The getSharedPrefs returns instantly as soon as it's called because it's async and you're treating it as a "Fire and Forget" by not awaiting. Seeing that you can't await in your initState function that makes sense.

This is where you want to use the FutureBuilder widget. Create a Future that returns a boolean (or enum if you want more states) and use a future builder as your home child to return the correct widget.

Create your future

Future<bool> showLoginPage() async {
  var sharedPreferences = await SharedPreferences.getInstance();

  // sharedPreferences.setString('user', 'hasuser');

  String user = sharedPreferences.getString('user');

  return user == null;
}

When user is null this will return true. Use this future in a Future builder to listen to the value changes and respond accordingly.

 @override
  Widget build(BuildContext context) {
    return MaterialApp(home: FutureBuilder<bool>(
     future: showLoginPage(),
     builder: (buildContext, snapshot) {
       if(snapshot.hasData) {
         if(snapshot.data){
           // Return your login here
        return Container(color: Colors.blue);
      }

      // Return your home here
      return Container(color: Colors.red);
    } else {

      // Return loading screen while reading preferences
      return Center(child: CircularProgressIndicator());
    }
  },
));
}

I ran this code and it works fine. You should see a blue screen when login is required and a red screen when there's a user present. Uncomment the line in showLoginPage to test.

like image 70
Filled Stacks Avatar answered Sep 28 '22 05:09

Filled Stacks


There is a much pretty way of doing this. Assuming that you have some routes and a boolean SharedPreference key called initialized. You need to use the WidgetsFlutterBinding.ensureInitialized() function before calling runApp() method.

void main() async {
  var mapp;
  var routes = <String, WidgetBuilder>{
    '/initialize': (BuildContext context) => Initialize(),
    '/register': (BuildContext context) => Register(),
    '/home': (BuildContext context) => Home(),
  };
  print("Initializing.");
  WidgetsFlutterBinding.ensureInitialized();
  await SharedPreferencesClass.restore("initialized").then((value) {
    if (value) {
      mapp = MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'AppName',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        routes: routes,
        home: Home(),
      );
    } else {
      mapp = MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'AppName',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        routes: routes,
        home: Initialize(),
      );
    }
  });
  print("Done.");
  runApp(mapp);
}

The SharedPreference Class Code :

class SharedPreferencesClass {
  static Future restore(String key) async {
    final SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
    return (sharedPrefs.get(key) ?? false);
  }

  static save(String key, dynamic value) async {
    final SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
    if (value is bool) {
      sharedPrefs.setBool(key, value);
    } else if (value is String) {
      sharedPrefs.setString(key, value);
    } else if (value is int) {
      sharedPrefs.setInt(key, value);
    } else if (value is double) {
      sharedPrefs.setDouble(key, value);
    } else if (value is List<String>) {
      sharedPrefs.setStringList(key, value);
    }
  }
}
like image 42
Ebenezer Nikabou Avatar answered Sep 28 '22 03:09

Ebenezer Nikabou