使用多个提供者在启动时加载共享首选项,没有消费者
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 插件的扩展示例,我将向您展示它在我以前的项目中是如何完成的:
创建 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);
}
}
创建 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;
}
}
在主函数内部调用 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,
),
);
}
最后,初始化提供者,在我的例子中使用 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 的小部件很重要。
我正在尝试在应用加载时加载已保存的偏好设置,但不确定如何操作。我要么出错,要么最终创建一个循环。
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 插件的扩展示例,我将向您展示它在我以前的项目中是如何完成的:
创建 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); } }
创建 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; } }
在主函数内部调用 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, ), ); }
最后,初始化提供者,在我的例子中使用 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 的小部件很重要。