功能类 Widget 指的是非 UI Widget,具有一定功能的 Widget。Flutter 中功能类 Widget 有很多,本节主要讲如下的功能类 Widget:

  • Theme
  • MediaQuery
  • Navigator
  • InheritedWidget

Theme

Theme Widget 的功能是为 MaterialApp 定义主题数据,如导航栏颜色、标题字体、Icon 样式。

Theme 在 MaterialApp 中的位置

在看代码的时候,你可能没有看到 Theme 的使用,实际上是在使用 MaterialApp 时,会自动将 Theme Widget 加上去:
Flutter 学习(二十七)功能类 Widget - 图1
Theme 会嵌套 MaterialApp。

在 Flutter 中获取 Theme 的实例

要想在 Flutter 中获取 Theme 的实例,首先根 Widget 必须是 MaterialApp,然后在 MaterialApp 的子 Widget 里运行:

  1. Theme.of(context);

返回的类型是 ThemeData ,Theme 的所有数据都存储在 ThemeData 里。

ThemeData 说明

ThemeData 的构造函数为:

  1. factory ThemeData({
  2. Brightness brightness,
  3. MaterialColor primarySwatch,
  4. Color primaryColor,
  5. Brightness primaryColorBrightness,
  6. Color primaryColorLight,
  7. Color primaryColorDark,
  8. Color accentColor,
  9. Brightness accentColorBrightness,
  10. Color canvasColor,
  11. Color scaffoldBackgroundColor,
  12. Color bottomAppBarColor,
  13. Color cardColor,
  14. Color dividerColor,
  15. Color highlightColor,
  16. Color splashColor,
  17. InteractiveInkFeatureFactory splashFactory,
  18. Color selectedRowColor,
  19. Color unselectedWidgetColor,
  20. Color disabledColor,
  21. Color buttonColor,
  22. ButtonThemeData buttonTheme,
  23. Color secondaryHeaderColor,
  24. Color textSelectionColor,
  25. Color cursorColor,
  26. Color textSelectionHandleColor,
  27. Color backgroundColor,
  28. Color dialogBackgroundColor,
  29. Color indicatorColor,
  30. Color hintColor,
  31. Color errorColor,
  32. Color toggleableActiveColor,
  33. String fontFamily,
  34. TextTheme textTheme,
  35. TextTheme primaryTextTheme,
  36. TextTheme accentTextTheme,
  37. InputDecorationTheme inputDecorationTheme,
  38. IconThemeData iconTheme,
  39. IconThemeData primaryIconTheme,
  40. IconThemeData accentIconTheme,
  41. SliderThemeData sliderTheme,
  42. TabBarTheme tabBarTheme,
  43. CardTheme cardTheme,
  44. ChipThemeData chipTheme,
  45. TargetPlatform platform,
  46. MaterialTapTargetSize materialTapTargetSize,
  47. PageTransitionsTheme pageTransitionsTheme,
  48. AppBarTheme appBarTheme,
  49. BottomAppBarTheme bottomAppBarTheme,
  50. ColorScheme colorScheme,
  51. DialogTheme dialogTheme,
  52. Typography typography,
  53. CupertinoThemeData cupertinoOverrideTheme
  54. }) {
  55. ...
  56. }

ThemeData 的使用

ThemeData 的数据是通过 MaterialApp 的 theme 参数来使用的,如:

  1. MaterialApp(
  2. title: "Flutter Demo",
  3. theme: ThemeData(
  4. primaryColor: Colors.blue,
  5. ),
  6. ...
  7. }

Theme.of(context) 的使用

Theme.of(context) 会获得 MaterialApp 的主题数据 ThemeData,这些数据都是 final 的,所以是只读的,不能修改。

这里写一个例子,读取 MaterialApp 的 primaryColor 用于 Text 的 颜色。

  1. import 'package:flutter/material.dart';
  2. void main() => runApp(ThemeFeatureWidget());
  3. class ThemeFeatureWidget extends StatelessWidget {
  4. @override
  5. Widget build(BuildContext context) {
  6. return MaterialApp(
  7. title: "Flutter Demo",
  8. theme: ThemeData(
  9. primaryColor: Colors.red,
  10. ),
  11. home: Scaffold(
  12. appBar: AppBar(title: Text("Flutter 功能类Widget -- Theme")),
  13. body: ChildText()),
  14. );
  15. }
  16. }
  17. class ChildText extends StatelessWidget {
  18. @override
  19. Widget build(BuildContext context) {
  20. // TODO: implement build
  21. return Text("Hello Flutter",
  22. style: TextStyle(color: Theme.of(context).primaryColor));
  23. }
  24. }

运行效果为:

Flutter 学习(二十七)功能类 Widget - 图2

MediaQuery

MediaQuery Widget 的功能是查询一些数据,例如整个屏幕的宽度、高度、设备像素比等数据

MediaQuery 在 MaterialApp 中的位置

MediaQuery 同 Theme 一样,只看代码,你可能没有看到 MediaQuery 的使用,但实际上在使用 MaterialApp 时,会自动将 MediaQuery Widget 加上去:
Flutter 学习(二十七)功能类 Widget - 图3
MediaQuery 会嵌套 MaterialApp。

在 Flutter 中获取 MediaQuery 的实例

要想在 Flutter 中获取 MediaQuery 的实例,首先根 Widget 必须是 MaterialApp,然后在 MaterialApp 的子 Widget 里运行:

  1. MediaQuery.of(context);

返回的类型是 MediaQueryData。

