MaterialApp
在前面我们已经分析了FlutterUI框架的加载过程,分析的系统启动部分,最原始的资源加载过程,所有的系统资源加载完成之后,就开始加载提供给开发者使用的接口数据,FlutterUI使用的是Bloc架构模式,所有的用户界面的入口是在runApp的时候提供给FlutterUI的框架层进行关联使用,WidgetsApp是FlutterUI层提供的一个接口,主要定义了标准、基础的FlutterUI层和用户UI之间的一个统一接口,我们可以自己自定义,MaterialApp作为系统框架层和开发者定义的数据来进行配置初始化WidgetsApp,DefaultTextStyle、MediaQuery、Localizations、Navigator、Overlay、SemanticsDebugger
MaterialApp同时需要完成两件事情:
1.保存全局性的数据和FlutterUI界面的Application配置
2.接收开发者的配置参数,进行初始化
3.配置全局导航
4.绘制性能图标
5.调试模式辅助工具
WidgetsApp
通过包裹多个不同类型的widget,保存整个App使用到的数据,把整个系统事件和全局信息通过WidgetsAppAPP的进行统一的管理处理,使用Stack控件保存整个用户UI接口的Widget层级,
WidgetsApp本身就是一个StatefulWidget,我们直接看一下build的执行过程
1.初始化Navigator(后续再分析导航控制器的使用过程)
2.提供builder接口给开发者替换掉系统默认的导航控制器,可以实现自己的导航FlutterUI绘制逻辑
3.DefaultTextStyle也是Widget的子类,InheritedWidget保存了主题数据,所用的用户界面获取全局的字体数据
5.PerformanceOverlay,现在UI的绘制图表,LeafRenderObjectWidget的子类,进行绘制
6.WidgetInspector查看视图的轮廓
7.处理键盘快捷键,一般在开发网页版本的时候经常用到Shortcuts
8.焦点事件的传递过程DefaultFocusTraversal,使用ReadingOrderTraversalPolicy事件进行传递
9._MediaQueryFromWindow控制整个FlutterUI在手机屏幕上的显示区域,刘海屏和普通屏幕的边界,圆角都不一样,所以在所有的Widget外出做一层包裹,避免有溢出或者有遮挡的部分
10.本地化Localizations
@override
Widget build(BuildContext context) {
Widget navigator;
if (_navigator != null) {
navigator = Navigator(
key: _navigator,
// If window.defaultRouteName isn't '/', we should assume it was set
// intentionally via `setInitialRoute`, and should override whatever
// is in [widget.initialRoute].
initialRoute: WidgetsBinding.instance.window.defaultRouteName != Navigator.defaultRouteName
? WidgetsBinding.instance.window.defaultRouteName
: widget.initialRoute ?? WidgetsBinding.instance.window.defaultRouteName,
onGenerateRoute: _onGenerateRoute,
onUnknownRoute: _onUnknownRoute,
observers: widget.navigatorObservers,
);
}
提供builder接口给开发者替换掉系统默认的导航控制器,可以实现自己的导航FlutterUI绘制逻辑,可以做一下定制的内容
Widget result;
if (widget.builder != null) {
result = Builder(
builder: (BuildContext context) {
return widget.builder(context, navigator);
},
);
} else {
assert(navigator != null);
result = navigator;
}
if (widget.textStyle != null) {
result = DefaultTextStyle(
style: widget.textStyle,
child: result,
);
}
PerformanceOverlay performanceOverlay;
// We need to push a performance overlay if any of the display or checkerboarding
// options are set.
if (widget.showPerformanceOverlay || WidgetsApp.showPerformanceOverlayOverride) {
performanceOverlay = PerformanceOverlay.allEnabled(
checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages,
checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers,
);
} else if (widget.checkerboardRasterCacheImages || widget.checkerboardOffscreenLayers) {
performanceOverlay = PerformanceOverlay(
checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages,
checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers,
);
}
if (performanceOverlay != null) {
result = Stack(
children: <Widget>[
result,
Positioned(top: 0.0, left: 0.0, right: 0.0, child: performanceOverlay),
],
);
}
if (widget.showSemanticsDebugger) {
result = SemanticsDebugger(
child: result,
);
}
assert(() {
if (widget.debugShowWidgetInspector || WidgetsApp.debugShowWidgetInspectorOverride) {
result = WidgetInspector(
child: result,
selectButtonBuilder: widget.inspectorSelectButtonBuilder,
);
}
if (widget.debugShowCheckedModeBanner && WidgetsApp.debugAllowBannerOverride) {
result = CheckedModeBanner(
child: result,
);
}
return true;
}());
Widget title;
if (widget.onGenerateTitle != null) {
title = Builder(
// This Builder exists to provide a context below the Localizations widget.
// The onGenerateTitle callback can refer to Localizations via its context
// parameter.
builder: (BuildContext context) {
final String title = widget.onGenerateTitle(context);
assert(title != null, 'onGenerateTitle must return a non-null String');
return Title(
title: title,
color: widget.color,
child: result,
);
},
);
} else {
title = Title(
title: widget.title,
color: widget.color,
child: result,
);
}
final Locale appLocale = widget.locale != null
? _resolveLocales(<Locale>[widget.locale], widget.supportedLocales)
: _locale;
assert(_debugCheckLocalizations(appLocale));
return Shortcuts(
shortcuts: _keyMap,
child: Actions(
actions: _actionMap,
child: DefaultFocusTraversal(
policy: ReadingOrderTraversalPolicy(),
child: _MediaQueryFromWindow(
child: Localizations(
locale: appLocale,
delegates: _localizationsDelegates.toList(),
child: title,
),
),
),
),
);
}
MaterialApp:builder
在MaterialApp中提供了一个参数builder可以通过控制整个App的显示区域,可以指定主要的显示区域和屏幕边界的的间隔,可以在外围添加一些控件类型
return MaterialApp(
title: 'Flutter Demo',
color: Colors.red,
builder: (BuildContext context, Widget navigator) {
return Container(
color: Colors.green,
child: Row(
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 20, right: 20),
child: Icon(
Icons.verified_user,
size: 50,
),
),
Container(
margin: EdgeInsets.only(left: 20, right: 20),
child: Icon(
Icons.group,
size: 50,
),
),
Container(
margin: EdgeInsets.only(left: 20, right: 20),
child: Icon(
Icons.language,
size: 50,
),
)
],
),
Expanded(
flex: 1,
child: Container(
child: navigator,
))
],
),
);
},
theme: ThemeData(
primarySwatch: Colors.blue,
),
// debugShowMaterialGrid: true,
// debugShowCheckedModeBanner: true,
// showSemanticsDebugger: true,
// showPerformanceOverlay: true,
home: MyHomePage(title: 'Flutter Demo Home Page'),
);

小结
先对用户侧的开发接口的功能有一个概括性的认识,先把分析框架打起来,有哪些部分组成?这些控件控制那个功能点?是怎么实现的?
先搞懂框架和设置框架要解决的问题点,在处理细节的东西,和开车是一个道理,到一个陌生的地方,先把导航调好,在注意红路灯和行人,关注在自己要走的路线上的事物,才能够把整个流程和框架走通,避免在一个细小的知识点花费太多的实际,理解了思想之后,我们在一起学习每个组件是怎么实现的,在使用的时候忘记了可以有明确的思路去查找细节点。(个人的学习方法,可以相互讨论提高学习效率,学习就是去改变自己的认知,有产出,而不是去学习这个过程)