Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter: Shared Preferences null on Startup

Problem: Shared preference bool value is null on startup even though I have given it a value if prefs.getBool('myBool') returns null (though my shared preferences value should already be set and saved). It does, however, work by the time I press a button (I assume because it has finished running the async code).

Question: How can I force shared preferences to load on startup (so my value is not null) without having to press the print button?

Example Code:

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

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

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

  @override
  createState() => new MyAppState();
}

class MyAppState extends State<MyApp> {
  final padding = const EdgeInsets.all(50.0);

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

    MySharedPreferences.load();
    MySharedPreferences.printMyBool();
  }

  @override
    Widget build(BuildContext context) {
      return new MaterialApp(
        home: new Scaffold(
          body: new Padding(
            padding: padding,
            child: new Column(
              children: <Widget>[
                new Padding(
                  padding: padding,
                  child: new RaisedButton(
                    child: new Text('Save True'),
                    onPressed: () => MySharedPreferences.save(myBool: true),
                  ),
                ),
                new Padding(
                  padding: padding,
                  child: new RaisedButton(
                    child: new Text('Save False'),
                    onPressed: () => MySharedPreferences.save(myBool: false),
                  ),
                ),
                new Padding(
                  padding: padding,
                  child: new RaisedButton(
                    child: new Text('Print myBool'),
                    onPressed: () => MySharedPreferences.printMyBool(),
                ),
              ),
            ],
          ),
        ), 
      ),
    );
  }
}

class MySharedPreferences {
  static bool _myBool;

  static void load() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    _myBool = prefs.getBool('myBool') ?? false;
  }

  static void save({myBool: bool}) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    _myBool = myBool;
    await prefs.setBool('myBool', _myBool);
  }

  static void printMyBool() {
    print('myBool: ${_myBool.toString()}');
  }
}

Results: On startup, myBool: null is printed. Once the button is pressed, myBool: false/true is then printed.

like image 715
PityTheFool Avatar asked Apr 21 '18 16:04

PityTheFool


2 Answers

Your problem is that you call load() and printMyBool() in quick succession. Because load() is async calling it hasn't executed any of its code, it has only scheduled it. So, printMyBool executes before the body of load.

There's no need to put static functions in a class - just declare them as top level functions. Also, you don't really want _myBool to be a global - it should be part of a Widget's state. That way when you update it, Flutter knows what parts of your tree to redraw.

I've restructured your code to remove the redundant statics.

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

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

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

  @override
  createState() => new MyAppState();
}

const EdgeInsets pad20 = const EdgeInsets.all(20.0);
const String spKey = 'myBool';

class MyAppState extends State<MyApp> {
  SharedPreferences sharedPreferences;

  bool _testValue;

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

    SharedPreferences.getInstance().then((SharedPreferences sp) {
      sharedPreferences = sp;
      _testValue = sharedPreferences.getBool(spKey);
      // will be null if never previously saved
      if (_testValue == null) {
        _testValue = false;
        persist(_testValue); // set an initial value
      }
      setState(() {});
    });
  }

  void persist(bool value) {
    setState(() {
      _testValue = value;
    });
    sharedPreferences?.setBool(spKey, value);
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
        body: new Center(
          child: new Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              new Padding(
                padding: pad20,
                child: new Text(
                    _testValue == null ? 'not ready' : _testValue.toString()),
              ),
              new Padding(
                padding: pad20,
                child: new RaisedButton(
                  child: new Text('Save True'),
                  onPressed: () => persist(true),
                ),
              ),
              new Padding(
                padding: pad20,
                child: new RaisedButton(
                  child: new Text('Save False'),
                  onPressed: () => persist(false),
                ),
              ),
              new Padding(
                padding: pad20,
                child: new RaisedButton(
                  child: new Text('Print myBool'),
                  onPressed: () => print(_testValue),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
like image 82
Richard Heap Avatar answered Oct 11 '22 08:10

Richard Heap


Add condition ?? when you get value from preference.

int intValue = prefs.getInt('intValue') ?? 0;
like image 23
jai khambhayta Avatar answered Oct 11 '22 07:10

jai khambhayta