MediaQueryData 说明

MediaQueryData 的构造函数为:

  1. class MediaQueryData {
  2. const MediaQueryData({
  3. this.size = Size.zero,
  4. this.devicePixelRatio = 1.0,
  5. this.textScaleFactor = 1.0,
  6. this.platformBrightness = Brightness.light,
  7. this.padding = EdgeInsets.zero,
  8. this.viewInsets = EdgeInsets.zero,
  9. this.alwaysUse24HourFormat = false,
  10. this.accessibleNavigation = false,
  11. this.invertColors = false,
  12. this.disableAnimations = false,
  13. this.boldText = false,
  14. });
  15. ...
  16. }
参数名字 参数类型 意义
size Size 屏幕的逻辑像素大小
size.width:屏幕的宽度(逻辑像素)
size.height:屏幕的高度(逻辑像素)
devicePixelRatio double 像素比:每个逻辑像素代表的设备像素数
textScaleFactor double 每个逻辑像素代表的字体像素数
platformBrightness Brightness 平台的亮度模式,有两种模式:light和dark
默认为light
padding EdgeInsets 表示系统状态栏或者刘海屏的padding
viewInsets EdgeInsets 表示设备键盘的padding
alwaysUse24HourFormat bool 格式化时间时是否使用24小时格式
accessibleNavigation bool 是否使用TalkBack或 VoiceOver等辅助功能服务与应用程序进行交互
invertColors bool 设备是否反转平台的颜色。
现在只能在 iOS 上使用
disableAnimations bool 平台是否要求禁用动画
boldText bool 平台是否请求使用粗体来绘制文本。

MediaQuery.of(context) 的使用

MediaQuery.of(context) 获得的 MediaQueryData 数据里,我们一般关心的是 :

  • Size
  • devicePixelRatio

Size

Size 是整个屏幕的宽高数据,不管你在哪里调用 MediaQuery.of(context) ,获得的都是整个屏幕的 Size。

devicePixelRatio

devicePixelRatio 表示的是每个逻辑像素代表的设备像素数。
设备像素数 = 逻辑像素数 * devicePixelRatio

获取子 Widget 的宽高

要想获取子 Widget 的宽高,就不能使用 MediaQuery.of(context) ,这个时候要用到 GlobalKey。

为子 Widget 设置 GlobalKey,然后通过 GlobalKey 获取子 Widget 的宽高。

这里写一个例子,获取屏幕的宽高和子 Widget 的宽高。

Navigator

Navigator 是用来管理页面显示的 Widget,这些页面以堆栈的数据结构存储,当有新页面显示时,就会把上一个页面压入栈,所以栈底是最初的页面,栈顶是当前的页面。

Navigator 在 MaterialApp 中的位置

Navigator 同 Theme 一样,只看代码,你可能没有看到 Navigator 的使用,但实际上在使用 MaterialApp 时,会自动将 Navigator Widget 加上去:
Flutter 学习(二十七)功能类 Widget - 图4
Navigator 会嵌套 MaterialApp。

Routes(路由)

移动应用将全屏幕显示的叫做 “screens” 或者 “pages”,在 Flutter 里叫做 Routes(路由),Navigator 就是用来管理 Routes 显示的 Widget。

Navigator 的说明

Navigator 提供了操作 Routes 的两种方法: Navigator.pushNavigator.pop

在 Flutter 中获取 Navigator 的实例

在 Flutter 中除了 MaterialApp 提供的 Navigator,也可以实现自定义的 Navigator,但是大部分情况下还是使用的 MaterialApp 提供的 Navigator。

首先根 Widget 必须是 MaterialApp,然后在 MaterialApp 的子 Widget 里运行:

  1. Navigator.of(context);

返回的类型是 NavigatorState,是 Navigator Widget 的 State 类。

NavigatorState 说明

以下是 NavigatorState 类的图:

Flutter 学习(二十七)功能类 Widget - 图5

以下是 Navigator 类的图:

Flutter 学习(二十七)功能类 Widget - 图6

可以看到 Navigator 类里的方法和 NavigatorState 类的方法都一样,所以也可以直接使用 Navigator ,而不是 Navigator.of(context)

这里 Navigator 就简单介绍完了, 具体使用会在后面介绍。

InheritedWidget

InheritedWidget 可以高效的将数据在 Widget 树中向下传递,通常用来共享数据,Flutter 中非常重要的一个功能 Widget。

前面介绍的 Theme 和 MediaQuery 之所以可以在子 Widget 内访问到数据,就是使用到了 InheritedWidget。

大家应该都有这样的经历,从 A 页面跳转到 B 页面,但 B 页面需要用到 A 页面的一些数据,这时候该怎么做呢?
通常做法是,A 页面打开 B 页面的时候,把数据从 A 页面传递给 B 页面,但这样做,会使 A 页面的数据和 B 页面的数据产生割裂,如果又要求 B 页面产生的结果又返回给 A 页面呢?这使得我们要用很大的精力去处理数据传输的问题。

InheritedWidget 的目的就是处理数据问题,可以将共享的数据在 InheritedWidget 里实现,这样 A 页面和 B 页面都可以直接访问数据并修改,数据一旦修改,就会触发依赖这个数据的 UI 的刷新。InheritedWidget 在 Flutter 开发中有很大的作用。

这里 InheritedWidget 就简单介绍完了, 具体使用会在后面介绍。

参考

【1】Flutter 实战
【2】Flutter 中文文档
【3】Flutter 完全手册