第142期:flutter的状态组件和状态管理
  x9sxU9d8g9Tj 2023年11月02日 56 0

封面图

第142期:flutter的状态组件和状态管理_Text

我们在看电影的时候,往往只关注某个主演的角色,其实那些小角色的表演,远远比主演角色的表演要丰富~

场景

怎样才能在我们的flutter应用中对用户输入做出响应?比如我们有个图标,我们想让它支持点击事件,或者在状态改变的时候换一个不同的图标。

其实我们可以创建一个有状态的组件来控制或管理那些需要变化的组件。

状态组件 VS 无状态组件

这两个概念在react中我们非常熟悉,状态组件内部定义的有自己的属性,可以用来控制不同状态下展示不同的界面。无状态组件则只负责展示界面,没有其他的多余功能。

在flutter中无状态组件有很多,比如:​​Icon​​, ​​IconButton​​, and ​​Text​​。他们继承​​StatelessWidget​​类。

状态组件​​stateful widget​​​则是动态的:例如,它可以响应用户交互触发的事件或接收数据时更改其外观。​​Checkbox​​, ​​Radio​​, ​​Slider​​, ​​InkWell​​, ​​Form​​, and ​​TextField​​其实都是状态组件,他们继承了​​StateulWidget​​类。

回想一下web端的开发,其实大同小异。

组件的状态存储在state对象中,将控件的状态与其外观分开。状态由可以更改的值组成,例如滑块的当前值或是否选中复选框。当小部件的状态发生变化时,状态对象调用setState(),告诉框架重新绘制小部件。

创建状态组件

需要注意的是:

/**
1. 状态组件件由两个类实现:StatefulWidget的子类和State的子类。
2. state类包含组件的可变状态和组件的build()方法。
3. 当组件状态发生变化时,state对象调用setstate方法,通知框架重新绘制组件。
**/

创建一个自定义的状态组件需要创建两个类:

/**
1. StatefulWidget类 用来定义组件。
2. 包含组件状态的State类, 用来定义组件的build方法。
**/

先继承​​StatefulWidget​​类,定义组件:

class FavoriteWidget extends StatefulWidget {
const FavoriteWidget({super.key});

@override
State<FavoriteWidget> createState() => _FavoriteWidgetState();
}

再扩展​​State​​类定义状态:

class _FavoriteWidgetState extends State<FavoriteWidget> {
bool _isFavorited = true;
int _favoriteCount = 41;

// ···
}

然后实现​​build()​​方法实现组件展示:

class _FavoriteWidgetState extends State<FavoriteWidget> {
// ···
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(0),
child: IconButton(
padding: const EdgeInsets.all(0),
alignment: Alignment.centerRight,
icon: (_isFavorited
? const Icon(Icons.star)
: const Icon(Icons.star_border)),
color: Colors.red[500],
onPressed: _toggleFavorite,
),
),
SizedBox(
width: 18,
child: SizedBox(
child: Text('$_favoriteCount'),
),
),
],
);
}
}

然后实现​​ _toggleFavorite​​方法用来更新状态:

void _toggleFavorite() {
setState(() {
if (_isFavorited) {
_favoriteCount -= 1;
_isFavorited = false;
} else {
_favoriteCount += 1;
_isFavorited = true;
}
});
}

这样就简单的实现了一个状态组件。

状态管理

需要注意的内容:

/**
1. 管理状态有不同的方法。
2. 作为组件的开发者,我们可以选择具体使用哪种方法。
3. 如果我们不确定怎么管理状态,就把状态放到父组件中进行管理。
**/

到底是谁在负责状态的管理呢?组件本身?父组件?或者有个更高级的组件?其实是根据情况而定的。

根据实际情况进行状态管理是一种最有效的方法,以下是管理状态的最常见方法:

  • 组件自身控制自己的状态
  • 父组件控制子组件的状态
  • 混合状态控制

​我们该怎么选择呢?​​建议如下:

/**
1. 如果所讨论的状态是用户数据,例如复选框的选中或未选中模式,或者滑块的位置,那么状态最好由父组件管理。
2. 如果所讨论的状态是美学的,例如动画,那么状态最好由组件自身管理。
**/

组件管理自己的状态

有时候,组件在内部管理自己状态比较好。例如,当​​ListView​​​的内容超过渲染框时,它会自动滚动。大多数使用ListView的开发人员不想管理ListView的滚动行为,所以就让​​ListView​​本身管理其滚动偏移量。

举个例子,看下面的代码:

import 'package:flutter/material.dart';

// TapboxA manages its own state.

//------------------------- TapboxA ----------------------------------

class TapboxA extends StatefulWidget {
const TapboxA({super.key});

@override
State<TapboxA> createState() => _TapboxAState();
}

class _TapboxAState extends State<TapboxA> {
bool _active = false;

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

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

//------------------------- MyApp ----------------------------------

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Demo'),
),
body: const Center(
child: TapboxA(),
),
),
);
}
}
  • 组件​​TapboxA​​​管理自己的状态​​_active​
  • 状态​​_active​​用来控制组件的颜色
  • ​_handleTap​​​方法调用​​setState​​来更新组件的展示

父组件管理状态

通常情况下,父组件管理状态并通知其子组件何时更新是最有意义的。例如,​​IconButton​​​可以让图标看作是可点击的按钮。​​IconButton​​是一个无状态的小部件,因为我们可以让父组件知道按钮是否被点击,以便采取适当的操作。

