Flutter跨平台移动端开发丨Widget、Element、State、状态管理

目录

  1. Widget
  2. Element
  3. State
  4. 状态管理

Widget 的概念

widget 的主要工作是通过实现 build 函数 来构建自身。一个 widget 通常由一些低级别的 widget 组成,flutter 框架依次的构建这些低级别的 widget,直到构建到最底层的子 widget 时,它会计算并描述 widget 的几何形状

flutter 中所有的对象都是一个 widget 。它既可以表示UI元素(如:Text / Image / Row / Column),也可表示功能性的组件(如:GestureDetectorWidget - 手势检测 / Theme - 数据传递)

Widget 的分类

widget 可分为 无状态的 StatelessWidget 或者是有状态的 StatefulWidget,两者的区别在于状态的改变,需要根据当前widget是否需要管理一些状态来选择使用

StatelessWidget:无状态,比如标题栏中的标题
StatefulWidget:有状态,创建时需要指定一个 State ,在需要更新 UI时调用 setState(VoidCallbackfn),并在 VoidCallback 中改变一些些变量数值等,组件会重新 build 以达到数显状态/UI的效果。代码实例如下:

/**
 * @des StatefulWidget 使用实例 - 计数器
 * @author liyongli 20190409
 * */
class Conunter extends StatefulWidget{

  // 快捷写法
  @override
  _CounterState createState() => new _CounterState();

  /**
   * // 原始写法
   * @override
   * State<StatefulWidget> createState() {
   *    return  new _CounterState();
   * }
   * */

}

/**
 * 计数器操作模块
 * */
class _CounterState extends State<Conunter>{

  // 计数器
  int number = 0;

