You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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:

  1. 修改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,
      ),
    );
  }
}
  1. 在主组件中调用时传入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 11:53:11