Flutter中使用BuildContext扩展获取主题模式遇MediaQuery缺失异常
解决Flutter中MediaQuery无祖先组件的异常
问题描述
尝试通过MediaQuery.of(this).platformBrightness获取设备主题模式时,触发以下Flutter异常:
FlutterError (No MediaQuery widget ancestor found.
ClienteApp widgets require a MediaQuery widget ancestor
相关代码
扩展代码
extension DarkMode on BuildContext { bool get isDarkMode { final brightness = MediaQuery.of(this).platformBrightness; return brightness == Brightness.dark; } }
主题文件
class Styles { static ThemeData themeData(BuildContext context) { return ThemeData( primaryColor: ThemeColors.primary, colorScheme: ColorScheme.fromSwatch( primarySwatch: MaterialColor(0xFF199BF1, primaryColor), brightness: context.isDarkMode ? Brightness.dark : Brightness.light, backgroundColor: ThemeColors.backGroundColor, ), ); } }
主组件
class ClienteApp extends StatelessWidget { final bool isLogged; final bool theme; const ClienteApp({Key? key, required this.isLogged, required this.theme}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Customer App', debugShowCheckedModeBanner: false, theme: Styles.themeData(context), darkTheme: ThemeData.dark(), themeMode: (theme) ? ThemeMode.dark : ThemeMode.light, initialRoute: (isLogged) ? '/home' : '/', routes: { '/': (context) => const LoginPage(), '/newuser': (context) => const NewUserScreen(), '/lostpassword': (context) => const LostPasswordIndexPage(), '/home': (context) => DefaultBottomBarController(child: const HomePage()), }, navigatorKey: NavigationService.navigatorState, ); } }
异常原因
在ClienteApp的build方法中,传递给Styles.themeData的context是ClienteApp自身的上下文。此时MaterialApp尚未完成构建,而MediaQuery组件是由MaterialApp提供的,因此该上下文不存在MediaQuery的祖先组件,导致触发异常。
解决方案
方案一:复用已有的theme参数控制亮度
既然已经通过theme参数控制themeMode,可以直接将该参数传入主题方法,避免依赖MediaQuery:
- 修改
Styles.themeData方法,增加isDark参数:
class Styles { static ThemeData themeData(BuildContext context, bool isDark) { return ThemeData( primaryColor: ThemeColors.primary, colorScheme: ColorScheme.fromSwatch( primarySwatch: MaterialColor(0xFF199BF1, primaryColor), brightness: isDark ? Brightness.dark : Brightness.light, backgroundColor: ThemeColors.backGroundColor, ), ); } }
- 在主组件中调用时传入
theme参数:
theme: Styles.themeData(context, theme),
方案二:直接获取系统平台亮度(无需依赖上下文)
如果需要获取系统默认的主题亮度,可以使用PlatformDispatcher直接获取,不需要依赖MediaQuery:
修改扩展代码:
import 'package:flutter/foundation.dart'; extension DarkMode on BuildContext { bool get isDarkMode { final brightness = PlatformDispatcher.instance.platformBrightness; return brightness == Brightness.dark; } }
注:Flutter 3.10+ 中
WidgetsBinding.instance.window已被废弃,推荐使用PlatformDispatcher.instance.platformBrightness。
方案三:延迟主题初始化到MaterialApp内部
如果一定要使用MediaQuery,可以通过Builder组件获取MaterialApp提供的上下文:
修改主组件的build方法:
@override Widget build(BuildContext context) { return MaterialApp( title: 'Customer App', debugShowCheckedModeBanner: false, darkTheme: ThemeData.dark(), themeMode: theme ? ThemeMode.dark : ThemeMode.light, initialRoute: isLogged ? '/home' : '/', routes: { '/': (context) => const LoginPage(), '/newuser': (context) => const NewUserScreen(), '/lostpassword': (context) => const LostPasswordIndexPage(), '/home': (context) => DefaultBottomBarController(child: const HomePage()), }, navigatorKey: NavigationService.navigatorState, home: Builder( builder: (innerContext) { // innerContext包含MaterialApp提供的MediaQuery return MaterialApp( title: 'Customer App', debugShowCheckedModeBanner: false, theme: Styles.themeData(innerContext), darkTheme: ThemeData.dark(), themeMode: theme ? ThemeMode.dark : ThemeMode.light, initialRoute: isLogged ? '/home' : '/', routes: { '/': (context) => const LoginPage(), '/newuser': (context) => const NewUserScreen(), '/lostpassword': (context) => const LostPasswordIndexPage(), '/home': (context) => DefaultBottomBarController(child: const HomePage()), }, navigatorKey: NavigationService.navigatorState, ); }, ), ); }
该方案稍显冗余,仅在特殊场景下推荐使用。
内容的提问来源于stack exchange,提问作者rafaelpadu
相关产品推荐
相关产品推荐

