FSA全栈行动 FSA全栈行动
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr

公众号:FSA全栈行动

记录学习过程中的知识
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr
  • Flutter - Dart特性语法
  • Flutter - Dart事件循环机制与异步
  • Flutter - StateWidget与生命周期
    • 一、程序入口
      • 1、界面的本质(Widget)
      • 2、MaterialApp
    • 二、State Widget
      • 1、StatelessWidget
      • 2、StatefulWidget
      • 3、为什么 State Widget 本身不能定义状态?
    • 三、综合案例
      • 1、StatelessWidget 综合案例(商品列表)
      • 2、StatefulWidget 综合案例(计数器)
    • 四、生命周期
      • 1、StatelessWidget 的生命周期
      • 2、StatefulWidget 的生命周期
  • Flutter - 基础Widget
  • Flutter - 基础布局
  • Flutter - ListView与GridView
  • 《Flutter从入门到实战》
GitLqr
2021-03-23
目录

Flutter - StateWidget与生命周期

欢迎关注微信公众号:[FSA全栈行动 👋]

# 一、程序入口

一般情况下,Flutter 的主入口是 main.dart。

# 1、界面的本质(Widget)

在 main 函数中通过 runApp 函数启动一个 Flutter 界面,而 runApp(Widget app) 函数接收的参数 Widget app 就是界面了,即界面是一个 Widget:

main() {
  runApp(Center(
    child: Text("Hello world",
        textDirection: TextDirection.ltr,
        style: TextStyle(fontSize: 30, color: Colors.orange)),
  ));
}

在 Flutter 中,所有的界面、控件都是 Widget

# 2、MaterialApp

为了快速开发一个标准 material 的界面,Flutter 提供了 MaterialApp 这个 Widget:

import 'package:flutter/material.dart';

main() {
  // 1. runApp 函数
  runApp(MaterialApp(
    // debugShowCheckedModeBanner: false, // 控制界面右上角是否显示`debug`提示
    home: Scaffold(
      appBar: AppBar(
        title: Text("第一个Flutter程序"),
      ),
      body: Center(
        child: Text(
          "Hello World",
          textDirection: TextDirection.ltr,
          style: TextStyle(fontSize: 30, color: Colors.orange),
        ),
      ),
    ),
  ));
}

Scaffold 也是 Widget,翻译为脚手架,可以帮助开发者搭建出带 AppBar 的界面

# 二、State Widget

万物基于 Widget 的 Flutter 把 Widget 分为两类,分别是:

  • StatelessWidget: 无状态的 Widget,内容是确定没有状态(data)的改变
  • StatefulWidget: 有状态的 Widget,在运行过程中有一些状态(data)需要改变

# 1、StatelessWidget

上面的 demo 中,runApp(Widget app)、MaterialApp({this.home})、Scaffold({this.body}) 这 3 个方法接收的主要参数都是 Widget 类型,并且不涉及状态的保存,因此可以把它们抽成对应的 3 个 StatelessWidget 来使用,这样就可以有效的避免 嵌套地狱 的发生:

这种抽取出一个个 Widget 的方式,类似于大前端中的组件化开发

import 'package:flutter/material.dart';

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("第一个Flutter程序"),
      ),
      body: ContentBody(),
    );
  }
}

class ContentBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
        "Hello World",
        textDirection: TextDirection.ltr,
        style: TextStyle(fontSize: 30, color: Colors.orange),
      ),
    );
  }
}

AndroidStudio 中的 State Widget 快速创建模板:

  • stless:输入 stless 直接生成 StatelessWidget
  • stful:输入 stful 直接生成 StatefulWidget

# 2、StatefulWidget

Flutter 是声明式开发,即一旦状态发生变化,界面会自动改变,这里说的状态就是数据。StatefulWidget 就是可以有状态的 Widget,一个简单的例子:写一个同意协议控件:

/// flag:状态
/// Stateful不能定义状态 -> 创建一个单独的State类,这个State类负责维护状态
class ContentBody extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return ContentBodyState();
  }
}

/// 使用State类来保存状态
class ContentBodyState extends State<ContentBody> {
  var flag = true;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Checkbox(
            value: flag,
            onChanged: (value) {
              /// flag = value; // 无效代码,不会刷新界面
              /// 必须使用 setState() 方法强制界面刷新
              setState(() {
                flag = value;
              });
            },
          ),
          Text("同意协议", style: TextStyle(fontSize: 20)),
        ],
      ),
    );
  }
}

 注意:

  • Flutter 中的状态(State)和 React 中的状态概念一致。
  • 单单修改数据是不行的,还必须调用 setState() 通知界面在下一帧重新绘制才行。

