flutter StatefulWidget 轻松了解flutter 有状态组件

1,067 阅读7分钟

flutter StatefulWidget 轻松了解flutter 有状态组件

 flutter的状态组件有两种,分别是有状态组件StatefulWidget和无状态组件StatelessWidget。前一段时间,我只学习了无状态组件StatelessWidget,因为比较简单,直接布局就好了,但是只用无状态组件也有一个问题,就是无法交互和变化,不能实现复杂的功能。所以我决定开始进入下一步的学习,不能一直困在原地不动。

更新 flutter SDK

 因为一些原因,很久没使用flutter了,现在已经有了更新,那么先更新一下flutter版本吧。更新也很简单,命令是:flutter upgrade,按下回车就可以自动更新了。由3.3.2更新到3.3.6.

1.png

flutter Widget 组件(控件)

 Flutter 从 React 中吸取灵感,通过现代化框架创建出精美的组件。它的核心思想是用 widget 来构建你的 UI 界面。 Widget 描述了在当前的配置和状态下视图所应该呈现的样子。当 widget 的状态改变时,它会重新构建其描述(展示的 UI),框架则会对比前后变化的不同,以确定底层渲染树从一个状态转换到下一个状态所需的最小更改。在写应用的过程中,取决于是否需要管理状态,你通常会创建一个新的组件继承 StatelessWidget 或 StatefulWidget。Widget类本身是一个抽象类,其中最核心的就是定义了createElement()接口,在 Flutter 开发中,我们一般都不用直接继承Widget类来实现一个新组件,相反,我们通常会通过继承StatelessWidget或StatefulWidget来间接继承widget类来实现。StatelessWidget和StatefulWidget都是直接继承自Widget类,而这两个类也正是 Flutter 中非常重要的两个抽象类。如果用户交互或数据改变导致widget改变,那么它就是有状态的。如果一个widget是最终的或不可变的,那么它就是无状态。

flutter StatelessWidget 无状态组件

 StatelessWidget和StatefulWidget都是直接继承自Widget类,无状态组件在执行过程中只有一个 build 阶段,在执行期间只会执行一个 build 函数,所以在执行速度和效率方面比有状态组件更好。所以在设计组件时尽量使用无状态组件实现,能尽可能的节省计算机资源。StatelessWidget继承自widget类,重写了createElement()方法,StatelessWidget用于不需要维护状态的场景,它通常在build方法中通过嵌套其他 widget 来构建UI,在构建过程中会递归的构建其嵌套的 widget。下边是一个StatelessWidget无状态组件的例子:

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false, //去掉右上角debug标识
      theme: ThemeData(
        //主题设置
        primarySwatch: Colors.blue,
      ),
      home: const SelectionArea(
        //子组件支持文字选定 3.3新特性
        child: Scaffold(
          //子组件
          body: MyAppbody(),
        ),
      ),
    );
  }
}

flutter StatefulWidget 有状态组件

 在Flutter默认程序中的计数器中,点击了+号按钮后,显示的数字需要+1,还有一些情况,我们会进行下拉刷新、上拉加载更多,这时数据就会发生变化,而StatelessWidget通常用来展示哪些数据固定不变的,如果数据会发生改变,我们使用StatefulWidget。StatefulWidget也是继承自widget类,并重写了createElement()方法,不同的是返回的Element 对象并不相同;另外StatefulWidget类中添加了一个新的接口createState()。

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key key }) : super(key: key);
    
  @override
  StatefulElement createElement() => StatefulElement(this);
    
  @protected
  State createState();
}

 StatefulElement 间接继承自Element类,与StatefulWidget相对应(作为其配置数据)。StatefulElement中可能会多次调用createState()来创建状态(State)对象。createState() 用于创建和 StatefulWidget 相关的状态,它在StatefulWidget 的生命周期中可能会被多次调用。例如,当一个 StatefulWidget 同时插入到 widget 树的多个位置时,Flutter 框架就会调用该方法为每一个位置生成一个独立的State实例,其实,本质上就是一个StatefulElement对应一个State实例。

initState

 initState是初始化方法,当 widget 第一次插入到 widget 树时会被调用,对于每一个State对象,Flutter 框架只会调用一次该回调,所以,通常在该回调中做一些一次性的操作,如状态初始化、订阅子树的事件通知等。不能在该回调中调用。

