Flutter基础(二)--App结构和生命周期

文章目录

  • 一、Flutter-APP结构简介
  • 二、widget
    • 1、StatelessWidget无状态widget
    • 2、StatefulWidget有状态widget
  • 三、MaterialApp
    • 1、概述
    • 2、参数介绍
    • 3、常用参数详解
      • A、路由设置方面的参数
      • B、任务管理界面方面的参数
      • C、导航监听器:navigatorObservers
      • D、主题theme
  • 四、Scaffold
    • 1、概述
    • 2、参数介绍
    • 3、使用方式
  • 五、AppBar
  • 六、生命周期
    • 1、概述
    • 2、生命周期回调函数
      • A、State中的回调函数
      • B、WidgetsBindingObserver中回调函数
    • 3、回调函数调用顺序

一、Flutter-APP结构简介

  1. Flutter是Dart语言的移动应用框架。
  2. 以Dart程序的main()函数为入口,通过runApp()函数进入到Flutter框架的应用,实现界面的展示和交互,如果不调用runApp()函数,那么所执行的就是一个Dart控制台应用,屏幕上什么也不会显示。
  3. Flutter-APP不使用原生控件,二是提供了一组自己的widget(包括material Design和Cupertino(iOS风格的widget)),由Flutter framework和Engine引擎管理和渲染,从而得到更好的性能。
  4. Flutter中一切皆为widget,无论是结构,布局,或者主题等等。
  5. 而widget是不可变的,仅支持一帧,并且在每一帧上不会直接更新,要更新就必须使用Widget的状态。
  6. 起手姿势:基本是固定模式
    1. runApp()进入,函数接受给定的widget并使其成为widget树的根
    2. 无状态StatelessWidget中设置MaterialApp确立APP设计风格,主题;
    3. 通过有状态StatefulWidgetState,来更改界面状态和响应用户操作;
    4. 通过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

  1. Widget采用现代响应式框架构建,中心思想是用widget构建你的UI。
  2. Widget描述了他们的视图在给定其当前配置和状态时的样子,包括视觉结构平台交互等。
  3. 当widget的状态发生变化时,widget会重新构建UI,Flutter会对比前后变化的不同,以确定底层渲染树从一个状态转换到下一个状态所需的最小更改。
  4. Widget本身没有可变状态(所有的字段必须是final)。
  5. 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实现者通过Statesetstate()方法确保状态在放改变时及时通知。
  • StatefulWidgetSatelessWidget一样本事是不可以改变状态的,之所以可以改变状态,是因为此类内部内置一个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来运行应用。

  • 参数homeroutesonGenerateRoutebuilder中至少一个是非空的,这里指定的路由是应用程序启动的路由。

  • 如果只指定routes参数,那么集合中必须含有一个key为:Navigator.defaultRouteName'/'的路由,因为这俩key表示程序启动的路由根路径。

  • home的路由路径为:'/',等价于Navigator.defaultRouteName

  • 所以home参数和routes参数内key为的Navigator.defaultRouteName'/'的路由不能同时出现,否则会冲突。

2、参数介绍

字段 类型 作用
1 navigatorKey Globalkey 导航键,全局唯一
2 home widget 主页
3 routes Map 路由表,内含各种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
    );
  }

效果如下:
Flutter基础(二)--App结构和生命周期_第1张图片

3、常用参数详解

A、路由设置方面的参数

homeroutesinitialRouteonGenerateRouteonUnknownRoutebuilder

  1. builder

    • home的作用一样,首次打开的路由,但优先级最高,不能为null。
    • builder设置后,initialRoute设置也失效,即启动后只显示builder设置的路由界面。

    使用:

    new MaterialApp(
      builder: (BuildContext context, Widget child) {
       return new BuilderPage();//这是页面
     },
    )     
    
  2. home:主页,

    • 路径为'/' ,默认首先打开此路径的路由。
    • 如果应用只有一个界面,使用 home 设置这个界面即可。
  3. 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(),
      },
    ) 
    
  4. initialRoute:初始路由

    • 显示的第一个路由,即进入程序时首先显示此widget界面。
    • 根据参数,去routes集合中查找路由。
    • 在显示此路由时,homeroutes中设置的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',
    ) 
    

    效果:
    Flutter基础(二)--App结构和生命周期_第2张图片

  5. 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();
          },
        );
      },
    ) 
    

    效果:
    Flutter基础(二)--App结构和生命周期_第3张图片

  6. onUnknownRoute:未知路由

    ​ 当前几项设置均为找到时,则会回调此路由。

B、任务管理界面方面的参数

titleonGenerateTitlecolor

  1. title:任务管理界面,显示的APP的标题,不同手机效果不同。
  2. onGenerateTitle:当与title同时设置时,此参数生效。
  3. color:任务管理界面,APP的图标颜色。

C、导航监听器:navigatorObservers

  1. 此参数为导航监听器,监听导航对路由弹出,放入的操作;
  2. 监听器是个集合,可根据不同需求对路由做不同的设置;
  3. 集合中传入参数为实现NavigatorObserver接口的监听器。
  4. 可以通过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-----------');
  }
}

界面操作示意图:
Flutter基础(二)--App结构和生命周期_第4张图片
打印结果:

//首次进入,首页路由
----------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

  1. 设置应用程序的主题和小部件的主题:如亮度、颜色、排版;
  2. 返回值为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
PopupMenuDividerListTile
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:浮点按钮。

图例:
Flutter基础(二)--App结构和生命周期_第5张图片

2、参数介绍

  • appBar
    • 一个显示在顶部的应用程序栏。系统提供使用类AppBar()
  • body
    • 脚手架的主要部分,用来布局小布局,默认从左上角放置。
    • body位置appBar下,浮点按钮和drawer后。
  • drawer
    • 抽屉,从屏幕左侧滑出,提供api类为Drawer
  • endDrawer
    • 抽屉,从屏幕右侧滑出。
  • floatingActionButton
    • 浮点按钮,位于body上面;通常使用api类FloatingActionButton
    • 默认放置在body的右下角。
  • floatingActionButtonLocation
    • 确定浮点按钮的位置;
    • 如果为null,默认放在FloatingActionButtonLocation.endFloatbody右下角。
  • floatingActionButtonAnimator
    • 将浮点按钮移动到新位置的动画;
    • 如果为null,默认使用FloatingActionButtonAnimator.scaling
    • 系统提供动画类FloatingActionButtonAnimator,其中提供几种实现动画。
  • persistentFooterButtons
    • 脚手架底部的一组按钮,通常使用FlatButton部件,;
    • 位于body下,bottomNavigationBar之上,始终可见,不随body滚动。
  • bottomNavigationBar
    • 脚手架底部导航,位于最下面;
    • SnackBar从之上滑出。
  • bottomSheet
    • 这是一个持久可见的部件;位于body下,bottomNavigationBarpersistentFooterButtons之上;
    • 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,
)
  • 效果:
    Flutter基础(二)--App结构和生命周期_第6张图片

六、生命周期

1、概述

  1. Flutter中视图Widget也有生命周期,它生命周期的回调函数都在State类中;
  2. Widget的实际工作就是描述如何创建Element,而Element就负责状态和生命周期的管理;
  3. StatefulWidget通过createElement()创建StatefulElementStatefulElement 内部会负责创建和保存 State,这就是为什么StatefulWidget被重新创建而内部的状态不会丢失的原因;源码见上面widget。
  4. 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.onBeginFrameWindow.onDrawFrame
    • suspending
      • 此状态下,引擎将不会回调Window.onBeginFrameWindow.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
    

你可能感兴趣的:(Flutter)