# 3、为什么 State Widget 本身不能定义状态?

无论 StatelessWidget 还是 StatefulWidget,其父类都是 Widget,来看看 Widget 的定义:

@immutable
abstract class Widget extends DiagnosticableTree {
    ...
}

Widget 有 @immutable 注解,就意味着 Widget 的所有子类即使有成员属性,也一定是使用 final 修饰的,试问 final 变量会有变化吗?

/// 所有的Widget类中都不能定义状态,类成员属性必须是final
class ContentBody extends StatelessWidget {
  // IDE报错:This class (or a class that this class inherits from) is marked as '@immutable', but one of more of its instance fields aren't final: ContentBody.flag

  // 错误代码
  // @immutable 注释过的类,成员属性必须是final的
  var flag = true;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Checkbox(value: flag, onChanged: (value) => flag = value),
          Text("同意协议", style: TextStyle(fontSize: 20)),
        ],
      ),
    );
  }
}

# 三、综合案例

# 1、StatelessWidget 综合案例(商品列表)

import 'package:flutter/material.dart';

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("商品列表"),
      ),
      body: HomeContent(),
    );
  }
}

class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Column 会越界relayoutBoundary,改成ListView就好了
    return ListView(
      children: [
        HomeProductItem("Apple1", "Macbook1",
            "https://tva1.sinaimg.cn/large/006y8mN6gy1g72j6nk1d4j30u00k0n0j.jpg"),
        SizedBox(height: 6),
        HomeProductItem("Apple2", "Macbook2",
            "https://tva1.sinaimg.cn/large/006y8mN6gy1g72imm9u5zj30u00k0adf.jpg"),
        SizedBox(height: 6),
        HomeProductItem("Apple3", "Macbook3",
            "https://tva1.sinaimg.cn/large/006y8mN6gy1g72imqlouhj30u00k00v0.jpg"),
      ],
    );
  }
}

class HomeProductItem extends StatelessWidget {
  final String title;
  final String desc;
  final String imageURL;
  final style1 = TextStyle(fontSize: 25, color: Colors.orange);
  final style2 = TextStyle(fontSize: 20, color: Colors.green);

  HomeProductItem(this.title, this.desc, this.imageURL);

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(8),
      decoration: BoxDecoration(
        border: Border.all(
          width: 5, // 设置边框的宽度
          color: Colors.pink, // 设置边框的颜色
        ),
      ),
      child: Column(
        // crossAxisAlignment: CrossAxisAlignment.start,
        // crossAxisAlignment: CrossAxisAlignment.center,
        // crossAxisAlignment: CrossAxisAlignment.end,
        // crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          // Text(title, style: style1, textAlign: TextAlign.center),
          Text(title, style: style1),
          SizedBox(height: 8),
          Text(desc, style: style2),
          SizedBox(height: 8),
          Image.network(imageURL),
        ],
      ),
    );
  }
}

# 2、StatefulWidget 综合案例(计数器)

import 'package:flutter/material.dart';

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("title")),
      body: HomeContent("hello world"),
    );
  }
}

/// 为什么Flutter在设计的时候StatefulWidget的build方法放在State中
/// 1. build出来的Widget是需要依赖State中的变量(状态/数据)
/// 2. 在Flutter的运行过程中:
///   Widget是不断的销毁和创建的
///   当我们自己的状态发生改变时,并不希望重新创建一个新的State
class HomeContent extends StatefulWidget {
  final String message;

  HomeContent(this.message);

  @override
  _HomeContentState createState() => _HomeContentState();
}

class _HomeContentState extends State<HomeContent> {
  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          _getButtons(),
          Text("当前计数:$_counter", style: TextStyle(fontSize: 25)),
          Text("传递的信息:${widget.message}")
        ],
      ),
    );
  }

  Widget _getButtons() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        ElevatedButton(
          child: Text("+", style: TextStyle(fontSize: 20)),
          style: TextButton.styleFrom(backgroundColor: Colors.pink),
          onPressed: () {
            setState(() {
              _counter++;
            });
          },
        ),
        ElevatedButton(
          child: Text("-", style: TextStyle(fontSize: 20)),
          style: TextButton.styleFrom(backgroundColor: Colors.purple),
          onPressed: () {
            setState(() {
              _counter--;
            });
          },
        ),
      ],
    );
  }
}

# 四、生命周期

# 1、StatelessWidget 的生命周期

