文章目录
一、Flutter-APP结构简介
- Flutter是Dart语言的移动应用框架。
- 以Dart程序的
main()
函数为入口,通过runApp()
函数进入到Flutter框架的应用,实现界面的展示和交互,如果不调用runApp()
函数,那么所执行的就是一个Dart控制台应用,屏幕上什么也不会显示。 - Flutter-APP不使用原生控件,而是提供了一组自己的widget(包括material Design和Cupertino(iOS风格的widget)),由Flutter framework和Engine引擎管理和渲染,从而得到更好的性能。
- Flutter中一切皆为widget,无论是结构,布局,或者主题等等。
- 而widget是不可变的,仅支持一帧,并且在每一帧上不会直接更新,要更新就必须使用Widget的状态。
- 起手姿势:基本是固定模式
runApp()
进入,函数接受给定的widget并使其成为widget树的根;- 无状态
StatelessWidget
中设置MaterialApp
确立APP设计风格,主题; - 通过有状态
StatefulWidget
的State
,来更改界面状态和响应用户操作; - 通过
Scaffold
来进行布局。
//runApp进入程序
void main() => runApp(new MyApp());
//无状态widget
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//MaterialAPP可设置主题和起始页
return new MaterialApp(
home: new MyHomePage(),//此属性页面也全屏
);
}
}
class MyHomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new MyHomePageState();
}
}
class MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return new Scaffold(//实现了基本的布局结构
appBar: new AppBar(),
body: 需求widget,
);
}
}
二、widget
- Widget采用现代响应式框架构建,中心思想是用widget构建你的UI。
- Widget描述了他们的视图在给定其当前配置和状态时的样子,包括视觉、结构、平台和交互等。
- 当widget的状态发生变化时,widget会重新构建UI,Flutter会对比前后变化的不同,以确定底层渲染树从一个状态转换到下一个状态所需的最小更改。
- Widget本身没有可变状态(所有的字段必须是final)。
- widget内容:
- 一个结构元素(如按钮或菜单)
- 一个文本样式元素(如字体或颜色方案)
- 布局的一个方面(如填充)
1、StatelessWidget无状态widget
-
这是一个不可改变状态的部件,它通过构建一个更具体的描述用户界面的的其他小部件集群来描述用户界面的一部分,构建过程通过递归进行,知道用户界面的描述完全完成。
-
当用户界面不依赖对象本身的配置信息以外的任何内容以及widget自身变化而变化时,无状态widget是很好用的,但对于动态更改的组合,还是要使用有状态的widget。
-
此类之所以无状态是因为,widget本身状态就是不可变的,仅支持一帧,在创建时,内部调用
createElement()
创建一个无状态的元素。
StatelessWidget:
abstract class StatelessWidget extends Widget {
const StatelessWidget({ Key key }) : super(key: key);
/// Creates a [StatelessElement] to manage this widget's location in the tree.
//系统调用。创建一个无状态元素来管理这个这个widget在widget树中的位置
@override
StatelessElement createElement() => StatelessElement(this);
//子类复写此方法传入一个widget
@protected
Widget build(BuildContext context);
}
StatelessElement(this);
class StatelessElement extends ComponentElement {
/// 直接创建一个给定的widget元素,之后渲染到树上
StatelessElement(StatelessWidget widget) : super(widget);
}
2、StatefulWidget有状态widget
- 这是一个可改变状态的部件。它通过构建一个更具体的描述用户界面的的其他小部件集群来描述用户界面的一部分,构建过程通过递归进行,知道用户界面的描述完全完成。
- 在构建widget时可以同步读取这个初始状态,而状态在widget生命周期中可能会发生改变,而widget实现者通过
State
的setstate()
方法确保状态在放改变时及时通知。 StatefulWidget
和SatelessWidget
一样本身是不可以改变状态的,之所以可以改变状态,是因为此类内部内置一个State
订阅对象,而State
对象是有生命周期的,并与widget绑定,来更改widget的状态。
StatefulWidget:
abstract class StatefulWidget extends Widget {
const StatefulWidget({ Key key }) : super(key: key);
/// Creates a [StatefulElement] to manage this widget's location in the tree.
///系统调用。创建一个有状态元素来管理这个这个widget在widget树中的位置
@override
StatefulElement createElement() => StatefulElement(this);
@protected
State createState();
}
StatefulElement(this);
class StatefulElement extends ComponentElement {
/// Creates an element that uses the given widget as its configuration.
StatefulElement(StatefulWidget widget)
//此时会创建一个State对象来和widget绑定,通过State的生命周期来监听widget的状态
: _state = widget.createState(), super(widget) {..各种断言..}
@override
Widget build() => state.build(this);
/// The [State] instance associated with this location in the tree.
/// 与树中此位置的widget关联,一一对应
State<StatefulWidget> get state => _state;
State<StatefulWidget> _state;
.....
}
三、MaterialApp
1、概述
-
它封装了许多应用程序通常需要的小部件,并添加了特定的设计功能,如主题,网格像素。
-
为了继承主题数据,widget需要位于
MaterialApp
内才能正常显示,通常使用MaterialApp
来运行应用。 -
参数
home
、routes
、onGenerateRoute
或builder
中至少一个是非空的,这里指定的路由是应用程序启动的路由。 -
如果只指定
routes
参数,那么集合中必须含有一个key为:Navigator.defaultRouteName
或'/'
的路由,因为这俩key表示程序启动的路由根路径。 -
home
的路由路径为:'/'
,等价于Navigator.defaultRouteName
。 -
所以
home
参数和routes
参数内key为的Navigator.defaultRouteName
或'/'
的路由不能同时出现,否则会冲突。
2、参数介绍
字段 | 类型 | 作用 | |
---|---|---|---|
1 | navigatorKey | Globalkey | 导航键,全局唯一 |
2 | home | widget | 主页 |
3 | routes | Map<String,WidgetBuilder> | 路由表,内含各种widget |
4 | initialRoute | String | 初始路由 |
5 | onGenerateRoute | RouteFactory | 生成路由,当初始路由未找到时,展示此页面 |
6 | onUnknownRoute | RouteFactory | 未知路由 |
7 | navigatorObservers | List | 导航监听器 |
8 | builder | TransitionBuilder | widget的构建者 |
9 | title | String | 任务管理器中应用程序标题 |
10 | onGenerateTitle | GenerateAppTitle | 生成标题,每次在WidgetsApp构建时都会重新生成 |
11 | color | Color | 任务管理器中应用程序title颜色 |
12 | theme | ThemeData | 主题 |
13 | locale | Locale | app语言支持 |
14 | localizationsDelegates | Iterable | 多语言代理 |
15 | localeResolutionCallback | LocaleResolutionCallback | 区域分辨回调 |
16 | supportedLocales | Iterable | 支持的多语言 |
17 | debugShowMaterialGrid | bool | 调试显示材质网格,默认false |
18 | showPerformanceOverlay | bool | 打开性能监控,覆盖在屏幕最上面,默认false |
19 | checkerboardRasterCacheImages | bool | 打开栅格缓存图像的检查板。默认false |
20 | checkerboardOffscreenLayers | bool | 打开显示到屏幕外位图的图层的检查面板。默认false |
21 | showSemanticsDebugger | bool | 打开一个覆盖图,显示框架报告的可访问性信息,显示边框,默认false |
22 | debugShowCheckedModeBanner | bool | 右上角显示一个debug的图标,默认为true |
- 13到16参数为支持国际化设置;
- 17到22设置果如下:
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
//默认设置
debugShowMaterialGrid: false,//17
showPerformanceOverlay: false,//18
checkerboardRasterCacheImages: false,//19
checkerboardOffscreenLayers: false,//20
showSemanticsDebugger: false,//21
debugShowCheckedModeBanner: true,//22
);
}
效果如下:
3、常用参数详解
A、路由设置方面的参数
home
、routes
、initialRoute
、onGenerateRoute
、onUnknownRoute
和builder
:
-
builder
:- 和
home
的作用一样,首次打开的路由,但优先级最高,不能为null。 - 当
builder
设置后,initialRoute
设置也失效,即启动后只显示builder设置的路由界面。
使用:
new MaterialApp( builder: (BuildContext context, Widget child) { return new BuilderPage();//这是页面 }, )
- 和
-
home
:主页,- 路径为
'/'
,默认首先打开此路径的路由。 - 如果应用只有一个界面,使用 home 设置这个界面即可。
- 路径为
-
routes
:路由集合- 路由集合,里面以键值对的形式存放各个widget界面;
- 通过
Navigator.pushNamed(context, '/TwoPage');
调用。 - 当使用
Navigator.pushNamed
推送命名路由的时候,会在routes
中查找路由名字,然后使用对应的 WidgetBuilder 来构造一个带有页面切换动画的MaterialPageRoute。 - 如果所查找的路由在
routes
不存在,则会通过onGenerateRoute
来查找。 - 如果
home
不为null,routes
中不能包含key为Navigator.defaultRouteName
或'/'
的路由,否则会冲突。
使用:
new MaterialApp( routes: { ///这两个不能同时出现,而且home不为null,不可以设置这个 //'/': (BuildContext context) => new OnePage(), //Navigator.defaultRouteName:(BuildContext context) => new MyHomePage(), //key:value形式 '/TwoPage': (BuildContext context) => new TwoPage(), }, )
-
initialRoute
:初始路由- 显示的第一个路由,即进入程序时首先显示此widget界面。
- 根据参数,去
routes
集合中查找路由。 - 在显示此路由时,
home
或routes
中设置的Navigator.defaultRouteName
(或'/'
)路由,也会打开,并被此路由覆盖,点击返回可以推到home
路由 (home还是位于一级) 。
使用:
new MaterialApp( routes: { ///这两个不能同时出现,而且home不为null,不可以设置这个 // '/': (BuildContext context) => new OnePage(), // Navigator.defaultRouteName:(BuildContext context) => new MyHomePage(), //key:value形式 '/TwoPage': (BuildContext context) => new TwoPage(), }, initialRoute: '/TwoPage', )
效果:
-
onGenerateRoute
:生成路由- 当在
routes
中没有查找命名路由时,则会先调用此路由。 - 当
home
为null,并且routes
中也没有设置根路由Navigator.defaultRouteName
或'/'
时,则会将此路由设置的'/'
路径下,并打开此路由。 - 当
initialRoute
被设置,但未被找到时,也会回调此路由;
使用:
如果 1.未定义
'/'
路径下路由 和 2.设置initialRoute
的路由但未被找到,此时都将回调onGenerateRoute
的路由,此时将打开两次onGenerateRoute
的路由。new MaterialApp( //未设置'/'路径下的路由 routes: { '/TwoPage': (BuildContext context) => new TwoPage(), }, //此处的路由未定义,所以不会被找到 initialRoute: '/TwoPage1', //设置了此路由, onGenerateRoute: (RouteSettings setting) { return MaterialPageRoute(//此处返回一个`Route`类型的路由,系统提供了几个实现类 builder: (BuildContext context) { return GeneratePage(); }, ); }, )
效果:
- 当在
-
onUnknownRoute
:未知路由 当前几项设置均为找到时,则会回调此路由。
B、任务管理界面方面的参数
title
、onGenerateTitle
、color
title
:任务管理界面,显示的APP的标题,不同手机效果不同。onGenerateTitle
:当与title
同时设置时,此参数生效。color
:任务管理界面,APP的图标颜色。
C、导航监听器:navigatorObservers
- 此参数为导航监听器,监听导航对路由弹出,放入的操作;
- 监听器是个集合,可根据不同需求对路由做不同的设置;
- 集合中传入参数为实现
NavigatorObserver
接口的监听器。 - 可以通过
Navigator.of(context).pop();
关闭当前路由。
NavigatorObserver接口有如下函数:
class NavigatorObserver {
/// 进入路由
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) { }
/// 弹出路由
void didPop(Route<dynamic> route, Route<dynamic> previousRoute) { }
///删除路由
void didRemove(Route<dynamic> route, Route<dynamic> previousRoute) { }
/// 用新路由替换旧路由
void didReplace({ Route<dynamic> newRoute, Route<dynamic> oldRoute }) { }
/// 用户势控制路由
/// For example, this is called when an iOS back gesture starts, and is used
/// to disabled hero animations during such interactions.
void didStartUserGesture() { }
///用户手势不再控制路由,与上一个相配
void didStopUserGesture() { }
}
使用方式:
- 设置路由,首页设置。将路由放入
routes
集合统一管理,并注册导航监听器
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
//统一管理各个路由
routes: {
'/OnePage': (BuildContext context) => new OnePage(),
'/TwoPage': (BuildContext context) => new TwoPage(),
'/GeneratePage': (BuildContext context) => new GeneratePage(),
},
//注册路由监听器
navigatorObservers: [//这个监听器是个集合,可根据不同需求对路由做不同的设置
new MyNavigatorObserver(),
],
);
}
}
class MyHomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() => new MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('MaterialApp'),
),
body:Center(//这是个将子widget放入中间位置的widget
child: RaisedButton(//这是一个按钮
child: Text('skip one page'),//按钮上文字
onPressed: () {//点击事件
//路由跳转,使用pushName方法可以获得路由名字,此字符串为routes内的key
Navigator.pushNamed(context, '/OnePage');
},
),
),
);
}
}
- 各路由界面: one page为例;此处各个路由界面结构相似,显示当前路由的名字
class OnePage extends StatelessWidget {
@override
Widget build(BuildContext context) => new OnePageFull();
}
class OnePageFul extends StatefulWidget {
@override
_OnePageFulState createState() => _OnePageFulState();
}
class _OnePageFulState extends State<OnePageFul> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('one page'),
),
body: Center(
child: RaisedButton(
child: Text('skip two page'),
onPressed: () {
//以命名的名字跳转的方法
Navigator.pushNamed(context, '/TwoPage');
///另一种方法,在two page跳转到generate page时使用的,如:
//Navigator.of(context).push(
// new MaterialPageRoute(
// builder: (BuildContext context) => new GeneratePage(),
// ),
//);
},
),
),
);
}
}
导航监听器: 监听各个路由进入和弹出,如果路由没有放入routes
集合被命名,那么此路由的name为null。
class MyNavigatorObserver extends NavigatorObserver {
///route 当前路由
///previousRoute 先前活动的路由
///放入路由 即打开
@override
void didPush(Route route, Route previousRoute) {
print('----------push-----------');
print('当前活动的路由:${route.settings}');
print('先前活动的路由:${previousRoute?.settings}');
print('----------end-----------');
}
///弹出当前路由
@override
void didPop(Route route, Route previousRoute) {
print('----------pop-----------');
print('当前活动的路由:${route.settings}');
print('先前活动的路由:${previousRoute?.settings}');
print('----------end-----------');
}
}
界面操作示意图:
打印结果:
//首次进入,首页路由
----------push-----------
当前活动的路由:"/"
先前活动的路由:null
----------end-----------
//跳转到one page路由
----------push-----------
当前活动的路由:"/OnePage"
先前活动的路由:"/"
----------end-----------
//跳转到two page路由
----------push-----------
当前活动的路由:"/TwoPage"
先前活动的路由:"/OnePage"
----------end-----------
//跳转到generate page路由,此处路由未被命名,打印name为null
----------push-----------
当前活动的路由:"null"
先前活动的路由:"/TwoPage"
----------end-----------
//从generate page返回上一个路由two page
----------pop-----------
当前弹出的路由:"null"
先前活动的路由:"/TwoPage"
----------end-----------
//从two page返回上一个路由one page
----------pop-----------
当前弹出的路由:"/TwoPage"
先前活动的路由:"/OnePage"
----------end-----------
//从one page返回上一个home路由
----------pop-----------
当前弹出的路由:"/OnePage"
先前活动的路由:"/"
----------end-----------
D、主题theme
- 设置应用程序的主题和小部件的主题:如亮度、颜色、排版;
- 返回值为
ThemeDate
,提供了几种工厂构造方法如下:
factory ThemeData({
//..各种选填参数,一共45个..
//未设置会有默认值,然后将值付给raw构造
return ThemeData.raw(..各种必填非null参数..);
})
//常量构造方式,内部参数必不为null,而且必填
const ThemeData.raw({..各种必填非null参数..})
///一个默认淡蓝色主题
factory ThemeData.light() => ThemeData(brightness: Brightness.light);
///一个默认的黑色主题,带有蓝绿色调。
factory ThemeData.dark() => ThemeData(brightness: Brightness.dark);
//默认为淡蓝色主题:
factory ThemeData.fallback() => ThemeData.light();
///创建此主题的副本,但使用新值替换给定字段。
ThemeData copyWith({
//...
return ThemeData.raw(..各种必填非null参数..);
})
///将带有[baseTheme]基础主题和[localTextGeometry]文本主题合并生成的新主题,。
static ThemeData localize(ThemeData baseTheme, TextTheme localTextGeometry){}
///两个主题之间的线性插值。t取值为0.0到1.0,将两个主题的颜色从0.0到1.0调节
static ThemeData lerp(ThemeData a, ThemeData b, double t){}
参数描述:
太多了自己试吧!看着都头疼,大部分时候使用默认的,自定义时也只是很少的主体颜色。
名称 | 类型 | 作用 |
---|---|---|
brightness | Brightness | 描述整体主题的调度,包括按钮,画布,卡片和原色的亮度;默认为light。 |
primarySwatch | MaterialColor | 原色样本,供primaryColor使用;默认设置blue。 |
primaryColor | Color | 状态栏和标题栏bars的背景颜色;默认为primarySwatch的值。 |
primaryColorBrightness | Brightness | 状态栏和bars的亮度;默认根据背景颜色的阙值来设置light 或dark。 |
primaryColorLight | Color | 状态栏和bars颜色的较浅版本。 |
primaryColorDark | Color | 状态栏和bars颜色的较深版本。 |
accentColor | Color | 小部件的前景色(旋钮、文本、覆盖边缘效果等)。默认根据不同主题亮度显示不同。 |
accentColorBrightness | Brightness | 小部件的前景色亮度,用于确定文本的颜色和放在强调颜色之上的图标(如浮动按钮);默认根据颜色阙值设置。 |
canvasColor | Color | 画布颜色,即页面颜色;默认根据亮度设置 |
scaffoldBackgroundColor | Color | 应用程序内页面的背景颜色;默认为canvasColor颜色 |
bottomAppBarColor | Color | 底部bar的背景颜色;默认根据亮度设置,亮时为白色。 |
cardColor | Color | 使用卡片组件时,卡片的背景颜色;默认根据亮度设置,亮时为白色。 |
dividerColor | Color | 分割线的颜色,如Divider 、PopupMenuDivider 、ListTile 和 DataTable 之间的分割线。 |
highlightColor | Color | 选中后的颜色。 |
splashColor | Color | 触摸,点击后的颜色 |
splashFactory | InteractiveInkFeatureFactory | 定义触摸点击后的动画效果,类似水波纹。 |
selectedRowColor | Color | 用于突出显示选定行的颜色。 |
unselectedWidgetColor | Color | 用于处于非活动(但已启用)状态的小部件的颜色。如,未选中的复选框。通常与accentColor形成对比。 |
disabledColor | Color | 用于无效部件的颜色,无论其状态是如何 |
buttonColor | Color | 按钮的背景颜色;默认根据亮度设置。 |
buttonTheme | ButtonThemeData | 按钮的主题。包括背景颜色大小和文本主题 |
secondaryHeaderColor | Color | 未知 |
textSelectionColor | Color | 选中文本的颜色 |
cursorColor | Color | 文本输入框光标颜色 |
textSelectionHandleColor | Color | 调整当前选定的文本部分的句柄的颜色。光标下的句柄。 |
backgroundColor | Color | 与primaryColor形成对比,如进度条剩余部分颜色。 |
dialogBackgroundColor | Color | 对话框的背景颜色;默认根据亮度改变,light时为白色。 |
indicatorColor | Color | |
hintColor | Color | 文本或占位符文本的颜色,如输入框TextField失去焦点后的颜色。 |
errorColor | Color | 用于输入验证错误的颜色,例如在[TextField]字段中。 |
toggleableActiveColor | Color | 用于突出显示可切换小部件的活动状态的颜色,如[开关]、[收音机]和[复选框]。 |
fontFamily | String | 文本字体名称。 |
textTheme | TextTheme | 文本主题,与卡片和画布颜色形成对比。 |
primaryTextTheme | TextTheme | 文本主题,与primary颜色形成对比 |
accentTextTheme | TextTheme | 文本主题,与accent颜色形成对比 |
inputDecorationTheme | InputDecorationTheme | 输入框的主题 |
iconTheme | IconThemeData | 图标主题,与卡片和画布形成对比 |
primaryIconTheme | IconThemeData | 图标主题,与primary颜色形成对比。 |
accentIconTheme | IconThemeData | 图标主题,与accent颜色形成对比。 |
sliderTheme | SliderThemeData | [滑块]的颜色和形状的主题。这是从[SliderTheme.of]返回的值。 |
tabBarTheme | TabBarTheme | 自定义选项卡栏指示器的大小、形状和颜色的主题。 |
chipTheme | ChipThemeData | 渲染[Chip]所用的颜色和样式,这是从[ChipTheme.of]返回的值。 |
platform | TargetPlatform | 使小部件适应目标平台,默认为当前平台 |
materialTapTargetSize | MaterialTapTargetSize | 小部件的tap目标和布局的大小,默认为padded填充,48px*48px |
pageTransitionsTheme | PageTransitionsTheme |
四、Scaffold
1、概述
-
这个widget部件是一个实现了基本布局结构布局。
-
它提供了一个应用程序结构包括:
- AppBar:应用顶部水平标题栏
- drawer:侧拉抽屉
- bottomBar:底部导航
- bottomSheet和SnackBar:底部弹窗
- floatingButton:浮点按钮。
图例:
2、参数介绍
appBar
:- 一个显示在顶部的应用程序栏。系统提供使用类
AppBar()
;
- 一个显示在顶部的应用程序栏。系统提供使用类
body
:- 脚手架的主要部分,用来布局小布局,默认从左上角放置。
body
位置appBar
下,浮点按钮和drawer后。
drawer
:- 抽屉,从屏幕左侧滑出,提供api类为
Drawer
。
- 抽屉,从屏幕左侧滑出,提供api类为
endDrawer
:- 抽屉,从屏幕右侧滑出。
floatingActionButton
:- 浮点按钮,位于
body
上面;通常使用api类FloatingActionButton
; - 默认放置在
body
的右下角。
- 浮点按钮,位于
floatingActionButtonLocation
:- 确定浮点按钮的位置;
- 如果为null,默认放在
FloatingActionButtonLocation.endFloat
;body
右下角。
floatingActionButtonAnimator
:- 将浮点按钮移动到新位置的动画;
- 如果为null,默认使用
FloatingActionButtonAnimator.scaling
; - 系统提供动画类
FloatingActionButtonAnimator
,其中提供几种实现动画。
persistentFooterButtons
:- 脚手架底部的一组按钮,通常使用
FlatButton
部件,; - 位于
body
下,bottomNavigationBar
之上,始终可见,不随body
滚动。
- 脚手架底部的一组按钮,通常使用
bottomNavigationBar
:- 脚手架底部导航,位于最下面;
SnackBar
从之上滑出。
bottomSheet
:- 这是一个持久可见的部件;位于
body
下,bottomNavigationBar
和persistentFooterButtons
之上; - 与
SnackBar
出现位置相同,同时出现会覆盖SnackBar
- 这是一个持久可见的部件;位于
backgroundColor
:- 脚手架主体背景颜色。
resizeToAvoidBottomPadding
:body
是否自行调整大小,避免窗口的底部填充;默认为true。
primary
:- 脚手架是否显示在屏幕顶部,如果为真
appBar
高度将+状态栏高度; - 默认为true,相当于
AppBar.primary
为true;
- 脚手架是否显示在屏幕顶部,如果为真
3、使用方式
Scaffold(
appBar: AppBar(
title: Text('This is appBar'),
),
body: Container(
width: 1600.0,
color: Colors.yellow,
child: ListView(
children: <Widget>[
Container(
width: 160.0,
alignment: Alignment.center,
child: RaisedButton(
child: Text('skip two page'),
onPressed: () {},
),
),
Container(
height: 250.0,
alignment: Alignment.center,
child: Text(
'This is body',
style: TextStyle(fontSize: 20.0),
),
),
],
),
),
//左边的抽屉,设置后AppBar左边会有个图标按钮
drawer: new Drawer(
child: ListView(
children: <Widget>[
ListTile(
leading: Icon(Icons.change_history),
title: Text('Change history'),
onTap: () {
Navigator.pop(context); // close the drawer
},
),
//..三个....
],
),
),
//右边的抽屉,设置后AppBar右边会有个图标按钮
endDrawer: new Drawer(
child: ListView(
children: <Widget>[
ListTile(
leading: Icon(Icons.change_history),
title: Text('Change history'),
onTap: () {
Navigator.pop(context); // close the drawer
},
),
//..三个....
],
),
),
floatingActionButton: new Builder(
builder: (BuildContext context) {
return new FloatingActionButton(
onPressed:null,
child: new Icon(Icons.add),
);
},
),
bottomNavigationBar: BottomAppBar(
child: Container(
alignment: Alignment.center,
height: 50.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text('this'),
Text('is'),
Text('bottomNavigationBar'),
],
),
),
),
persistentFooterButtons: <Widget>[
new FlatButton(
onPressed: () {},
child: new Text('This'),
),
new FlatButton(
onPressed: () {},
child: new Text('is'),
),
new FlatButton(
onPressed: () {},
child: new Text('persistentFooterButtons'),
),
],
bottomSheet: Container(
color: Colors.cyanAccent,
height: 50.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text('This'),
Text('is'),
Text('bottomSheet'),
],
),
),
);
}
五、AppBar
- 参数:
名字 | 类型 | 作用 |
---|---|---|
leading | widget | 标题前的widget小部件,默认为null。此时可根据下一个参数控制。 |
automaticallyImplyLeading | bool | 控制Leading的,默认为true。当为true和leading为null时,将自动推断leading部件是什么;如果为false和leading为null时,则留出title前导空间。如果leading不为空,则此设置失效。 |
title | widget | 标题部件。通常为Text描述界面内容。 |
actions | List | 标题后面的小部件组。通常使用IconButton。 |
flexibleSpace | widget | 一个灵活的小部件,堆叠在toolbar和tabbar后面。它的高度将与appBar的总高度相同。只有在AppBar高度改变时才会有灵活的表现。 |
bottom | PreferredSizeWidget | appBar底部的部件,通常使用TabBar |
elevation | double | appBar底部阴影大小,默认为4.0,。 |
backgroundColor | Color | 背景颜色,通常与[brightness]、[iconTheme]、[textTheme]一起设置 |
brightness | Brightness | 亮度 |
iconTheme | IconThemeData | 图标的主题 |
textTheme | TextTheme | 文本主题 |
primary | bool | appBar是否现在在屏幕顶部,默认为true。appBar的高将加上状态栏的高度。 |
centerTitle | bool | 标题是否居中 |
titleSpacing | double | 标题四周间距 |
toolbarOpacity | double | appBar的透明度,取值0.0-1.0,默认为1.0. |
bottomOpacity | double | appBar底部的透明度,默认为1.0. |
- 使用:
new AppBar(
leading: Icon(Icons.arrow_back),
automaticallyImplyLeading: true,
title: Container(
color: Colors.yellow,
child: Text(
'title',
style: TextStyle(color: Colors.red),
),
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.flag),
onPressed:null,
),
IconButton(
icon: Icon(Icons.access_alarm),
onPressed:null,
),
],
flexibleSpace: Container(
color: Colors.purpleAccent,
child: FlexibleSpaceBar(
centerTitle: true,
title: Container(
color: Colors.redAccent,
child: Text('flexibleSpace'),
),
),
),
elevation: 14.0,
centerTitle: true,
)
- 效果:
六、生命周期
1、概述
- Flutter中视图Widget也有生命周期,它生命周期的回调函数都在
State
类中; - Widget的实际工作就是描述如何创建
Element
,而Element
就负责状态和生命周期的管理; StatefulWidget
通过createElement()
创建StatefulElement
,StatefulElement
内部会负责创建和保存State
,这就是为什么StatefulWidget
被重新创建而内部的状态不会丢失的原因;源码见上面widget。RenderObject
:负责视图渲染。所有的布局、绘制和事件响应全都由它负责。
2、生命周期回调函数
A、State中的回调函数
initState()
:- 当创建State时插入渲染树的时候调用,这个函数只调用一次;
- 此处可以进行初始化,但不能使用
BuildContext.inheritFromWidgetOfExactType
; - 复写此方法时,要在
super.iniState()
后
didChangeDependencies()
:- 紧跟
initState()
后面调用; - 如果
build
的调用引用了后来更改的InheritedWidget
,那么框架将调用这个方法来通知这个对象更改的消息; - 子类很少复写,因为框架总是在依赖更改后直接调用
build
; - 此时可以调用
BuildContext.inheritFromWidgetOfExactType
。
- 紧跟
build(BuildContext context)
:- 描述此小部件表示的用户界面部分;
- 此函数会在很多不同情况下调用:
- 调用
initState()
后; - 调用
didUpdateWidget()
后; - 调用
setState()
之后; - 在此
State
对象的依赖更改之后(如:前面build
更改引用的InheritedWidget
); - 调用
deactivate()
后,再将State
对象重新插入到渲染树的另一个位置。
- 调用
didUpdateWidget(LifecyclePageFull oldWidget)
:- 每当小部件配置更改时调用;
- 如果父小部件重新构建并请求在渲染树更新中的这个位置;
- 如果复写此方法,要在
super.didUpdateWidget(oldWidget)
之后。
deactivate()
:- 从渲染树中移除此对象时调用;
- 在
dispose()
之前调用。 - 如果复写此方法,要在调用
super.deactivate()
之前;
dispose()
:- 当此对象从树中永久删除时调用;
- 此时调用
setState()
是错误的; - 如果复写此方法,要在调用
super.dispose()
之前;
B、WidgetsBindingObserver中回调函数
- 此监听是监听点击home键和从任务管理器中唤起应用时的状态
AppLifecycleState
提供四种状态:resumed
:- 可见可交互
inactive
:- 此时处于非活动状态,不接收用户输入;
- 比如调起对话框或另一个窗口
- 此状态下,应用可随时转到
paused
状态
paused:
- 应用程序当前对用户不可见,不响应用户输入,在后台运行;
- 此状态下,引擎将不会回调
Window.onBeginFrame
和Window.onDrawFrame
。
suspending
- 此状态下,引擎将不会回调
Window.onBeginFrame
和Window.onDrawFrame
。 - ios无此状态。
- 此状态下,引擎将不会回调
3、回调函数调用顺序
class _LifecyclePageFullState extends State<LifecyclePageFull>
with WidgetsBindingObserver {
int number = 0;
@override
void initState() {
print('生命周期--initState');
super.initState();
//注册widget监听,复写时要在super之后
WidgetsBinding.instance.addObserver(this);
}
@override
void didChangeDependencies() {
print('生命周期--didChangeDependencies');
super.didChangeDependencies();
}
@override
void didUpdateWidget(LifecyclePageFull oldWidget) {
print('生命周期--didUpdateWidget');
super.didUpdateWidget(oldWidget);
}
@override
void deactivate() {
print('生命周期--deactivate');
super.deactivate();
}
@override
void dispose() {
print('生命周期--dispose');
//注销widget监听,复写时要在super之前
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.inactive:
print('Observer--可见没有响应inactive');
break;
case AppLifecycleState.paused:
print('Observer--不可见不响应paused');
break;
case AppLifecycleState.resumed:
print('Observer--可见可交互resumed');
break;
case AppLifecycleState.suspending:
print('Observer--申请暂停suspending');
break;
}
super.didChangeAppLifecycleState(state);
}
@override
Widget build(BuildContext context) {
print('生命周期--build');
return new Scaffold(
appBar: new AppBar(
centerTitle: true,
title: new Text('leftcycle'),
),
body: new Center(
child: new Text(
'$number',
style: TextStyle(fontSize: 25.0),
),
),
floatingActionButton: new FloatingActionButton(
onPressed: () {
setState(() {
print('number加1了');
number++;
});
},
child: new Icon(Icons.add),
),
);
}
}
-
当界面从创建到可见时:
initState()
–>didChangeDependencies()
–>build()
打印结果:
//生命周期--initState //生命周期--didChangeDependencies //生命周期--build
-
当从渲染树删除时:点击返回键
deactivate()
–>dispose()
打印结果:
//生命周期--deactivate //生命周期--dispose
-
横竖屏切换:
didUpdateWidget()
–>build()
,将调用两次(荣耀8)打印结果:
//生命周期--didUpdateWidget //生命周期--build //生命周期--didUpdateWidget //生命周期--build
-
点击home键:
AppLifecycleState.inactive
–>AppLifecycleState.paused
打印结果:
//Observer--可见没有响应inactive //Observer--不可见不响应paused
-
从任务管理器唤起应用:
AppLifecycleState.inactive
–>AppLifecycleState.resumed
打印结果:
//Observer--可见没有响应inactive //Observer--可见可交互resumed
-
点击浮点按钮:
调用
build()
打印结果:
//number加1了 //生命周期--build