Flutter 如何在没有 MediaQuery 的情况下获得亮度?
Flutter how to get brightness without MediaQuery?
我的目标是创建一个应用程序,用户可以在其中选择自己喜欢的主题。
我正在使用共享首选项保存用户的选择,以便我可以在下一个应用程序启动时加载它。
用户可以 select:
- 深色模式(独立于 OS 设置)
- 灯光模式(独立于 OS 设置)
- 系统(暗模式和亮模式之间的变化取决于 OS 设置)
在 BLoC 的帮助下,我几乎实现了我想要的。但问题是我需要在我的 Bloc 事件中传递亮度。为了获得系统 (OS) 亮度,我需要使用
MediaQuery.of(context).platformBrightness
但是 Bloc 在 MaterialApp 之前启动,因此 MediaQuery 不可用。当然我可以稍后传递亮度(从 MaterialApp 的子小部件)但是然后(例如,如果用户激活了暗模式)它从亮到暗但对用户可见的时间非常短(因为在 InitialState 中我在轻模式下通过了)。
class MyApp extends StatelessWidget {
final RecipeRepository recipeRepository;
MyApp({Key key, @required this.recipeRepository})
: assert(recipeRepository != null),
super(key: key);
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<ThemeBloc>(create: (context) =>
ThemeBloc(),),
],
child: BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, state){
return MaterialApp(
theme: state.themeData,
title: 'Flutter Weather',
localizationsDelegates: [
FlutterI18nDelegate(fallbackFile: 'en',),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
supportedLocales: [
const Locale("en"),
const Locale("de"),
],
home: Home(recipeRepository: recipeRepository),
);
},
),
);
}
}
ThemeBloc:
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
@override
ThemeState get initialState =>
ThemeState(themeData: appThemeData[AppTheme.Bright]);
@override
Stream<ThemeState> mapEventToState(
ThemeEvent event,
) async* {
if (event is LoadLastTheme) {
ThemeData themeData = await _loadLastTheme(event.brightness);
yield ThemeState(themeData: themeData);
}
if (event is ThemeChanged) {
await _saveAppTheme(event.theme);
yield ThemeState(themeData: appThemeData[event.theme]);
}
}
Future<ThemeData> _loadLastTheme(Brightness brightness) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
String themeString = prefs.getString(SharedPrefKeys.appThemeKey);
print("saved theme: $themeString");
if ((prefs.getString(SharedPrefKeys.appThemeKey) != null) &&
themeString != "AppTheme.System") {
switch (themeString) {
case "AppTheme.Bright":
{
return appThemeData[AppTheme.Bright];
}
break;
///Selected dark mode
case "AppTheme.Dark":
{
return appThemeData[AppTheme.Dark];
}
break;
}
}
print("brightness: $brightness");
if (brightness == Brightness.dark) {
return appThemeData[AppTheme.Dark];
} else {
return appThemeData[AppTheme.Bright];
}
}
Future<void> _saveAppTheme(AppTheme appTheme) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString(SharedPrefKeys.appThemeKey, appTheme.toString());
}
}
如果你绝对必须这样做,你可以像这样直接从低级 window 对象中获取 MediaQuery
数据:
final brightness = MediaQueryData.fromWindow(WidgetsBinding.instance.window).platformBrightness;
但是,我强烈建议您考虑一下,如果您需要从您的集团内部访问 MediaQuery
,您应该将 BlocProvider
移动到 MaterialApp
之后实例化,这样您可以正常访问MediaQuery
。
我的目标是创建一个应用程序,用户可以在其中选择自己喜欢的主题。 我正在使用共享首选项保存用户的选择,以便我可以在下一个应用程序启动时加载它。 用户可以 select: - 深色模式(独立于 OS 设置) - 灯光模式(独立于 OS 设置) - 系统(暗模式和亮模式之间的变化取决于 OS 设置) 在 BLoC 的帮助下,我几乎实现了我想要的。但问题是我需要在我的 Bloc 事件中传递亮度。为了获得系统 (OS) 亮度,我需要使用
MediaQuery.of(context).platformBrightness
但是 Bloc 在 MaterialApp 之前启动,因此 MediaQuery 不可用。当然我可以稍后传递亮度(从 MaterialApp 的子小部件)但是然后(例如,如果用户激活了暗模式)它从亮到暗但对用户可见的时间非常短(因为在 InitialState 中我在轻模式下通过了)。
class MyApp extends StatelessWidget {
final RecipeRepository recipeRepository;
MyApp({Key key, @required this.recipeRepository})
: assert(recipeRepository != null),
super(key: key);
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<ThemeBloc>(create: (context) =>
ThemeBloc(),),
],
child: BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, state){
return MaterialApp(
theme: state.themeData,
title: 'Flutter Weather',
localizationsDelegates: [
FlutterI18nDelegate(fallbackFile: 'en',),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
supportedLocales: [
const Locale("en"),
const Locale("de"),
],
home: Home(recipeRepository: recipeRepository),
);
},
),
);
}
}
ThemeBloc:
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
@override
ThemeState get initialState =>
ThemeState(themeData: appThemeData[AppTheme.Bright]);
@override
Stream<ThemeState> mapEventToState(
ThemeEvent event,
) async* {
if (event is LoadLastTheme) {
ThemeData themeData = await _loadLastTheme(event.brightness);
yield ThemeState(themeData: themeData);
}
if (event is ThemeChanged) {
await _saveAppTheme(event.theme);
yield ThemeState(themeData: appThemeData[event.theme]);
}
}
Future<ThemeData> _loadLastTheme(Brightness brightness) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
String themeString = prefs.getString(SharedPrefKeys.appThemeKey);
print("saved theme: $themeString");
if ((prefs.getString(SharedPrefKeys.appThemeKey) != null) &&
themeString != "AppTheme.System") {
switch (themeString) {
case "AppTheme.Bright":
{
return appThemeData[AppTheme.Bright];
}
break;
///Selected dark mode
case "AppTheme.Dark":
{
return appThemeData[AppTheme.Dark];
}
break;
}
}
print("brightness: $brightness");
if (brightness == Brightness.dark) {
return appThemeData[AppTheme.Dark];
} else {
return appThemeData[AppTheme.Bright];
}
}
Future<void> _saveAppTheme(AppTheme appTheme) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString(SharedPrefKeys.appThemeKey, appTheme.toString());
}
}
如果你绝对必须这样做,你可以像这样直接从低级 window 对象中获取 MediaQuery
数据:
final brightness = MediaQueryData.fromWindow(WidgetsBinding.instance.window).platformBrightness;
但是,我强烈建议您考虑一下,如果您需要从您的集团内部访问 MediaQuery
,您应该将 BlocProvider
移动到 MaterialApp
之后实例化,这样您可以正常访问MediaQuery
。