封面图
我们在看电影的时候,往往只关注某个主演的角色,其实那些小角色的表演,远远比主演角色的表演要丰富~
场景
怎样才能在我们的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:
我正在参加年度气人作者榜的投票~
但是我没他们的票多~
我想变的更气人~
你们能帮帮我吗~
让我变的更气人~
掘金年度气人作者打榜~
标签:状态,142,active,State,child,组件,const,flutter From: https://blog.51cto.com/u_15531399/5982010