Flutter: MVVM architecture with Shared Preferences & Provider

855

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}');
                }
            }
          },
        ),
      ),
    );
  }
}
Share:
855
Ang Xing
Author by

Ang Xing

Updated on December 29, 2022

Comments

  • Ang Xing
    Ang Xing over 1 year

    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(
          ),
        );
      }
    }
    
    
  • Ang Xing
    Ang Xing about 3 years
    I need to pass in my model class to initialise my ChangeNotifierProvider and my model class require me to pass in a shared preference. I am stuck here.
  • Patrick O'Hara
    Patrick O'Hara about 3 years
    I expanded the answer