使用多个提供者在启动时加载共享首选项,没有消费者

Load sharedpreferences on startup with multiprovider, no conusmer

我正在尝试在应用加载时加载已保存的偏好设置,但不确定如何操作。我要么出错,要么最终创建一个循环。

providers.dart

class SettingsProvider with ChangeNotifier {
  SharedPreferences? _prefs;
  String _savedCurrency = '£';
  String _currentTheme = 'light';

  String get currencySymbol => _savedCurrency;
  String get currentTheme => _currentTheme;

  _initPrefs() async {
    _prefs ??= await SharedPreferences.getInstance();
  }

  loadSettings() {
    _savedCurrency = '£';
    _currentTheme = 'light';

    loadSavedTheme();
    loadSavedCurrency();
  }

  // Currency
  Future<void> loadSavedCurrency() async {
    await _initPrefs();
    _savedCurrency = _prefs!.getString('currency') ?? '£';
    notifyListeners();
  }

  Future<void> saveNewCurrency(String newCurrency) async {
    await _initPrefs();
    _prefs!.setString('currency', newCurrency);
    _savedCurrency = newCurrency;
    notifyListeners();
  }

  //Theme

  Future<void> loadSavedTheme() async {
    await _initPrefs();
    _currentTheme = _prefs!.getString('theme') ?? '£';
    notifyListeners();
  }

  Future<void> saveNewTheme(String newTheme) async {
    await _initPrefs();
    _prefs!.setString('theme', newTheme);
    _currentTheme = newTheme;
    notifyListeners();
  }
}

main.dart

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) => MultiProvider(
          providers: [
            ChangeNotifierProvider(create: (_) => GoogleSignInProvider()),
            ChangeNotifierProvider(
                create: (_) => SettingsProvider().loadSettings()),
          ],
          builder: (context, child) {
            String? _theme =
                Provider.of<SettingsProvider>(context, listen: true)
                    .currentTheme;
            print('theme is $_theme');
            return MaterialApp(
              home: LandingPage(),
              theme: ThemeData.light(),
              darkTheme: ThemeData.dark(),
              themeMode: applyThemeMode(_theme),
            );
          });
}

ThemeMode applyThemeMode(String? theme) {
  if (theme == null || theme.isEmpty) {
    return ThemeMode.system;
  }
  if (theme == 'light') {
    return ThemeMode.light;
  } else if (theme == 'dark') {
    return ThemeMode.dark;
  } else {
    return ThemeMode.system;
  }
}

不确定您的应用是否需要它,但我在 main.dart 中所做的是检查用户是否已登录。

根据登录状态(true 或 false),我将用户重定向到登录屏幕或启动屏幕。在启动屏幕的初始化中,我在我的一个提供程序中调用了一个初始化函数。当启动屏幕内的初始化完成后,用户将被重定向到主屏幕。由于画面背景相同,所以初始化体验非常流畅。

对于您的代码:

ChangeNotifierProvider(create: (_) => SettingsProvider().loadSettings())

应该是:

ChangeNotifierProvider(create: (_) => SettingsProvider())

可以在第一个屏幕的初始化中调用提供商的 loadSettings()。

创建新的 Flutter 应用程序时,Flutter 模板中现在提供了一个很好的示例(不使用 Provider)。

