How do you initialise a provider with data taken from shared preferences?

Viewed 143

I am new to flutter and dart so the answer to this may be simple, or I may be going about this COMPLETELY the wrong way.

I basically want to use Provider for state management, and Shared Preferences for local storage.

I have a class called 'ResolutionsProvider' which contains a list of custom 'Resolution' objects which are used throughout my app. It contains methods for adding and removing Resolutions and it all works fine.

The problem is that I want the data to be persistent if the user restarts the app...

I'm trying to use SharedPreferences for this so I have methods within 'ResolutionsProvider' to save data to, and load data from Shared Preferences...

I know through print calls that the data is saving to shared preferences correctly...

My (potentially wrong) thinking is that I need to achieve the following...

  1. App is run
  2. ResolutionsProvider is instantiated as a ChangeNotifierProvider
  3. ResolutionsProvider.loadData() is run to populate List from SharedPreferences...

I've tried to call ResolutionsProvider.loadData() from within an initState but I get the following error...

E/flutter (30660): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: dependOnInheritedWidgetOfExactType<_InheritedProviderScope<ResolutionsProvider?>>() or dependOnInheritedElement() was called before _AppState.initState() completed.
E/flutter (30660): When an inherited widget changes, for example if the value of Theme.of() changes, its dependent widgets are rebuilt. If the dependent widget's reference to the inherited widget is in a constructor or an initState() method, then the rebuilt dependent widget will not reflect the changes in the inherited widget.
E/flutter (30660): Typically references to inherited widgets should occur in widget build() methods. Alternatively, initialization based on inherited widgets can be placed in the didChangeDependencies method, which is called after initState and whenever the dependencies change thereafter.

So i guess the question is two fold...

  1. If my approach is correct, how do I call the 'loadData' method to populate my provider on booting the app if I can't do so within initState

  2. If my approach is incorrect, what's the right approach to achieve my basic objective?

Here's my main.dart code

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await LocalStorage.init();
  runApp(
    ChangeNotifierProvider(
      create: (context) => ResolutionsProvider(),
      child: const App(),
    ),
  );
}

Here's the relevant code from my App.dart

class App extends StatefulWidget {
  const App({Key? key}) : super(key: key);

  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  @override
  void initState() {
    loadData();
    print("app initialised");
    super.initState();
  }

  void loadData() async {
    await Provider.of<ResolutionsProvider>(context).loadResolutions();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(blah blah blah);

Here's the relevant parts of my ResolutionProvider.dart

class ResolutionsProvider extends ChangeNotifier {
  List<Resolution> resolutions = [];

  // Interfacing with Shared Preferences
  loadResolutions() async {
    resolutions = await LocalStorage.getResolutions();
    print('${resolutions.length} resolutions loaded');
    // notifyListeners();
  }

  saveResolutions() async {
    LocalStorage.saveResolutions(resolutions);
  }
0 Answers
Related