Режим постоянной темы с Riverpod

Я пытаюсь сохранить ThemeMode, используя пакеты Riverpod и shared_preferences. Вот что я сделал в themeNotifier:

import 'package:flutter/material.dart';
import 'settings_repository.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'theme_notifier.g.dart';

@riverpod
class ThemeNotifier extends _$ThemeNotifier {
  @override
  ThemeMode build() {
    return ThemeMode.system;
  }

  Future themeMode() async {
    state = await ref.watch(settingsRepositoryProvider).loadThemeMode();
  }

  Future updateThemeMode(ThemeMode? newThemeMode) async {
    if (newThemeMode == null) return;
    if (state == newThemeMode) return;
    state = newThemeMode;

    await ref
        .watch(settingsRepositoryProvider)
        .updateThemeMode(theme: newThemeMode);
  }
}

и вот что я сделал в settingsRepository:

import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shared_preferences/shared_preferences.dart';

part 'settings_repository.g.dart';

class SettingsRepository {
  final Future<SharedPreferencesWithCache> _prefs =
      SharedPreferencesWithCache.create(
          cacheOptions: const SharedPreferencesWithCacheOptions(
               // This cache will only accept the key 'isDarkMode'.
              allowList: <String>{'isDarkMode'}));

  SettingsRepository();

  get prefs => _prefs;

  Future<ThemeMode> loadThemeMode() async {
    final SharedPreferencesWithCache prefs = await _prefs;
    final isDarkMode = prefs.getBool('isDarkMode');
    log('THEME MODE :: $isDarkMode');
    return isDarkMode == null
        ? ThemeMode.system
        : isDarkMode
            ? ThemeMode.dark
            : ThemeMode.light;
  }

  Future<void> updateThemeMode({ThemeMode? theme}) async {
    final SharedPreferencesWithCache prefs = await _prefs;

    await prefs.setBool('isDarkMode', theme == ThemeMode.dark);
    log('THEME MODE UPDATE:: $theme');
    log('isDarkMode :: ${prefs.getBool('isDarkMode')}');
  }
}

@riverpod
SettingsRepository settingsRepository(SettingsRepositoryRef ref) =>
    SettingsRepository();

затем я потребляю свой materialapp и передаю его провайдеру:

Widget build(BuildContext context) {
    
    return Consumer(
      builder: (context, ref, _) {
        return MaterialApp(
          home: const RootNavigationBar(),
          
          restorationScopeId: 'app',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(),
          darkTheme: ThemeData.dark(),
          themeMode: ref.watch(themeNotifierProvider),

          
         
      },
    );
}

Все работает нормально, но когда я пытаюсь закрыть приложение, а затем открыть его, выдает эту ошибку.

════════ Exception caught by widgets library ═══════════════════════════════════
The following _TypeError was thrown building Consumer(dirty, dependencies: [UncontrolledProviderScope], state: _ConsumerState#733d8):
type 'Future<dynamic>' is not a subtype of type 'ThemeMode'
The relevant error-causing widget was:
    Consumer Consumer:file:///D:/Documents/Flutter/myapp/lib/src/app.dart:27:12

Затем приложение запускается нормально после нескольких секунд темного экрана.

В документации Riverpod рекомендуется избегать использования ref.watch в асинхронном контексте.

Dan R 14.08.2024 10:29

@DanR Чего следует избегать, так это использования ref.watch после асинхронного разрыва. В коде ОП его нет. Проблема заключается в использовании ref.watch в коде, который выполняется императивно. В этом случае вместо него следует использовать ref.read.

Dhafin Rayhan 14.08.2024 14:47

Я согласен. Точнее, в документации говорится: «Метод просмотра не следует вызывать асинхронно, как внутри onPressed ElevatedButton». см. riverpod.dev/docs/concepts/… .

Dan R 14.08.2024 17:35
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема решена

Наконец я нашел решение: проблема была в themeNotifier при использовании метода ref.watch для доступа к провайдеру. а затем вызвать функцию themeMode внутри метода сборки, а не возвращать ее из него, и на самом деле это была единственная проблема (вернуть асинхронную функцию внутри метода сборки themeNotifier)

Комментарий @Dan R был полезен, он предложил избегать ref.watch в асинхронном контексте. Это хорошая практика.


вот окончательное решение кода:


Код темыNotifier

import 'package:flutter/material.dart';
import 'settings_repository.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'theme_notifier.g.dart';

@riverpod
class ThemeNotifier extends _$ThemeNotifier {
 @override
 ThemeMode build() {
  themeMode();
  return ThemeMode.system;
  }

themeMode() async {
 final ThemeMode themeMode =
    await ref.read(settingsRepositoryProvider).loadThemeMode();
 state = themeMode;
 }

Future updateThemeMode(ThemeMode? newThemeMode) async {
  if (newThemeMode == null) return;
 if (state == newThemeMode) return;
 state = newThemeMode;

 await ref
    .read(settingsRepositoryProvider)
    .updateThemeMode(theme: newThemeMode);
 }
}

Другие вопросы по теме