Search code examples
fluttermvvmsharedpreferencesprovider

Flutter: MVVM architecture with Shared Preferences & Provider


I am trying to build a reusable function of Shared Preference & provider using MVVM architecture. However, I not sure how to initialize the ChangeNotifierProvider at my main. dart file. Below is the code I have.

class LanguagePreference {
  SharedPreferences prefs;

  LanguagePreference({required this.prefs});

  
  addUserLanguage(String lang) async {
    await SharedPreferences.getInstance();
    prefs.setString('userLanguage', "$lang");
  }

  
  getUserLanguage() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    //Return String
    return prefs.getString('userLanguage');
  }

}
  class LanguagePreferenceVM extends ChangeNotifier {
  LanguagePreference languagepreference;

  LanguagePreferenceVM({required this.languagePreference});

  String userLanguage = '';

  addUserLanguage(String newUserLanguage) async {
    await languagePreference.addUserLanguage(newUserLanguage);
  }

  getUserLanguage() async {
    userLanguage = await languagePreference.getUserLanguage();
    notifyListeners();
  }

  
}
 class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [],
      child: MaterialApp(
      ),
    );
  }
}


Solution

  • Here is a comprehensive example, below. Notes:

    • There is no need to pass SharedPreferences to your model as it has a static method to get the instance.
    • I use Provider.of<LanguagePreferenceVM> to get the provider to update the language.
    • I use Consumer<LanguagePreferenceVM> to get the provider to display the language when it notifies listeners.
    • I use FutureBuilder to handle the async result.

    Example code:

    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    import 'package:shared_preferences/shared_preferences.dart';
    
    class LanguagePreference {
      SharedPreferences _prefs;
    
      Future<void> getSharedPreferences() async {
        if (_prefs == null) {
          _prefs = await SharedPreferences.getInstance();
        }
      }
    
      LanguagePreference();
    
      Future<void> addUserLanguage(String lang) async {
        await getSharedPreferences();
        _prefs.setString('userLanguage', "$lang");
      }
    
      Future<String> getUserLanguage() async {
        //Return String
        try {
          await getSharedPreferences();
          var language = _prefs.getString('userLanguage');
          return language;
        } catch (e) {
          return '';
        }
      }
    }
    
    class LanguagePreferenceVM extends ChangeNotifier {
      LanguagePreference languagePreference;
    
      LanguagePreferenceVM({@required this.languagePreference});
    
      String userLanguage = '';
    
      Future<void> setUserLanguage(String newUserLanguage) async {
        await languagePreference.addUserLanguage(newUserLanguage);
        userLanguage = newUserLanguage;
      }
    
      Future<String> getUserLanguage() async {
        userLanguage = await languagePreference.getUserLanguage();
        return userLanguage;
      }
    }
    
    class PrefsTest extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MultiProvider(
          providers: [
            ChangeNotifierProvider<LanguagePreferenceVM>(
              create: (ctx) =>
                  LanguagePreferenceVM(languagePreference: LanguagePreference()),
            ),
          ],
          child: PrefsTest2(),
        );
      }
    }
    
    class PrefsTest2 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        var languagePreferenceVM =
            Provider.of<LanguagePreferenceVM>(context, listen: false);
        languagePreferenceVM.setUserLanguage('English');
        return Scaffold(
          appBar: AppBar(title: Text('Prefs')),
          body: Consumer<LanguagePreferenceVM>(
            builder: (ctx, languagePreferenceVM, child) => FutureBuilder(
              future: languagePreferenceVM.getUserLanguage(),
              builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
                switch (snapshot.connectionState) {
                  case ConnectionState.waiting:
                    return const CircularProgressIndicator();
                  default:
                    if (snapshot.hasError) {
                      return Text('Error: ${snapshot.error}');
                    } else {
                      return Text('User language: ${snapshot.data}');
                    }
                }
              },
            ),
          ),
        );
      }
    }