  // 按钮点击事件监听
  void _numberAdd(){
    setState(() {
      number += 1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Row(
      children: <Widget>[
        new RaisedButton(
            onPressed: _numberAdd,
            child: new Text("ADD"),
        ),
        new Text("更新数值: $number次")
      ],
    );
  }

}
Widget 支持库

flutter 提供了一套丰富、强大的基础 widget ,在此基础上还提供了Android 默认风格库: Material 与 IOS 风格库:Cupertino。

导入相应的依赖即可使用:

import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
基础 Widget
  • Text:文本
  • Row:水平布局,基于 web Flexbox 布局模型。
  • Column:垂直布局,基于 web Flexbox 布局模型。
  • Stack:取代线性布局,与 Android 中 FrameLayout相似,允许子 widget 堆叠,使用 positioned 定位它们相对于上下左右四条边的位置。基于 web absolute positioning(绝对定位) 布局模型
  • Container:矩形元素,可以装饰 BoxDecoration,如 background、边框、阴影,它可以具有边距(margins)、填充(padding)和应用与其大小的约束(constraints)。Container 可以使用矩阵在三维空间中对其进行变换
Material

遵循 Material Design 规则。以 MaterialAppWidget 开始,包含有 Scaffold、AppBar、FlatButton 等。

使用前需要先引入依赖:

import 'package:flutter/material.dart';

Material 库中有一些 widget 可以根据实际运行平台切换风格,如 MaterialPageRoute,在路由切换时,切换动画会随平台不同而变化

Cupertino

遵循 IOS 应用风格,目前还没有 Material 库丰富。

使用前需要先引入依赖:

import 'package:flutter/cupertino.dart';

由于 Material 和 Cupertino 都是在基础 widget 库之上的,所以如果你的应用中引入了这两者之一,则不需要再引入 flutter/widgets.dart 了,因为它们内部已经引入过了。


Element

widget 中主要包含了组件的配置数据,但它并不代表最终绘制在屏幕上的显示元素,真正代表屏幕上显示元素的是 element,widget 是 element 的配置数据,一个 widget 可同时对应多个 element


State 的概念

每一个 StatefulWidget 类都会对应一个 State 类,State 表示与其对应的 StatefulWidget 要维护的状态,保存的状态信息可以在 build 时被获取,同时,在 widget 生命周期中可以被改变,改变发生时,可以调用其 setState() 方法通知 framework 发生改变,framework 会重新调用 build 方法重构 widget 树,最终完成更新 UI 的目的。

state 中包含两个常用属性:widgetcontextwidget 属性表示当前正在关联的 widget 实例,但关联关系可能会在 widget 重构时发生变化(framework 会动态设置 widget 属性为最新的widget 对象)。context 属性是 buildContext 类的实例,表示构建 widget 的上下文,每个 widget 都有一个自己的 context 对象,它包含了查找、遍历当前 widget 树的方法。

State 的生命周期

  • initState:当前 widget 对象插入 widget树中时调用
  • didChangeDependencies:当前 State 对象的依赖项发生变化时调用
  • build:绘制当前界面布局时调用
  • reassemble:使用热重载时调用
  • didUpdateWidget:widget 配置内容有变动重构时调用
  • deactivate:当前 widget 对象从 widget 树中移出时调用
  • dispose:当前 widget 对象从 widget 树中永久删除时调用
名称 返回值/类型 意义
context read-only BuildContext The location in the tree where this widget builds.
mounted read-only bool Whether this State object is currently in a tree.
widget read-only T The current configuration.
hashCode read-only, inherited int The hash code for this object.
runtimeType read-only, inherited Type A representation of the runtime type of the object.
build(BuildContext context) Widget 绘制当前界面布局时调用 / Describes the part of the user interface represented by this widget.
deactivate() void 当前 widget 对象从 widget 树中移出时调用 / Called when this object is removed from the tree.
debugFillProperties(DiagnosticPropertiesBuilder properties) void Add additional properties associated with the node.
didChangeDependencies() void 当前 State 对象的依赖项发生变化时调用 / Called when a dependency of this State object changes.
didUpdateWidget(T oldWidget) void widget 配置内容有变动重构时调用 / Called whenever the widget configuration changes.
dispose() void 当前 widget 对象从 widget 树中永久删除时调用 / Called when this object is removed from the tree permanently.
initState() void 当前 widget 对象插入 widget树中时调用 / Called when this object is inserted into the tree.
reassemble() void 使用热重载时调用 / Called whenever the application is reassembled during debugging, for example during hot reload.
setState (VoidCallback fn) void Notify the framework that the internal state of this object has changed.

实际代码测试

/**
 * @des StatefulWidget 使用实例 - 计数器
 * @author liyongli 20190409
 * */
class Conunter extends StatefulWidget{

  // 快捷写法
  @override
  _CounterState createState() => new _CounterState();

  /**
   * // 原始写法
   * @override
   * State<StatefulWidget> createState() {
   *    return  new _CounterState();
   * }
   * */

}

/**
 * 计数器操作模块
 * */
class _CounterState extends State<Conunter>{

  // 计数器
  int number = 0;

  // 按钮点击事件监听
  void _numberAdd(){
    setState(() {
      number += 1;
    });

  }

  @override
  Widget build(BuildContext context) {
    print("widget 绘制 - build");
    return new Row(
      children: <Widget>[
        new RaisedButton(
            onPressed: _numberAdd,
            child: new Text("ADD"),
        ),
        new Text("更新数值: $number次")
      ],
    );
  }

  @override
  void initState() {
    super.initState();
    print("State 创建 - initState");
  }

  @override
    void didUpdateWidget(Conunter oldWidget) {
      super.didUpdateWidget(oldWidget);
      print("widget 重构 - didUpdateWidget");
  }

  @override
  void deactivate() {
    super.deactivate();
    print("State 移出 - deactivate");
  }

  @override
  void dispose() {
    super.dispose();
    print("State 删除 - dispose");
  }

  @override
  void reassemble() {
    super.reassemble();
    print("热重载 - reassemble");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("State 更改 - didChangeDependencies");
  }

}
  • 首次运行时
I/flutter (28866): State 创建 - initState
I/flutter (28866): State 更改 - didChangeDependencies
I/flutter (28866): widget 绘制 - build
  • 使用热重载时
I/flutter (28866): 热重载 - reassemble
I/flutter (28866): widget 重构 - didUpdateWidget
I/flutter (28866): widget 绘制 - build
  • 更改路由(移除当前 widget)后使用热重载时
I/flutter (28866): 热重载 - reassemble
I/flutter (28866): State 移出 - deactivate
I/flutter (28866): State 删除 - dispose

StatefulWidget 状态管理

管理状态的常见方法:

  • widget 管理自己的 state
  • 父 widget 管理子 widget 状态
  • 混合管理

决定状态管理的原则:

  • 有关用户数据由父 widget 管理
  • 有关界面效果由 widget 本身管理
  • 状态被不同 widget 共享,由他们共同的父 widget 管理

widget 管理自己的 state

/**
 * @des 管理自身状态
 * @author liyongli 20190410
 * */
class TapboxA extends StatefulWidget{

  TapboxA({Key key}):super(key:key);

  @override
  _TapboxAState createState() => new _TapboxAState();

}

/**
 * 颜色变化测试模块
 * */
class _TapboxAState extends State<TapboxA>{

  bool _active = false;

  void _handleTap(){
    setState(() {
      _active = !_active;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new GestureDetector(
      onTap: _handleTap,
      child: new Container(
        child: new Center(
          child: new Text(
            _active ? "Activie"  : "Inactive",
            style: new TextStyle(fontSize: 32.0,color: Colors.white),
          ),
        ),
        width: 200.0,
        height: 200.0,
        decoration: new BoxDecoration(
            color: _active ? Colors.lightGreen[700] : Colors.grey[600]
        ),
      ),
    ),
    );
  }
}

父 widget 管理子 widget 状态

/**
 * @des 父 widget 管理子 widget 状态
 * @author liyongli 20190410
 * */
class TapboxBParentWidget extends StatefulWidget {

  @override
  _TapboxBParentState createState() => new _TapboxBParentState();

}

/**
 * 父 widget
 * */
class _TapboxBParentState extends State<TapboxBParentWidget>{

  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Container(
      child: new TapboxB(
        active: _active,
        onChanged: _handleTapboxChanged
      ),
    );
  }
}

/**
 * 子 widget
 * */
class TapboxB extends StatelessWidget{

  TapboxB({Key key, this.active: true, @required this.onChanged})
      : super(key: key);
  ValueChanged<bool> onChanged ;
  bool active;

  void _handleTap(){
      onChanged(!active);
  }

  Widget build(BuildContext context) {
    return new GestureDetector(
      onTap: _handleTap,
      child: new Container(
        child: new Center(
          child: new Text(
            active ? 'Active' : 'Inactive',
            style: new TextStyle(fontSize: 32.0, color: Colors.white),
          ),
        ),
        width: 200.0,
        height: 200.0,
        decoration: new BoxDecoration(
          color: active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
      ),
    );
  }
}

混合管理

/**
 * @des 混合管理
 * @author liyongli 20190410
 * */
class ParentTapboxCWidget extends StatefulWidget{

  @override
  State<StatefulWidget> createState() => new ParentTapboxCState();

}

class ParentTapboxCState extends State<ParentTapboxCWidget>{

  bool _active = false;

  void _handleTapboxChanged(bool newValue){
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Container(
      child: new TapboxCWidget(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

class TapboxCWidget extends StatefulWidget{

  TapboxCWidget({Key key, this.active:false, @required this.onChanged}):super(key:key);
  final bool active;
  final ValueChanged<bool> onChanged;

  @override
  State<StatefulWidget> createState() => new TapboxCState();

}

class TapboxCState extends State<TapboxCWidget>{

  bool _highlight = false;

  void _handleTapDown(TapDownDetails details){
    setState(() {
      _highlight = true;
    });
  }

  void _handleTapUp(TapUpDetails details){
    setState(() {
      _highlight = false;
    });
  }

  void _handleCancel(){
    setState(() {
      _highlight = false;
    });
  }

  void _handleTap(){
    widget.onChanged(!widget.active);
  }


  @override
  Widget build(BuildContext context) {
    return new GestureDetector(
      onTapDown: _handleTapDown,
      onTapUp: _handleTapUp,
      onTap: _handleTap,
      onTapCancel: _handleCancel,
      child: new Container(
        child: new Center(
          child: new Text(
            widget.active ? "Active" : "Inactive",
          style: new TextStyle(fontSize: :32.0,color: Colors.white),),
        ),
        width: 200.0,
        height: 200.0,
        decoration: new BoxDecoration(
          color: widget.active ? Colors.lightGreen[700] : Colors.grey[600],
          border: _highlight
              ? new Border.all(
            color: Colors.teal[700],
            width: 10.0,
          )
              : null,
        ),
      ),
    );
  }
}

本篇到此完结,更多 Flutter 跨平台移动端开发 原创内容持续更新中~

期待您 关注 / 点赞 / 收藏 向着 大前端工程师 晋级!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容