看下面的代码:

import 'package:flutter/material.dart';

// ParentWidget manages the state for TapboxB.

//------------------------ ParentWidget --------------------------------

class ParentWidget extends StatefulWidget {
const ParentWidget({super.key});

@override
State<ParentWidget> createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;

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

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

//------------------------- TapboxB ----------------------------------

class TapboxB extends StatelessWidget {
const TapboxB({
super.key,
this.active = false,
required this.onChanged,
});

final bool active;
final ValueChanged<bool> onChanged;

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

@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleTap,
child: Container(
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: active ? Colors.lightGreen[700] : Colors.grey[600],
),
child: Center(
child: Text(
active ? 'Active' : 'Inactive',
style: const TextStyle(fontSize: 32.0, color: Colors.white),
),
),
),
);
}
}
  • 父组件定义了状态​​_active​​​用来控制组件​​TapboxB​​的展示
  • 父组件定义了​​_handleTapboxChanged​​​,当组件​​TapboxB​​​被点击的时候会更新​​_active​
  • 子组件​​TapboxB​​​接受​​active​​​属性,同时定义了​​onChanged​​​属性方法,当点击子组件​​TapboxB​​​时,会触发父组件的​​_handleTapboxChanged​​方法,通知父组件,从而实现组件的更新。

混合状态管理

对于其他的一些组件件,混合使用混合状态管理最有意义。在这个场景中,状态组件管理自己的一些状态,而父组件管理状态的其他方面。

看下面的代码:

import 'package:flutter/material.dart';

//---------------------------- ParentWidget ----------------------------

class ParentWidget extends StatefulWidget {
const ParentWidget({super.key});

@override
State<ParentWidget> createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;

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

@override
Widget build(BuildContext context) {
return SizedBox(
child: TapboxC(
active: _active,
onChanged: _handleTapboxChanged,
),
);
}
}

//----------------------------- TapboxC ------------------------------

class TapboxC extends StatefulWidget {
const TapboxC({
super.key,
this.active = false,
required this.onChanged,
});

final bool active;
final ValueChanged<bool> onChanged;

@override
State<TapboxC> createState() => _TapboxCState();
}

class _TapboxCState extends State<TapboxC> {
bool _highlight = false;

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

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

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

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

@override
Widget build(BuildContext context) {
// This example adds a green border on tap down.
// On tap up, the square changes to the opposite state.
return GestureDetector(
onTapDown: _handleTapDown, // Handle the tap events in the order that
onTapUp: _handleTapUp, // they occur: down, up, tap, cancel
onTap: _handleTap,
onTapCancel: _handleTapCancel,
child: Container(
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: widget.active ? Colors.lightGreen[700] : Colors.grey[600],
border: _highlight
? Border.all(
color: Colors.teal[700]!,
width: 10.0,
)
: null,
),
child: Center(
child: Text(widget.active ? 'Active' : 'Inactive',
style: const TextStyle(fontSize: 32.0, color: Colors.white)),
),
),
);
}
}

在这个示例中,按下时,框周围会出现一个深绿色边框。松手时,边框消失,框的颜色改变。组件​​TapboxC​​​将其活动状态导出到其父组件,但在自身内部管理其高亮状态。此示例有两个​​State​​​对象,​​_ParentWidgetState​​​和​​_TapboxCState​​。

​_ParentWidgetState​​对象:

  • 管理活动状态​​_active​​。
  • 实现了​​_handleTapboxChanged()​​方法,即在轻敲框时调用的方法。
  • 调用​​setState()​​​以在轻敲发生且​​_active​​状态更改时更新UI。

​_TapboxCState​​对象:

  • 管理自身状态​​_highlight​​。
  • ​GestureDetector​​​组件监听​​onTapDown​​和​​onTapUp​​事件。​​onTapDown​​时,它会添加高亮显示(实现为深绿色边框)。​​onTapUp​​时,它会删除高亮显示。
  • 在​​onTapDown​​和​​onTapUp​​调用​​setState()​​方法更新UI,并且​​_higlight​​状态发生变化。
  • 在​​_handleTap​​时,将状态传递到付组件中,通知父组件进行更新。

最后

在组件的状态管理中,我们使用的最多的交互场景大抵是表单相关的内容,相关的组件有:

  • Form
  • FormField
  • Checkbox
  • DropdowmButton
  • TextButton
  • FloatingActionButton
  • IconButton
  • Radio
  • ElevateButton
  • Slider
  • Switch
  • TextField
  • ...

和web开发使用的场景差不多~

我们在进行组件的封装时,本质上是在开发一个自定义的状态组件~

ps:

我正在参加年度气人作者榜的投票~
但是我没他们的票多~
我想变的更气人~
你们能帮帮我吗~
让我变的更气人~

​​掘金年度气人作者打榜~​​

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  b1UHV4WKBb2S   2023年11月13日   39   0   0 ide抗锯齿
  b1UHV4WKBb2S   2023年11月13日   33   0   0 裁剪ideflutter
  b1UHV4WKBb2S   2023年11月13日   26   0   0 flutterDart
  zSWNgACtCQuP   2023年11月13日   29   0   0 ide
x9sxU9d8g9Tj