0

Below is the code of a provider class. Whenever the app starts i want to get the forms which were saved in the shared preferences. However it is taking sometime to load the from sharedpreferences. So When i access the forms for the first time it is initially empty, im getting an empty list. Is there anyway to delay the getter until it has the objects of form model.

class FormProvider with ChangeNotifier {
  FormProvider() {
    loadformPreferences();
  }
  List<FormModel> _forms = [];
  List<FormModel> get forms => _forms;

  Future<void> saveForm(FormModel form) async {
    _forms.add(form);
    await saveformPreferences();
    notifyListeners();
  }

  Future<void> saveformPreferences() async {
    List<String> myforms = _forms.map((f) => json.encode(f.toJson())).toList();
    SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.setStringList('forms', myforms);
    
  }
  

  Future<void> loadformPreferences() async {
    // WidgetsFlutterBinding.ensureInitialized();
    SharedPreferences prefs = await SharedPreferences.getInstance();
    var result = prefs.getStringList('forms');
    if (result != null) {
      _forms = result.map((f) => FormModel.fromJson(json.decode(f))).toList();
    }
  }
}
  • You cannot delay the getter. When you can get notified with notifyListeners() when the data is fetched, why do you want to delay the getter? You can just handle the UI accordingly when it is empty, and update with data when you get notified. – Jigar Patel Jul 08 '20 at 04:49
  • i checking the forms by using `ProviderProvider.of(context, listen: false).forms`, if it is [ ], making an API call to get the forms else, im using the one from shared preferences. When i call the `ProviderProvider.of(context, listen: false).forms` after i kill the app, initally its returning [ ], therefore its making the api call although it has the form in shared preferences. @JigarPatel –  Jul 08 '20 at 05:18
  • Haven't implemented [FutureProvider](https://stackoverflow.com/questions/56989807/how-to-get-data-from-the-futureprovider-in-flutter) but might be helpful. – dev-aentgs Jul 08 '20 at 05:30

1 Answers1

1

In Flutter, all actions related to building the UI must not be asynchronous (or expensive) in order to main a high frame-rate.

Thus, even if you could hypothetically find a way to "wait" for the results from SharedPreferences, this would not be desirable, since whatever waiting was done would block the UI from making progress.

Thus, there are a couple approaches to this common problem:

Handle initial state explicitly in the UI

The simplest solution is to explicitly handle the case where the Provider has not yet fetched its data, by representing some sort of initial state in your Provider. One easy way to do this is to initialize _forms to null instead of []. Then in your Widget.build method, you could do something specific (like show a loading spinner) when the result is null:

Widget build(BuildContext context) {
  final provider = Provider.of<FormProvider>(context);
  if (provider.forms == null) {
     return CircularProgressIndicator();
  }
  // Otherwise, Do something useful with provider.forms
}

Construct your provider with the resolved data

Let's say that you don't use the FormProvider until the user performs an action, like click a button, at which point you push a new view onto the navigator with the FormProvider.

If you wish to guarantee that the FormProvider will always be initialized with the SharedPreferences values, then you can delay the construction of the new view until SharedPreferences has finished:

class MyButton extends StatelessWidget {
  Widget build(BuildContext context) {
    return Button(onClick: () async {
      final forms = await _fetchFormsFromSharedPrefs();
      Navigator.push(context, MaterialPageView(builder: (context) => 
        Provider(create: (_) => FormProvider(forms))));
    });
  }
}
Jack Reilly
  • 463
  • 2
  • 8