顶层 Widge:MaterialApp

是什么

MaterialApp 是 Flutter 提供的 应用级顶层 Widget,它帮你一次性配置好应用的 主题、路由、导航、本地化 等全局环境,让你只需专注写页面。

如果没有 MaterialApp,你其实还是可以写 Flutter 应用,但很多 MaterialApp 自动帮你做的事情就要自己手动搭,而且步骤会很繁琐。

如果没有 MaterialApp,你得手动写:

  1. Theme(全局主题)
  2. Directionality(文本方向)
  3. Material(Material 风格环境)
  4. Navigator(导航管理)
  5. Localizations(国际化支持)
  6. 调试横幅/标题等额外功能

有Material 风格的程序的构建,当然相对应的是 ios 风格是 CupertinoApp

常用构造参数

参数 作用 示例
title 应用标题(在任务管理器或某些系统 UI 显示) "My App"
home 应用的首页 Widget HomePage()
routes 命名路由表(Map<String, WidgetBuilder> {"/about": (_) => AboutPage()}
theme 主题(Material 风格) ThemeData(primarySwatch: Colors.blue)
darkTheme 暗色模式主题 ThemeData.dark()
themeMode 控制主题模式 ThemeMode.system
debugShowCheckedModeBanner 是否显示右上角 DEBUG 横幅 false
locale / supportedLocales 多语言支持 Locale('en')
onGenerateRoute 动态路由生成逻辑 自定义函数
const MaterialApp({
  Key key,
  // 导航键 , key的作用提高复用性能
  this.navigatorKey,
  // 主页
  this.home,
  // 路由
  this.routes = const <String, WidgetBuilder>{},
  // 初始命名路由
  this.initialRoute,
  // 路由构造
  this.onGenerateRoute,
  // 未知路由
  this.onUnknownRoute,
  // 导航观察器
  this.navigatorObservers = const <NavigatorObserver>[],
  // 建造者
  this.builder,
  // APP 标题
  this.title = '',
  // 生成标题
  this.onGenerateTitle,
  // APP 颜色
  this.color,
  // 样式定义
  this.theme,
  // 主机暗色模式
  this.darkTheme,
  // 样式模式
  this.themeMode = ThemeMode.system,
  // 多语言 本地化
  this.locale,
  // 多语言代理
  this.localizationsDelegates,
  // 多语言回调
  this.localeListResolutionCallback,
  this.localeResolutionCallback,
  // 支持的多国语言
  this.supportedLocales = const <Locale>[Locale('en', 'US')],
  // 调试显示材质网格
  this.debugShowMaterialGrid = false,
  // 显示性能叠加
  this.showPerformanceOverlay = false,
  // 检查缓存图片的情况
  this.checkerboardRasterCacheImages = false,
  // 检查不必要的setlayer
  this.checkerboardOffscreenLayers = false,
  // 显示语义调试器
  this.showSemanticsDebugger = false,
  // 显示debug标记 右上角
  this.debugShowCheckedModeBanner = true,
})

简单示例

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // APP 标题
      // ios 没有用、 android 进程名称 、 web 标题tab栏名称
      title: 'Material App',

      // APP 颜色
      color: Colors.green,

      // 样式
      theme: ThemeData(
        primarySwatch: Colors.yellow,
      ),

      // 主机暗色模式
      darkTheme: ThemeData(
        primarySwatch: Colors.red,
      ),

      // 显示debug标记 右上角
      // debugShowCheckedModeBanner: false,

      // 调试显示材质网格
      // debugShowMaterialGrid: true,

      // 显示性能叠加
      // showPerformanceOverlay: true,

      // 检查缓存图片的情况
      // checkerboardRasterCacheImages: true,

      // 检查不必要的setlayer
      // checkerboardOffscreenLayers: true,

      // 显示语义调试器
      // showSemanticsDebugger: true,

      // 首页
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Material App'),
        ),
        body: Center(
          child: Column(
            children: const [
              Text("data"),
              FlutterLogo(
                size: 100,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

builder

builder的作用

在 Flutter 中,MaterialAppbuilder 参数其实是一个很常用但容易忽略的东西。

它的本质不是配置,而是一个 全局 UI 包裹入口

作用改变的是 Widget 树的结构,在全局页面外面加一层“壳子”,实现对所有页面的统一控制。

所以它非常适合做 全局性的约束 / 控制 / 功能注入

typedef TransitionBuilder = Widget Function(BuildContext context, Widget? child);

final TransitionBuilder? builder;

常见用途

全局注入布局或功能

比如给所有页面加上统一的 MediaQuery 调整字体缩放、强制方向等:

MaterialApp(
  builder: (context, child) {
    return MediaQuery(
      data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
      child: child!,
    );
  },
);

全局 Overlay / Loading / Toast

很多全局的提示框、Loading 动画(例如 flutter_easyloading)就是靠 builder 注入的:

MaterialApp(
  builder: (context, child) {
    return FlutterEasyLoading(child: child);
  },
);

其他

统一样式或功能增强

  • 比如全局加 Directionality、手势检测、SafeArea 等。

调试/监控

  • 可以在 builder 里对所有页面做统一的 debug 包装,统计渲染树、事件分发。

Widget 树的结构

没有 builder 的情况

MaterialApp(
  home: MyHomePage(),
);

//--------------------------------------------------------------------------------

MaterialApp
  └── WidgetsApp (内部封装)
        └── MediaQuery (系统提供的,描述屏幕大小、缩放、文字缩放等)
              └── Localizations
                    └── Directionality
                          └── Banner (DEBUG 横幅, 如果 debugShowCheckedModeBanner = true)
                              └── Navigator
                                    └── MyHomePage

builder 的情况

MaterialApp(
  builder: (context, child) {
    return MediaQuery(
      data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
      child: FlutterEasyLoading(child: child),
    );
  },
  home: MyHomePage(),
);

//--------------------------------------------------------------------------------

MaterialApp
  └── WidgetsApp (内部封装)
        └── MediaQuery (系统提供的,描述屏幕大小、缩放、文字缩放等)
              └── Localizations
                    └── Directionality
                          └── Banner (DEBUG 横幅, 如果 debugShowCheckedModeBanner = true)
                                └── builder 返回的内容
                                      └── MediaQuery (textScaleFactor = 1.0)   // 你写的
                                            └── FlutterEasyLoading
                                                  └── Navigator
                                                        └── MyHomePage
  1. MaterialApp 内部自己就会构建一层 MediaQuery,里面有系统默认的 textScaleFactor
    • builder 里再加了一个新的 MediaQuery,等于是 覆盖默认值(这里强制 textScaleFactor=1.0)。
  2. FlutterEasyLoading 被放在 Navigator 的外层,所以它能覆盖所有页面,随时显示全局的 Loading/Toast。
  3. Navigator 内部才是真正的页面路由栈,home: MyHomePage() 就是它的第一个页面。

×

喜欢就点赞,疼爱就打赏