Search code examples
androidflutterdartasynchronoussharedpreferences

Flutter - Keeping variable's value after each app restart


On my one page I would like my user to start with default text on one variable ( codeDialog) and then I want them to change that text, after that time that text they wrote would be my new default text. Sadly I couldn't get it to work.

Right now when I restart the app and open that screen it resets to null. I think its because of initializing variable ( which is null) but I couldn't find a way to fix that. Any idea? Thanks.

All of this is inside statefulwidget's state class:

void initState() {
    super.initState();
    print(codeDialog);
    isFirstTime().then((isFirstTime) {
      isFirstTime ? defaultText() : readFromShared();
    });
    setText();
    print(codeDialog);
    _textEditingController = new TextEditingController(text: codeDialog);
  }
Future<bool> isFirstTime() async {
    final prefs = await SharedPreferences.getInstance();
    var isFirstTime = prefs.getBool('first_time');
    if (isFirstTime != null && !isFirstTime) {
      prefs.setBool('first_time', false);
      return false;
    } else {
      prefs.setBool('first_time', false);
      return true;
    }
  }
  static String codeDialog;
  void defaultText(){
    print("first time");
    codeDialog="This is what i want my default variable to be when my app first opened.";
  }

  void readFromShared() async{
    print("not first");
    final prefs = await SharedPreferences.getInstance();
    codeDialog = prefs.getString('sharedmessage');
  }
  void setText() async {
    final prefs = await SharedPreferences.getInstance();
    prefs.setString('sharedmessage', codeDialog);
  }

and then i have AlertDialog with this it works and saves the data.

AlertDialog(
            scrollable: true,
            contentPadding: EdgeInsets.fromLTRB(20, 20, 20, 0),
            content: Column(
              children: [
                Container(
                  height: maxLines * 10.0,
                  child: TextField(
                    controller:  new TextEditingController(text: codeDialog),//_textEditingController,
                    maxLines: maxLines,
                    onChanged: (value){
                      setState(() {
                        valueText=value;
                      });
                    },
                    decoration: InputDecoration(
                      hintText: "Text here",
                    ),
                  ),
                ),
              ],
            ),
            actions: [
              TextButton(
                  onPressed: Navigator.of(context).pop, child: Text("Cancel")),
              TextButton(
                  onPressed: () {
                    setState(() {
                      Navigator.pop(context);
                      codeDialog = valueText;
                      print(codeDialog);
                      setText();
                    });
                  },
                  child: Text("Save"))
            ],
          )

EDIT: Also worth noting to people who may see this question. If you don't change TextField's value and submit it (After app restart for some reason) the value will change to null (i think due to there's no change in string). You can fix it by adding if(value==null) and saving it if its not null.


Solution

  • You have to use the package shared preference. I will edit with an example from my source codes

    I would use FutureBuilder instead of initstate as it is cleaner:

    import 'package:flutter/material.dart';
    import 'package:shared_preferences/shared_preferences.dart';
    
    class SavedPreference extends StatefulWidget {
      SavedPreference({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _SavedPreference createState() => _SavedPreference();
    }
    
    class _SavedPreference extends State<SavedPreference> {
      TextEditingController _textEditingController;
      String valueText;
    
      void initState() {
        super.initState();
      }
    
      Future<String> _getSharedValue() async {
        const defaultText =
            "This is what i want my default variable to be when my app first opened.";
        String ret;
        var prefs = await SharedPreferences.getInstance();
        bool isFirstTime = prefs.getBool('firstTime');
        print(isFirstTime);
    
        ret =
            (isFirstTime == null || isFirstTime == true) ? defaultText : prefs.getString('sharedmessage');
        print(ret);
    
        _textEditingController = new TextEditingController(text: ret);
        return ret;
      }
    
      String codeDialog;
    
      void setText() async {
        final prefs = await SharedPreferences.getInstance();
        prefs.setString('sharedmessage', valueText);
        prefs.setBool('firstTime', false);
      }
    
      @override
      Widget build(BuildContext context) {
        int maxLines = 10;
        return Container(
            child: FutureBuilder(
                future: _getSharedValue(),
                builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
                  print(snapshot.data);
                  if (snapshot.hasData) {
                    return TextButton(
                        child: Text(snapshot.data),
                        onPressed: () async => await showDialog(
                            context: context,
                            builder: (context) => new AlertDialog(
                                  scrollable: true,
                                  contentPadding:
                                      EdgeInsets.fromLTRB(20, 20, 20, 0),
                                  content: Column(
                                    children: [
                                      Container(
                                        height: maxLines * 10.0,
                                        child: TextField(
                                          controller: new TextEditingController(
                                              text: codeDialog),
                                          maxLines: maxLines,
                                          onChanged: (value) {
                                            valueText = value;
                                          },
                                          decoration: InputDecoration(
                                            hintText: "Text here",
                                          ),
                                        ),
                                      ),
                                    ],
                                  ),
                                  actions: [
                                    TextButton(
                                        onPressed: Navigator.of(context).pop,
                                        child: Text("Cancel")),
                                    TextButton(
                                        onPressed: () {
                                          setState(() {
                                            Navigator.pop(context);
                                            codeDialog = valueText;
                                            print(codeDialog);
                                            setText();
                                          });
                                        },
                                        child: Text("Save"))
                                  ],
                                )));
                  } else
                    return CircularProgressIndicator();
                }));
      }
    }
    
    

    Your problem was that initState was not awaiting properly the result of await SharedPreferences.getInstance(); (I tried to fix it inside initState and lost a tremendous amount of time)