我正在使用Riverpod state Provider管理flutter应用程序的ThemeMode,该提供程序一直按预期工作,直到我尝试读取Theme.of(context)
以获取ThemeData的当前值,这会导致部件的重建过多(连续13~14次)。因此,我决定在Riverpod的repository example之后为ThemeData创建一个提供程序,但我仍然得到了这些不必要的重建。如何防止这些不必要的Riverpod重建以获取ThemeData?为什么会这样呢
此代码在github上可用。
主应用程序:
final themeProvider = Provider<ThemeData>(
(ref) => throw UnimplementedError(),
dependencies: const [],
);
void main() {
runApp(const ProviderScope(child: MainApp()));
}
class MainApp extends ConsumerWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final ThemeMode themeMode = ref.watch(themeModeStateProvider);
if (kDebugMode) {
print("building app");
}
return MaterialApp(
theme: FlexThemeData.light(scheme: FlexScheme.mandyRed),
darkTheme: FlexThemeData.dark(scheme: FlexScheme.mandyRed),
themeMode: themeMode,
builder: (context, child) {
final theme = Theme.of(context);
return ProviderScope(
overrides: [
themeProvider.overrideWithValue(theme),
],
child: child!,
);
},
home: const HomeScreen(),
);
}
}
字符串
主题模式提供程序:
@riverpod
class ThemeModeState extends _$ThemeModeState {
@override
ThemeMode build() {
return ThemeMode.dark;
}
static ThemeMode getSystemTheme(BuildContext context) {
ThemeMode mode = ThemeMode.system;
if (mode == ThemeMode.system) {
if (MediaQuery.of(context).platformBrightness == Brightness.light) {
mode = ThemeMode.light;
} else {
mode = ThemeMode.dark;
}
}
return mode;
}
void toggleThemeMode() {
if (state == ThemeMode.dark) {
state = ThemeMode.light;
} else {
state = ThemeMode.dark;
}
}
}
型
主屏幕:
class HomeScreen extends ConsumerWidget {
static String routeName = "home";
const HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final ThemeData themeData = ref.watch(themeProvider);
final TextStyle headlineMedium = themeData.textTheme.headlineLarge!;
if (kDebugMode) {
print("building home");
}
return Scaffold(
body: Center(
child: Column(
children: [
Text(
"Hello World",
style: headlineMedium,
),
SwitchListTile(
contentPadding: EdgeInsets.zero,
title: const Text('Theme mode'),
value: ref.watch(themeModeStateProvider) == ThemeMode.light,
onChanged: (value) {
ref.watch(themeModeStateProvider.notifier).toggleThemeMode();
},
),
],
),
),
);
}
}
型
3条答案
按热度按时间gt0wga4j1#
我附上一个较短的代码来重现这个问题(不使用生成):
字符串
**另外,**widget生命周期管理方法和回调中不要使用
ref.watch
。使用ref.read
代替:型
您的问题在于
MainApp
小部件,特别是builder
参数。这个问题的简单解决方案是不在builder
中使用of(context)
,它看起来像这样:型
现在,您的重建已优化。
说到未来,很可能你的
ThemeData
也应该有一个完整的NotifierProvider
,在build
方法中优雅地将watch
扩展到当前的themeModeStateProvider
。那么ProviderScope -> overrideWithValue
构造就完全没有用处了。好吧,长期的解决方案是将问题写入Flutter存储库。
考虑
Localizations
后,最终版本如下所示:型
sqougxex2#
您不希望在应用程序中收听整个主题。您希望小部件只监听它们使用的内容。
由于您已将主题转换为 Provider,因此您可以使用Riverpod的“选择”功能:
TL;DR,而不是:
字符串
执行:
型
这样,您的消费者将只收听
headlineMedium
,而不是整个主题。yyyllmsg3#
MediaQuery.of(context).platformBrightness == Brightness.light
会在MediaQuery发生任何更改时触发,包括屏幕大小(旋转?)。您可能希望使用新的MediaQuery.platformBrightnessOf
仅在更改该参数时触发。