乐趣区

关于SegmentFault:Fluttervol1-理解State与StatefulWidget

???? 0

咱们所做进去的 App,不能仅仅是一幅静止不动的画面!想一下你生存中的这些场景:

  • 手机衰弱 App 上的步数随着你的幸(huang)苦(dong)运(shou)动(bi)而减少;
  • 手机时钟 App 里的秒表计数随着工夫的飞快流逝而扭转;
  • 进度条缓缓延长;

如果这些 App 是用 Flutter 编写成的,那么这些 Widget 的 “状态” 就在随着用户的操作而产生扭转。

StatefulWidget就是有状态的Widget。它在程序运行时,状态被容许产生扭转。

只有状态可能产生扭转的 StatefulWidget 能力让页面不再变化无穷。也就是说,要想编写出 可能被用户操作 的 App,就必须要应用StatefulWidget

???? 目录

  1. ???? Here’s the code!
  2. ???? 了解 StatefulWidgetState
  3. ???? 当咱们 setState(() {}); 时,Flutter 在幕后帮咱们做了什么?

???? 1

Here’s the code!

咱们要实现这样的一个计数器:

咱们实现了一个简略的计数器,画面两头的数字随着咱们按动左下角的蓝色按钮而递增。

main.dart如下:

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() {return MyAppState();
  }
}

class MyAppState extends State<MyApp> {
  var _counter = 0;

  @override
  Widget build(BuildContext buildContext) {
    return MaterialApp(
      title: '计数器',
      home: Scaffold(
        appBar: AppBar(title: Text('计数器')
        ),
        body: Center(
          child: Text(
            '$_counter',
             style: TextStyle(fontSize: 30)
          )
        ),
        floatingActionButton: FloatingActionButton(onPressed: () => setState(() => _counter += 1),
          child: Icon(Icons.add)
        ),
      )
    );
  }
}

代码来得太快,就像龙卷风?不要胆怯,让咱们一点一点分析它。

???? 2

了解 StatefulWidgetState

结尾,我引入了package:flutter/material.dart,这外面蕴含了 Flutter 曾经封装好的 Material 格调的Widget

import 'package: flutter/material.dart'

接下来,是经典的runApp(),这个时候把我编写好的MyApp(继承了StatefulWidget)的实例作为参数传入。

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

而后在 MyApp 内,咱们会发现显示的具体内容 并不是在这个类外面实现的!

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() {return MyAppState();
  }
}

在这个类外面,咱们重写了一个 createState() 函数,而后这个函数返回了一个 State<MyApp>,是用户定义的MyAppState 类的实例!

而后 MyAppState 是何方神圣呢?

class MyAppState extends State<MyApp> {
  var _counter = 0;

  @override
  Widget build(BuildContext buildContext) {
    return MaterialApp(
      title: '计数器',
      home: Scaffold(
        appBar: AppBar(title: Text('计数器')
        ),
        body: Center(
          child: Text(
            '$_counter',
             style: TextStyle(fontSize: 30)
          )
        ),
        floatingActionButton: FloatingActionButton(onPressed: () => setState(() => _counter += 1),
          child: Icon(Icons.add)
        ),
      )
    );
  }
}

能够看到了,MyAppState继承了 State<MyApp>,而在这个类外面,咱们重写了build() 函数,并将这个 Widget 的内容写在了这外面。

所以,咱们能够得悉:

  • StatefulWidget外面须要重写 createState() 函数,这个函数返回一个 State<StatefulWidget> 的实例。
  • State<StatefulWidget>外面须要重写 build() 函数,这个函数返回一个 Widget 的实例。

然而,咱们会发现,仅仅满足了这些,Widget的状态还不足以发生变化。回看方才的源码,这里有一行:

onPressed: () => setState(() => _counter += 1)

floatingActionButtononPressed 属性定义了这个按钮按完之后会触发什么动作,它的值是一个函数(在 Dart 内,函数是一等对象)。这个函数它会执行 setState(() => _counter += 1)。那么,这个setState() 是做什么的呢?

???? 3

当咱们 setState(() {}); 时,Flutter 在幕后帮咱们做了什么?

  1. setState(fn)会先执行咱们传入的函数fn
  2. 接下来,setState()会将这个组件标记为“脏的”。
  3. 下一帧时,Flutter 会从新执行这个“脏”组件的 build() 函数,将它从新渲染一遍。

在上述计数器程序中 setState(fn); 的执行过程。动图为自制,转载请注明出处。图中组件树为了简洁明了,省略了 AppBarIconWidget,只注明参加了这个过程的少数几个Widget

也就是说,setState({} ());才是本程序的点睛之笔

???? -1

总结

  • StatefulWidget就是有状态的 Widget。它在程序运行时,状态被容许产生扭转,要想编写出 可能被用户操作 的 App,就必须要应用StatefulWidget
  • 咱们实现了一个简略的计数器。
  • StatefulWidget外面须要重写 createState() 函数,这个函数返回一个 State<StatefulWidget> 的实例。
  • State<StatefulWidget>外面须要重写 build() 函数,这个函数返回一个 Widget 的实例。
  • 当咱们执行 setState(fn); 时:

    1. setState(fn)会先执行咱们传入的函数fn
    2. 接下来,setState()会将这个组件标记为“脏的”。
    3. 下一帧时,Flutter 会从新执行这个“脏”组件的 build() 函数,将它从新渲染一遍。

它的用处是刷新一个组件的 状态

退出移动版