didChangeDependencies()

 当State对象的依赖发生变化时会被调用;例如:在之前build() 中包含了一个InheritedWidget (第七章介绍),然后在之后的build() 中Inherited widget发生了变化,那么此时InheritedWidget的子 widget 的didChangeDependencies()回调都会被调用。

didUpdateWidget ()

 在 widget 重新构建时,Flutter 框架会调用widget.canUpdate来检测 widget 树中同一位置的新旧节点,然后决定是否需要更新,如果widget.canUpdate返回true则会调用此回调。

deactivate()

 当 State 对象从树中被移除时,会调用此回调。在一些场景下,Flutter 框架会将 State 对象重新插到树中,如包含此 State 对象的子树在树的一个位置移动到另一个位置时(可以通过GlobalKey 来实现)。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。

dispose()

 当 State 对象从树中被永久移除时调用;通常在此回调中释放资源。

flutter StatefulWidget 使用例子

创建项目

 接下来就试着写一些StatefulWidget。静态展示的页面我们使用StatelessWidget无状态组件,页面不需要变化,如果我们想改变页面中的数据的话这个时候就需要用到 StatefulWidget。我们先创建一个项目,输入创建项目命令flutter create stateful,打开我们的安卓虚拟机并执行代码flutter run 。简化一下初始代码。

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home:  const Homepage(),
    );
  }
}
class Homepage extends StatelessWidget {
  const Homepage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Text"),
      ),
      body: const Text("test"),
    );
  }
}


2.png

vscode快速生成StatefulWidget组件

 我们可以使用快捷键快速生成StatefulWidget组件,在vscode输入state快速生成,会生成两个类,一个继承自StatefulWidget的类,里面需要实现createState方法另一个继承自State,里面实现build方法,并且可以定义一些成员变量。

3.png

点击按钮变化界面示例实现

 我们先实现一个背景图片切换例子,点击按钮后背景图片跟随变化。怎么实现还没想好,那就先实现一个按钮吧。使用MaterialButton组件实现一个简单的按钮。

class Testwight extends StatefulWidget {
  const Testwight({super.key});
  @override
  State<Testwight> createState() => _TestwightState();
}

class _TestwightState extends State<Testwight> {
  @override
  Widget build(BuildContext context) {
    return  MaterialButton(
      color: Colors.blue,
      textColor: Colors.white,
      child: const Text('点我'),
      onPressed: () {
        // ...
      },
    );
  }
}



4.png

 下边的实现都是在有状态组件下实现的,我们首先定义两个全局变量mg1、mg1用来放图片地址,接着在_TestwightState里面先设置一个局部变量pngpath存放当前文件路径。布局我们外层使用Column,竖直布局,里面放置一个Image.asset来显示图片,在放置一个MaterialButton实现点击切换图片功能。

class Testwight extends StatefulWidget {
  const Testwight({super.key});
  @override
  State<Testwight> createState() => _TestwightState();
}
String mg1 = "images/1.png";//图片地址 全局变量
String mg2 = "images/1.jpg";//图片地址 全局变量
class _TestwightState extends State<Testwight> {
  String pngpath = mg1;//图片地址 局部变量
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Image.asset(pngpath),//图片组件
        MaterialButton(//按钮组件
          color: Colors.blue,
          textColor: Colors.white,
          child: const Text('点我'),
          onPressed: () {
            setState(() {//点击动作,调用setState刷新页面
              pngpath = pngpath == mg1 ? mg2 : mg1;//实现图片切换
            });
            // ...
          },
        )
      ],
    );
  }
}

 在MaterialButton组件中的onPressed监控点击事件,每次点击就调用setState函数,我们要改变有状态组件的状态,就要通过调用setState函数来实现,setState会在UI中重建此widget。上边也提到了一些其他函数,在合适的时机选择对应的函数就行了。(我不会截取动图,哪个大神教教我呗)

5.png

6.png

总结

 对flutter 的 StatefulWidget组件浅尝辄止的学习了一下,只学到了一点皮毛,还有很多特性还不了解,更不能灵活的使用,后续将会结合实际使用进行进一步的学习,今天算是入门了。