Search code examples
flutterdartproviderriverpod

Flutter : How to use Riverpod with SharedPreference and List<String> Variable in multipage?


I've created List<String> favId = []; variable to store the item's ID with SharedPreferences, so the favorited items IDs don't get lost after I restart the application. And here is my SharedPreferences method and favorite IconButton in detailDoaPage.dart :

...
static List<String> favId = [];

  getData() async {
    SharedPreferences pref = await SharedPreferences.getInstance();
    setState(() {
      favId = pref.getStringList("id") ?? [];
    });
  }

  void initState() {
    super.initState();
    getIds();
  }

  getIds() async {
    favId = getData();
  }

  void saveData() async {
    SharedPreferences pref = await SharedPreferences.getInstance();
    pref.setStringList("id", favId);
  }
...
IconButton(
                    icon: Icon(
                      favId.contains(doa.id.toString())
                          ? Icons.favorite
                          : Icons.favorite_border,
                      color: favId.contains(doa.id.toString())
                          ? Colors.red
                          : Colors.grey,
                    ),
                    onPressed: () => setState(() {
                      doa.fav = !doa.fav;
                      if (favId.contains(doa.id.toString())) {
                        favId.removeWhere(
                            (element) => element == doa.id.toString());
                      } else {
                        favId.add(doa.id.toString());
                      }
                      saveData();
                      favId.sort();
                    }),
                  )

As well as that, I also want to show the favorited items with ListView.builder in favPage.dart (another page). Of course, I want to get the favId from detailDoaPage.dart. How can I implement the provider/riverpod across these 2 pages?

Here is the preview of my app :

enter image description here

Thank you :)


Solution

  • My recommended approach would be to create a StateNotifier that handles the state as well as the interactions with SharedPreferences. The following simplifies the logic in your widgets as well.

    final sharedPrefs =
        FutureProvider<SharedPreferences>((_) async => await SharedPreferences.getInstance());
    
    class FavoriteIds extends StateNotifier<List<String>> {
      FavoriteIds(this.pref) : super(pref?.getStringList("id") ?? []);
    
      static final provider = StateNotifierProvider<FavoriteIds, List<String>>((ref) {
        final pref = ref.watch(sharedPrefs).maybeWhen(
              data: (value) => value,
              orElse: () => null,
            );
        return FavoriteIds(pref);
      });
    
      final SharedPreferences? pref;
    
      void toggle(String favoriteId) {
        if (state.contains(favoriteId)) {
          state = state.where((id) => id != favoriteId).toList();
        } else {
          state = [...state, favoriteId];
        }
        // Throw here since for some reason SharedPreferences could not be retrieved
        pref!.setStringList("id", state);
      }
    }
    

    Usage:

    class DoaWidget extends ConsumerWidget {
      const DoaWidget({Key? key, required this.doa}) : super(key: key);
    
      final Doa doa;
    
      @override
      Widget build(BuildContext context, ScopedReader watch) {
        final favoriteIds = watch(FavoriteIds.provider);
    
        return IconButton(
          icon: favoriteIds.contains('') ? Icon(Icons.favorite) : Icon(Icons.favorite_border),
          color: favoriteIds.contains('') ? Colors.red : Colors.grey,
          onPressed: () => context.read(FavoriteIds.provider.notifier).toggle(doa.id.toString()),
        );
      }
    }