由于您请求使用 Provider 插件的扩展示例,我将向您展示它在我以前的项目中是如何完成的:

  1. 创建 SettingsController class 以应用 UI 更改并通过 ChangeNotifier

    监听
    class SettingsController with ChangeNotifier {
        SettingsController(this._settingsService);
    
        // Make SettingsService a private variable so it is not used directly.
        final SettingsService _settingsService;
    
        // Make ThemeMode a private variable so it is not updated directly without
        // also persisting the changes with the SettingsService.
        late ThemeMode _themeMode;
    
        // Allow Widgets to read the user's preferred ThemeMode.
        ThemeMode get themeMode => _themeMode;
    
        late bool _showNavigationLabels;
    
        bool get showNavigationLabels => _showNavigationLabels;
    
        /// Retrieve previously saved user's settings from [SharedPreferences] using
        /// [SettingsService]
        Future<void> loadSettings() async {
           _themeMode = await _settingsService.themeMode();
           _showNavigationLabels = await _settingsService.showNavigationLabels();
    
           // Important! Inform listeners a change has occurred.
           notifyListeners();
        }
    
        /// Update and persist the ThemeMode based on the user's selection.
        Future<void> updateThemeMode(ThemeMode? newThemeMode) async {
          if (newThemeMode == null) return;
    
          // Dot not perform any work if new and old ThemeMode are identical
          if (newThemeMode == _themeMode) return;
    
          // Otherwise, store the new theme mode in memory
          _themeMode = newThemeMode;
    
          // Important! Inform listeners a change has occurred.
          notifyListeners();
    
          // Persist the changes to a local database or the internet using the
          await _settingsService.updateThemeMode(newThemeMode);
        }
    
        Future<void> updateShowNavigationLabels(bool? newShowNavigationLabels) async {
          if (newShowNavigationLabels == null) return;
    
          // Dot not perform any work if new and old ThemeMode are identical
          if (newShowNavigationLabels == _showNavigationLabels) return;
    
          // Otherwise, store the new theme mode in memory
          _showNavigationLabels = newShowNavigationLabels;
    
          // Important! Inform listeners a change has occurred.
          notifyListeners();
    
          // Persist the changes to a local database or the internet using the
          await _settingsService.updateShowNavigationLabels(newShowNavigationLabels);
        }
    }
    
  2. 创建 SettingsService 以编写将设置保存在本地存储/将其写入数据库的逻辑:

    /// A service that stores and retrieves user settings.
    ///
    /// If you'd like to persist the user settings locally,
    /// use the shared_preferences package.
    /// If you'd like to store settings on a web server, use the http / dio package.
    class SettingsService {
      /// Loads the User's preferred ThemeMode from local or remote storage.
      Future<ThemeMode> themeMode() async {
        SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
    
        bool? darkMode = sharedPreferences.getBool('darkMode');
    
        if (darkMode == null) {
          return ThemeMode.system;
        } else {
          return darkMode ? ThemeMode.dark : ThemeMode.light;
        }
      }
    
      /// Persists the user's preferred ThemeMode to local or remote storage.
      /// Use the shared_preferences package to persist settings locally or the
      /// http package to persist settings over the network.
      Future<void> updateThemeMode(ThemeMode theme) async {
        SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
    
        await sharedPreferences.setBool(
          'darkMode',
          theme == ThemeMode.dark ? true : false,
        );
      }
    
      /// Persists the user's preferred showNavigationLabels to local or remote storage.
      Future<void> updateShowNavigationLabels(bool showNavigationLabels) async {
        SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
    
        await sharedPreferences.setBool(
          'showNavigationLabels',
          showNavigationLabels,
        );
      }
    
      /// Loads the User's preferred ShowNavigationLabels from local or remote storage.
      Future<bool> showNavigationLabels() async {
        SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
    
        bool? showNavigationLabels =
            sharedPreferences.getBool('showNavigationLabels');
    
        return showNavigationLabels ?? true;
      }
    }
    
  3. 在主函数内部调用 settingsController.loadSettings() 并将控制器传递给 InitProviders 小部件:

    Future<void> main() async {
     WidgetsFlutterBinding.ensureInitialized();
    
     /// Set up the SettingsController, which will glue user settings to multiple
     /// Flutter Widgets.
     final SettingsController settingsController = SettingsController(
       SettingsService(),
     );
    
     /// Load the settings
     await settingsController.loadSettings();
    
     runApp(
       InitProviders(
         settingsController,
       ),
     );
    }
    
  1. 最后,初始化提供者,在我的例子中使用 InitProviders 小部件

    /// Initialize Providers to share global state throughout the app
    class InitProviders extends StatelessWidget {
     const InitProviders(
       this.settingsController, {
       Key? key,
     }) : super(key: key);
    
     final SettingsController settingsController;
    
     @override
     Widget build(BuildContext context) {
       return MultiProvider(
         providers: [
           ChangeNotifierProvider(
             create: (context) => settingsController,
           ),
         ],
         child: const InitApp(),
       );
     }
    }
    

注意 InitProviders 在应用程序的最开始是如何被调用的——在小部件树的顶部。这对于 Provider 插件和使用 InheritedWidget 的小部件很重要。