因为 StatelessWidget 没有状态,所以它的生命周期很简单:先调用 构造函数,再调用 build(),没了:

import 'package:flutter/material.dart';

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("title")),
      body: HomeContent("hello world"),
    );
  }
}

// StatelessWidget 的生命周期
class HomeContent extends StatelessWidget {
  final String message;

  HomeContent(this.message) {
    print("构造函数被调用");
  }

  @override
  Widget build(BuildContext context) {
    print("调用build方法");
    return Text(message);
  }
}

日志输出:

I/flutter ( 3092): 构造函数被调用
I/flutter ( 3092): 调用build方法

# 2、StatefulWidget 的生命周期

方法 说明
createState Framework 会通过调用 StatefulWidget.createState 来创建一个 State。
initState 新创建的 State 会和一个 BuildContext 产生关联,此时认为 State 已经被安装好了,initState 函数将会被调用。通常,我们可以重写这个函数,进行初始化操作。
didChangeDependencies 在 initState 调用结束后,这个函数会被调用。事实上,当 State 对象的依赖关系发生变化时,这个函数总会被 Framework 调用。
build 经过以上步骤,系统认为一个 State 已经准备好了,就会调用 build 来构建视图。我们需要在这个函数中返回一个 Widget。
deactivate deactivate 当 State 被暂时从视图树中移除时,会调用这个函数。页面切换时,也会调用它,因为此时 State 在视图树中的位置发生了变化,需要先暂时移除后添加。
dispose 当 State 被永久地从视图树中移除时,Framework 会调用该函数。在销毁前触发,我们可以在这里进行最终的资源释放。在调用这个函数之前,总会先调用 deactivate 函数。
didUpdateWidget 当 Widget 的配置发生变化时,会调用这个函数。比如,热重载的时候就会调用这个函数。调用这个函数后,会调用 build 函数。
setState 当需要更新 State 的视图时,需要手动调用这个函数,它会触发 build 函数。
  • 方法说明摘抄至:http://www.cxybcw.com/45008.html (opens new window)
  • 官方文档(全):https://api.flutter.dev/flutter/widgets/State-class.html (opens new window)
// StatefulWidget 的生命周期
class HomeContent extends StatefulWidget {
  HomeContent(String message) {
    print("1. 调用 HomeContent 的 constructor 方法");
  }

  @override
  _HomeContentState createState() {
    print("2. 调用 HomeContent 的 createState 方法");
    return _HomeContentState();
  }
}

class _HomeContentState extends State<HomeContent> {
  _HomeContentState() {
    print("3. 调用 _HomeContentState 的 constructor 方法");
  }

  @override
  void initState() {
    // 注意:这里必须调用super(@mustCallSuper)
    super.initState();
    print("4. 调用 _HomeContentState 的 initState 方法");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("调用 _HomeContentState 的 didChangeDependencies 方法");
  }

  @override
  void didUpdateWidget(covariant HomeContent oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("调用 _HomeContentState 的 didUpdateWidget 方法");
  }

  @override
  Widget build(BuildContext context) {
    print("5. 调用 _HomeContentState 的 build 方法");
    return Container();
  }

  @override
  void dispose() {
    super.dispose();
    print("6. 调用 _HomeContentState 的 dispose 方法");
  }
}

日志结果:

// 界面初始化
I/flutter ( 3092): 1. 调用 HomeContent 的 constructor 方法
I/flutter ( 3092): 2. 调用 HomeContent 的 createState 方法
I/flutter ( 3092): 3. 调用 _HomeContentState 的 constructor 方法
I/flutter ( 3092): 4. 调用 _HomeContentState 的 initState 方法
I/flutter ( 3092): 调用 _HomeContentState 的 didChangeDependencies 方法
I/flutter ( 3092): 5. 调用 _HomeContentState 的 build 方法

// 点击计数按钮
I/flutter ( 3092): 5. 调用 _HomeContentState 的 build 方法

没有打印 dispose()中的日志,是因为这里 Demo 中没有将 Widget 移除

#runApp#MaterialApp#StatelessWidget#StatefulWidget
Flutter - Dart事件循环机制与异步
Flutter - 基础Widget

← Flutter - Dart事件循环机制与异步 Flutter - 基础Widget→

最近更新
01
Flutter - Xcode16 还原编译速度
04-05
02
AI - 免费的 Cursor 平替方案
03-30
03
Android - 2025年安卓真的闭源了吗
03-28
更多文章>
Theme by Vdoing | Copyright © 2020-2025 FSA全栈行动
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×