前言
在上篇中学习了 Dart 语言的基础知识,接着我们来要来学习下 Flutter 的 UI 是如何布局的。在我们搭建环境的时候,我们创建了一个入门的 demo ,下面我们从这个 demo 学起。
入门
下面把 demo 生成的 main.dart 简化来学习:
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(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: const Text('Welcome to Flutter'),
),
body: const Center(
child: Text('Hello World'),
),
),
);
}
}
和 Android 里面的 xml 布局不同, Flutter 采用的是声明构建 UI 方式来布局页面的。Android 里面所有控件都是 View,而这里所有控件都是 Widget,只不过 View 和 Widget 有一些不同,View 有自己的生命周期,可以通过 invalidate 方法来进行重绘,也可以持有 View 做些 View 相关的操作,而 Widget 不同,它就是一个申明式对象,通过多个 Widget 来构建成一个 Widget 树,它的状态不可变,一旦状态改变,生命周期即结束了,所以它比 View 更轻量。
理解了 Widget 概念后,再回到上面程序里面就好理解了,程序里面的 MyApp、MaterialApp、Scaffold、AppBar、Center、Text 都是 Widget。继续跟着这些 Widget 的源码会发现,她们又分为 StatelessWidget、StatefulWidget、RenderObjectWidget。比如 MyApp,Text 是 StatelessWidget,MaterialApp,Scaffold,AppBar 是 StatefulWidget,Center 是 RenderObjectWidget,这三个 Widget 有什么区别呢?
StatelessWidget & StatefulWidget & RenderObjectWidget
为什么 Flutter 要归类这几种 Widget 呢?在上面有提到过,Widget 状态改变生命周期就结束,那我们怎么持有 Widget 来更新 Widget 操作呢,我们没法像 Android 里面的 View 一样直接调用 View 方法来操作,所以需要一个状态管理的 Widget 来更新操作,这样 StatefulWidget 就出现了,与之对应的就是 StatelessWidget,它包括一些不跟着交互而改变状态的 Widget。StatefulWidget 和 StatelessWidget 的区别就是 StatefulWidget 有一个跨帧存储和恢复状态数据的 State 对象。也就是 Widget 跟着交互会改变状态的用 StatefulWidget,不会改变的用 StatelessWidget。而 RenderObjectWidget 仅仅是渲染对象,它不包括尺寸的属性,比如这里的 Center,还有 Row、Column、Flex 等等也都是。那么怎么使用它们呢?
- StatelessWidget
import 'package:flutter/material.dart';
void main() {
runApp(const NewsApp());
}
class NewsApp extends StatelessWidget {
const NewsApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Text('News App');
}
}
跑起来,会发现报错了
也就是 在跟布局里面的 build 返回的 Widget 必须是一个带方向性的部件,比如 MaterialApp 或者 WidgetsApp,它两有啥区别呢,这个后面再来看。这里先用 MaterialApp :
class NewsApp extends StatelessWidget {
const NewsApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "News App",
theme: ThemeData(
primaryColor: Colors.white,
),
home: const Text('home page'),
);
}
}
跑起来虽然页面很丑,但 StatelessWidget 的使用能提现出来。注意这里只是根布局约束了必须是返回 MaterialApp 或者 WidgetsApp。其他地方 StatelessWidget 的 build 可以返回任何 Widget。比如上面的 home 我们配置一个 StatelessWidget
home: const MyHome(),
class MyHome extends StatelessWidget {
const MyHome({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Text('home page');
}
}
这样也是可以的。跑起来效果这里就不展示了,挺简单的
- StatefulWidget
接着来看看 StatefulWidget 的使用
home: const _MyHome(title: 'home title'),
class _MyHome extends StatefulWidget {
const _MyHome({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<StatefulWidget> createState() {
return _MyHomeState();
}
}
class _MyHomeState extends State<_MyHome>{
@override
Widget build(BuildContext context) {
return Text(widget.title);
}
}
StatefulWidget 需要结合 State 来使用,它增加了状态的管理,比如这里的 title 属性。
- RenderObjectWidget
关于 RenderObjectWidget 相关的,我们直接使用相关 Widget 就好了,比如使用 Center
class _MyHomeState extends State<_MyHome>{
@override
Widget build(BuildContext context) {
return Center(
child: Text(widget.title),
);
}
}
Widget
学完了上面的 StatelessWidget、StatefulWidget 和 RenderObjectWidget 之后,也算入门了,接下来的就是使用各种 Widget 以及学习它们的属性了
- MaterialApp、CupertinoApp、WidgetsApp 使用
在上面我们提到,App 的根 Widget 必须是这三个其中之一,那么它们有啥区别呢?当我们想要使用 md 风格的 App 时候,我们使用 MaterialApp,当我们想用 IOS 风格的 App 则使用 CupertinoApp,如果要自定义主题那我们使用 WidgetsApp。那么它们怎么使用呢?在上面我们使用了 MaterialApp ,它结合 Scaffold 和 AppBar 来使用。上面 _MyHomeState 改成下面:
class _MyHomeState extends State<_MyHome>{
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title),),
body: Center(
child: Text(widget.title),
),
);
}
}
- CupertinoApp
IOS 风格的用法,它包括按钮,选择器等等都是遵循 IOS 的风格
class NewsApp extends StatelessWidget {
const NewsApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const CupertinoApp(
title: "News App",
home: CupertinoStoreHomePage(),
);
}
}
class CupertinoStoreHomePage extends StatelessWidget {
const CupertinoStoreHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Cupertino Store'),
),
child: Container(),
);
}
}
- WidgetsApp
WidgetsApp 自定义自己的风格,它有一些比 MaterialApp 和 CupertinoApp 多了一些属性,比如:
class NewsApp extends StatelessWidget {
const NewsApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return WidgetsApp(
//应用文本默认样式
textStyle: const TextStyle(
color: Color(0x00000000),
fontSize: 18,
fontWeight: FontWeight.normal,
),
debugShowWidgetInspector: true,
color: Colors.blue,
//选择按钮生成器
inspectorSelectButtonBuilder: (BuildContext context, VoidCallback onPressed) {
return FloatingActionButton(
child: const Icon(Icons.search),
onPressed: onPressed,
mini: true,
);
},
// (生成路由的回调函数,当导航的命名路由的时候,会使用这个来生成界面),如果在routes中没有找到路由,则使用onGenerateRoute回调建立界面
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute<void>(
settings: settings,
builder: (BuildContext context) =>
Scaffold(body: Center(child: Text(settings.toString()))),
);
}
);
}
}
- MaterialApp 和 CupertinoApp 属性
这两个需要设置的属性是一致的:
- navigatorKey
全局导航键,在整个应用程序中唯一的标识元素,生命周期跟着整个应用。有全局键的 Widget 要自己维护子 Widget 树,当 Widget 移动它一个新位置的时候,那么子 Widget 也要移动到对应的位置,同时原来的位置进行删除。所以使用全局键是很耗性能的,它会让所有依赖的子 Widget 都进行重建。非必要情况不使用它,一般建议我们用 Key,ValueKey,ObjectKey,UniqueKey。如果指定了 navigatorKey,那么这个 Widget 将不再需要通过 Navigator.of 来获取了,直接通过 GlobalKey.currentState 来获取了。使用如下
navigatorKey: GlobalKey<AppNavigatorState>()
class AppNavigatorState extends NavigatorState {
@override
void initState() {
super.initState();
}
}
继承 NavigatorState,通过重写方法的方式维护自己的 Navigator 状态。
- scaffoldMessengerKey
也是一个全局键,为创建 ScaffoldMessenger 使用的 key,如果我们指定了 scaffoldMessengerKey,那么这个 Widget 将不再需要通过 ScaffoldMessenger.of 来获取了,直接通过 GlobalKey.currentState 来获取了,使用类似 navigatorKey,它状态对应 ScaffoldMessengerState。
scaffoldMessengerKey: GlobalKey<AppScaffoldMessengerState>(),
class AppScaffoldMessengerState extends ScaffoldMessengerState {
@override
void initState() {
super.initState();
}
}
- home
app 页面展示内容,也就是我们页面布局开始的地方,使用方法看页面具体布局,返回 Widget 就行了,如上面提到过的:
home: const _MyHome(title: 'home title'),
class _MyHome extends StatefulWidget {
const _MyHome({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<StatefulWidget> createState() {
return _MyHomeState();
}
}
class _MyHomeState extends State<_MyHome> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: GestureDetector(
onTap: (){Navigator.of(context).pushNamed(Details.routeName);},
child: Text(widget.title),
),
),
);
}
}
- routes
App 页面路由名称映射集合,当我们跳转的时候使用我们映射的名称就好了,方面页面修改了,修改一个地方就好了,使用如下:
routes:{
Details.routeName: (context) => const Details(),
}
class Details extends StatefulWidget {
static String routeName = "/details";
@override
_DetailsState createState() => new _DetailsState();
}
class _DetailsState extends State<Details> {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("详情"),
),
body: Center(
child: Container(
color: Colors.blueAccent,
child: Text(
'第二页',
style: TextStyle(fontSize: 32),
),
),
),
);
}
}
那么我们跳转就可以这样,在 home 使用中有使用到
Navigator.of(context).pushNamed(Details.routeName);
- initialRoute
启用应用后需要自动跳转的第二个页面的路由,使用就是指定一个页面路由:
initialRoute: Details.routeName,
routes: {
Details.routeName: (context) => const Details(),
});
- onGenerateRoute
生成路由配置,当跳转的页面在 routes 属性未配置或者找不到的时候,则会调用该方法创建页面,反之,则不调用。需要配置一个方法:
typedef RouteFactory = Route<dynamic> Function(RouteSettings settings);
所以使用如下:
onGenerateRoute: (RouteSettings setting) {
return PageRouteBuilder(
pageBuilder: (BuildContext context, Animation animation,
Animation secondaryAnimation) {
//生成的页面
return const Details();
},
);
},
- onGenerateInitialRoutes
生成启动应用时的路由列表,结合 initialRouteName 一起使用,根据 initialRouteName 的值生成不同的路由列表。它有个默认值 Navigator.defaultGenerateInitialRoutes。
- onUnknownRoute
优先级最低,当跳转的页面在 routes 属性未配置或者找不到并且 onGenerateRoute 返回 null 的时候,则会调用该方法创建页面,反之,则不调用。使用方法和 onGenerateRoute 属性一样,通常这里用来做一些类似 404 页面。
onUnknownRoute: (RouteSettings setting) {
//当 onGenerateRoute 返回 null 的时候调用,通常处理一些未找到的页面
return new PageRouteBuilder(
pageBuilder: (BuildContext context, Animation animation,
Animation secondaryAnimation) {
//这里为返回的Widget
return Text('404');
},
);
},
- navigatorObservers
路由监听器,监听跳转页面的时候的过程,使用如下:
navigatorObservers:<NavigatorObserver>[
new NavigatorObserver(),
],
class HomeNavObserver extends NavigatorObserver{
@override
void didPush(Route route, Route previousRoute) {
// TODO: implement didPush
super.didPush(route, previousRoute);
}
@override
void didPop(Route route, Route previousRoute) {
// TODO: implement didPop
super.didPop(route, previousRoute);
}
@override
void didRemove(Route route, Route previousRoute) {
// TODO: implement didRemove
super.didRemove(route, previousRoute);
}
@override
void didReplace({Route newRoute, Route oldRoute}) {
// TODO: implement didReplace
super.didReplace(newRoute, oldRoute);
}
@override
void didStartUserGesture(Route route, Route previousRoute) {
// TODO: implement didStartUserGesture
super.didStartUserGesture(route, previousRoute);
}
@override
void didStopUserGesture() {
// TODO: implement didStopUserGesture
super.didStopUserGesture();
}
}
- builder
加载页面前做一些隐性设置,如加载处理,国际化语言设置,字体大小设置,横竖屏方向设置等等,对应的方法为
typedef TransitionBuilder = Widget Function(BuildContext context, Widget child);
如加载页面前做个加载框
builder: (BuildContext context, Widget child) {
return Stack(
alignment: Alignment.center,
children: <Widget>[
child,
const CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.red),
),
],
);
},
执行效果为:
- title
任务管理器的标题。
title:"任务管理器的标题",
- onGenerateTitle
任务管理器的标题 。 和 title 属性一样,不同的是可以通过 context 来获取本地资源。设置的优先级高于 title 。
onGenerateTitle:(BuildContext context){
return MaterialLocalizations.of(context)....;
},
- color
设置 App 主题色,比 theme 的 primaryColor 优先级更高,源码如下
final Color materialColor = widget.color ?? widget.theme?.primaryColor ?? Colors.blue;
使用颜色值就好了
color: Colors.white,
- theme
设置应用的主题
theme: ThemeData(
//设置页面背景颜色白天黑夜模式
brightness: Brightness.light,
//MaterialColor 多种颜色混合的主题色
primarySwatch: Colors.cyan,
//Color,主题色
primaryColor: Colors.cyan,
//设置文字图标等白天黑夜模式
primaryColorBrightness: Brightness.light,
//白天模式颜色
primaryColorLight: Colors.white,
//黑夜模式颜色
primaryColorDark: Colors.black,
//如 FloatingActionButton 的背景颜色
accentColor: Colors.green,
//FloatingActionButton 白天黑夜模式
accentColorBrightness: Brightness.dark,
//canvas 绘制的地方的颜色
//canvasColor: Colors.black,
//scaffold 控件的默认背景颜色
scaffoldBackgroundColor: Colors.white,
//BottomAppBar 控件的默认背景颜色
bottomAppBarColor: Colors.blue,
//Card 控件的默认背景颜色
cardColor: Colors.blue,
// 分割线默认颜色,如 ListTiles 的分割线
dividerColor: Colors.blue,
//高亮默认颜色,如 FloatingActionButton 按下的颜色
//highlightColor: Colors.black,
//水波纹默认颜色,如 FloatingActionButton 控件点击的水波纹效果
splashColor: Colors.red,
//水波纹的自定义处理
splashFactory: new HandlerFactory(),
//如文本选中一行的默认颜色(暂不知道用在哪里)
selectedRowColor: Colors.white,
//未选中控件并且有点击事件(如 Checkbox 控件)的默认颜色
unselectedWidgetColor: Colors.black,
//禁用不可点击按钮或者复选框的默认颜色
disabledColor:Colors.red,
//button 并且 onPress 不为 null 的默认背景颜色
buttonColor:Colors.grey,
//button 并且 onPress 不为 null 的样式
buttonTheme:new ButtonThemeData(
),
//PaginatedDataTable 控件 在选项选中的时候 头部的默认背景颜色
secondaryHeaderColor:Colors.grey,
// TextField 文本被选中的颜色。
textSelectionColor:Colors.grey,
//TextField 光标的颜色
cursorColor:Colors.grey,
//选定文本句柄的默认颜色。
textSelectionHandleColor:Colors.red,
//如 进度条 ProgressIndicator 控件的默认背景颜色。
backgroundColor:Colors.grey,
//Dialog 控件的默认背景色
dialogBackgroundColor:Colors.grey,
//TabBar 控件指示器的默认颜色。
indicatorColor:Colors.grey,
//TextField 提示文本或占位符文本的默认文本颜色。
hintColor:Colors.grey,
//TextField 输入验证错误提示的默认文本颜色。
errorColor:Colors.red,
//Switch,Radio和Checkbox 等控件激活状态的默认颜色。
toggleableActiveColor:Colors.grey,
//字体路径
fontFamily:"lib/fonts/...",
//页面文本默认样式
textTheme:TextTheme(),
//状态栏或者标题栏的文本默认样式,对应 primaryColor。
primaryTextTheme:TextTheme(),
//如 FloatingActionButton 的样式,对应 primaryColor。
accentTextTheme:TextTheme(),
//对应 InputDecorator、TextField和TextFormField 属性InputDecoration。
inputDecorationTheme:new InputDecorationTheme(),
//图标的默认样式。
iconTheme:new IconThemeData(),
//标题栏或者状态栏的默认图标样式。
primaryIconTheme:new IconThemeData(),
//如 FloatingActionButton 的默认图标样式。
accentIconTheme:new IconThemeData(),
//Slider 系列控件的默认样式。
sliderTheme:new SliderThemeData(),
//tabBar 控件的默认样式。
tabBarTheme:new TabBarTheme(),
//card 控件的默认样式
cardTheme:new CardTheme(),
//Chip 控件的默认样式
chipTheme:new ChipThemeData(),
//widget 适配的平台。
platform:TargetPlatform.android,
//md 控件的点击范围。
materialTapTargetSize:new MaterialTapTargetSize(),
//对应平台的默认 MaterialPageRoute 转换。
pageTransitionsTheme:new PageTransitionsTheme(),
//AppBar 控件 默认样式
appBarTheme:new AppBarTheme(),
//BottomAppBarTheme 控件的默认样式
bottomAppBarTheme:new BottomAppBarTheme(),
//一组13种颜色,配置大多数组件的颜色属性。
//colorScheme:,
//弹窗默认样式
dialogTheme:DialogTheme(),
//floatingActionButton 的默认样式
floatingActionButtonTheme:new FloatingActionButtonThemeData(),
//TextTheme、primaryTextTheme和accentTextTheme的颜色文本主题值。
typography:new Typography(),
//Cupertino 控件默认样式
//cupertinoOverrideTheme:,
),
- darkTheme
黑夜模式的主题样式,设置同 theme 属性
- highContrastTheme
系统要求“高对比度”时使用的主题,针对 IOS 设备可以设置而设置的主题
- highContrastDarkTheme
系统要求“高对比度”时使用的深色主题,针对 IOS 设备可以设置而设置的主题
- themeMode
主题模式,跟随系统,白天,深色模式等等。
this.themeMode = ThemeMode.system,
- local
设置语言,null 为跟随系统
locale:Locale('zh','cn'),
_languageCode
static const Map<String, String> _deprecatedLanguageSubtagMap = const <String, String>{
'in': 'id', // Indonesian; deprecated 1989-01-01
'iw': 'he', // Hebrew; deprecated 1989-01-01
'ji': 'yi', // Yiddish; deprecated 1989-01-01
'jw': 'jv', // Javanese; deprecated 2001-08-13
'mo': 'ro', // Moldavian, Moldovan; deprecated 2008-11-22
'aam': 'aas', // Aramanik; deprecated 2015-02-12
'adp': 'dz', // Adap; deprecated 2015-02-12
'aue': 'ktz', // ǂKxʼauǁʼein; deprecated 2015-02-12
'ayx': 'nun', // Ayi (China); deprecated 2011-08-16
'bgm': 'bcg', // Baga Mboteni; deprecated 2016-05-30
'bjd': 'drl', // Bandjigali; deprecated 2012-08-12
'ccq': 'rki', // Chaungtha; deprecated 2012-08-12
'cjr': 'mom', // Chorotega; deprecated 2010-03-11
'cka': 'cmr', // Khumi Awa Chin; deprecated 2012-08-12
'cmk': 'xch', // Chimakum; deprecated 2010-03-11
'coy': 'pij', // Coyaima; deprecated 2016-05-30
'cqu': 'quh', // Chilean Quechua; deprecated 2016-05-30
'drh': 'khk', // Darkhat; deprecated 2010-03-11
'drw': 'prs', // Darwazi; deprecated 2010-03-11
'gav': 'dev', // Gabutamon; deprecated 2010-03-11
'gfx': 'vaj', // Mangetti Dune ǃXung; deprecated 2015-02-12
'ggn': 'gvr', // Eastern Gurung; deprecated 2016-05-30
'gti': 'nyc', // Gbati-ri; deprecated 2015-02-12
'guv': 'duz', // Gey; deprecated 2016-05-30
'hrr': 'jal', // Horuru; deprecated 2012-08-12
'ibi': 'opa', // Ibilo; deprecated 2012-08-12
'ilw': 'gal', // Talur; deprecated 2013-09-10
'jeg': 'oyb', // Jeng; deprecated 2017-02-23
'kgc': 'tdf', // Kasseng; deprecated 2016-05-30
'kgh': 'kml', // Upper Tanudan Kalinga; deprecated 2012-08-12
'koj': 'kwv', // Sara Dunjo; deprecated 2015-02-12
'krm': 'bmf', // Krim; deprecated 2017-02-23
'ktr': 'dtp', // Kota Marudu Tinagas; deprecated 2016-05-30
'kvs': 'gdj', // Kunggara; deprecated 2016-05-30
'kwq': 'yam', // Kwak; deprecated 2015-02-12
'kxe': 'tvd', // Kakihum; deprecated 2015-02-12
'kzj': 'dtp', // Coastal Kadazan; deprecated 2016-05-30
'kzt': 'dtp', // Tambunan Dusun; deprecated 2016-05-30
'lii': 'raq', // Lingkhim; deprecated 2015-02-12
'lmm': 'rmx', // Lamam; deprecated 2014-02-28
'meg': 'cir', // Mea; deprecated 2013-09-10
'mst': 'mry', // Cataelano Mandaya; deprecated 2010-03-11
'mwj': 'vaj', // Maligo; deprecated 2015-02-12
'myt': 'mry', // Sangab Mandaya; deprecated 2010-03-11
'nad': 'xny', // Nijadali; deprecated 2016-05-30
'ncp': 'kdz', // Ndaktup; deprecated 2018-03-08
'nnx': 'ngv', // Ngong; deprecated 2015-02-12
'nts': 'pij', // Natagaimas; deprecated 2016-05-30
'oun': 'vaj', // ǃOǃung; deprecated 2015-02-12
'pcr': 'adx', // Panang; deprecated 2013-09-10
'pmc': 'huw', // Palumata; deprecated 2016-05-30
'pmu': 'phr', // Mirpur Panjabi; deprecated 2015-02-12
'ppa': 'bfy', // Pao; deprecated 2016-05-30
'ppr': 'lcq', // Piru; deprecated 2013-09-10
'pry': 'prt', // Pray 3; deprecated 2016-05-30
'puz': 'pub', // Purum Naga; deprecated 2014-02-28
'sca': 'hle', // Sansu; deprecated 2012-08-12
'skk': 'oyb', // Sok; deprecated 2017-02-23
'tdu': 'dtp', // Tempasuk Dusun; deprecated 2016-05-30
'thc': 'tpo', // Tai Hang Tong; deprecated 2016-05-30
'thx': 'oyb', // The; deprecated 2015-02-12
'tie': 'ras', // Tingal; deprecated 2011-08-16
'tkk': 'twm', // Takpa; deprecated 2011-08-16
'tlw': 'weo', // South Wemale; deprecated 2012-08-12
'tmp': 'tyj', // Tai Mène; deprecated 2016-05-30
'tne': 'kak', // Tinoc Kallahan; deprecated 2016-05-30
'tnf': 'prs', // Tangshewi; deprecated 2010-03-11
'tsf': 'taj', // Southwestern Tamang; deprecated 2015-02-12
'uok': 'ema', // Uokha; deprecated 2015-02-12
'xba': 'cax', // Kamba (Brazil); deprecated 2016-05-30
'xia': 'acn', // Xiandao; deprecated 2013-09-10
'xkh': 'waw', // Karahawyana; deprecated 2016-05-30
'xsj': 'suj', // Subi; deprecated 2015-02-12
'ybd': 'rki', // Yangbye; deprecated 2012-08-12
'yma': 'lrr', // Yamphe; deprecated 2012-08-12
'ymt': 'mtm', // Mator-Taygi-Karagas; deprecated 2015-02-12
'yos': 'zom', // Yos; deprecated 2013-09-10
'yuu': 'yug', // Yugh; deprecated 2014-02-28
};
_countryCode
static const Map<String, String> _deprecatedRegionSubtagMap = const <String, String>{
'BU': 'MM', // Burma; deprecated 1989-12-05
'DD': 'DE', // German Democratic Republic; deprecated 1990-10-30
'FX': 'FR', // Metropolitan France; deprecated 1997-07-14
'TP': 'TL', // East Timor; deprecated 2002-05-20
'YD': 'YE', // Democratic Yemen; deprecated 1990-08-14
'ZR': 'CD', // Zaire; deprecated 1997-07-14
};
- localizationsDelegates
国际化代理类,国际化 App 的时候会用到,它主要作用是为构造相应类的特定于语言环境的实例。flutter 默认提供了两种代理 GlobalMaterialLocalizations.delegate 和 GlobalWidgetsLocalizations.delegate, GlobalMaterialLocalizations.delegate 是为 Material Components 库提供了本地化的字符串和其他值,GlobalWidgetsLocalizations.delegate 是定义 widget 默认的文本方向,从左到右或从右到左, GlobalCupertinoLocalizations.delegate 为 Cupertino(ios风格)库提供了本地化的字符串和其他值。
localizationsDelegates: [
// ... app-specific localization delegate[s] here
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
]
- localeListResolutionCallback,localeResolutionCallback
对语言变化的监听,比如切换系统语言等,localeResolutionCallback 返回的第一个参数是当前语言的Locale,而 localeListResolutionCallback 返回当前手机支持的语言集合。
- supportedLocales
App 支持的多语言,默认设置为美国英语
supportedLocales: [
const Locale('en'), // English
const Locale('he'), // Hebrew
const Locale('zh'), // Chinese
// ... other locales the app supports
],
- debugShowMaterialGrid
Material Design 调试的基线网格,有助于布局时 widget 对齐,设置为 true 效果如下
- showPerformanceOverlay
是否显示 GPU 和 UI 刷新率监控图,性能调试作用
- checkerboardRasterCacheImages
是否显示光栅缓存图像的棋盘格
- checkerboardOffscreenLayers
是否显示层级图
- showSemanticsDebugger
是否显示布局边界
- debugShowCheckedModeBanner
是否显示右上角的 debug 标签
- shortcuts
刘海屏兼容设置
Widget build(BuildContext context) {
return WidgetsApp(
shortcuts: <ShortcutActivator, Intent>{
... WidgetsApp.defaultShortcuts,
const SingleActivator(LogicalKeyboardKey.select): const ActivateIntent(),
},
color: const Color(0xFFFF0000),
builder: (BuildContext context, Widget? child) {
return const Placeholder();
},
);
}
- actions
添加 App Actions 小部件,App 桌面快捷启动方式的功能
Widget build(BuildContext context) {
return WidgetsApp(
actions: <Type, Action<Intent>>{
... WidgetsApp.defaultActions,
ActivateAction: CallbackAction<Intent>(
onInvoke: (Intent intent) {
// Do something here...
return null;
},
),
},
color: const Color(0xFFFF0000),
builder: (BuildContext context, Widget? child) {
return const Placeholder();
},
);
}
- restorationScopeId
恢复状态时候需要使用到,比如如果 ListView 设置了 restorationId ,当我们滑到某个地方,按返回键退出,如果需要再次进来回到这个滑动的位置的地方。要让它生效就需要设置 MaterialApp 的 restorationScopeId,它指定的是一个随意的字符串。
- scrollBehavior
配置 ScrollBehavior 控件的行为,默认为 MaterialScrollBehavior。
- useInheritedMediaQuery
MediaQuery 用于查询解析给定数据的媒体信息(例如,window宽高/横竖屏/像素密度比 等信息)官方提供这个组件让开发者可以获取想要的数据。它主要用于不同尺寸大小设备的适配。在后面学习 MediaQuery 的使用的时候应该会用到,暂时不知道有什么作用。
- WidgetsApp 属性
这里说说和上面 MaterialApp 不一样的属性
- pageRouteBuilder
设置页面跳转的转场,也可以自定义页面跳转时的动画。例如这里使用 material 的转场效果:
pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) {
return MaterialPageRoute(builder: builder, settings: settings);
}
- textStyle
它主要是为应用中的文本使用的默认样式
textStyle: const TextStyle(
color: Color(0x00000000),
fontSize: 18,
fontWeight: FontWeight.normal,
),
- debugShowWidgetInspector
调试 Widget 布局的工具,对应 Flutter widget inspector 工具,它可以可视化和查看 Widget 树结构
- inspectorSelectButtonBuilder
创建 WidgetInspector。 用于视图模式和检查模式之间切换的小部件。 这使得 MaterialApp 可以使用材质按钮来切换检查器的选择模式,而不需要 WidgetInspector 依赖于 material 包。
inspectorSelectButtonBuilder:
(BuildContext context, VoidCallback onPressed) {
return FloatingActionButton(
child: const Icon(Icons.search),
onPressed: onPressed,
mini: true,
);
}
好了,这篇就先到这里,后面继续学习。