Flutter开发
Flutter状态管理
1.UI布局
渲染逻辑是不可变的。管理状态的State的对象是。State。UI界面和状态管理不在一起。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class CountButtonPage extends StatefulWidget {
/// 构造方法传入 页面的标题
const CountButtonPage({super.key,required this.title});
/// 页面的标题
final String title;
@override
State<CountButtonPage> createState() => _CountButtonPage();
}
// 下划线 内部使用 私有的 状态管理者
class _CountButtonPage extends State<CountButtonPage> {
int _counter = 0;
void _incrementCounter() {
// 重新设置下状态 相当于reload
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blueAccent,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
所有的都在State里面管理,而且会保留count。
增量渲染 树状结构 。
构造方法的key是用来唯一定位的。
2. 状态管理
项目搭建
// 项目的搭建
/* 顶部nav 底部的tab */
import 'package:flutter/material.dart';
import 'package:wechat/pages/chat_page.dart';
import 'package:wechat/pages/discover/discover_page.dart';
import 'package:wechat/pages/friends_page.dart';
import 'package:wechat/pages/mine_page.dart';
class RootPage extends StatefulWidget {
@override
_RootPageState createState() => _RootPageState();
}
class _RootPageState extends State<RootPage> {
int _currentIndex = 2;
List<Widget> _pages = [ChatPage(), FriendsPage(), DiscoverPage(), MinePage()];
@override
Widget build(BuildContext context) {
return Scaffold(
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
selectedFontSize: 12.0,
currentIndex: _currentIndex,
fixedColor: Colors.green,
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
icon: Image.asset(
'images/tabbar_chat.png',
height: 20,
width: 20,
),
activeIcon: Image.asset(
'images/tabbar_chat_hl.png',
height: 20,
width: 20,
),
title: Text('微信'),
),
BottomNavigationBarItem(
icon: Icon(Icons.bookmark), title: Text('通讯录')),
BottomNavigationBarItem(icon: Icon(Icons.history), title: Text('发现')),
BottomNavigationBarItem(
icon: Icon(Icons.person_outline), title: Text('我')),
],
想一个问题,StatefulWidget
的状态应该被谁管理?
Widget本身?
父 Widget ?
都会?
还是另外的一个对象?
答案是取决于实际情况。常见的方法。
- Widget管理自己的状态
- Widget管理子Widget状态
- 混合管理(父和子Widget都管理状态)
如何决定使用哪种管理方法?下面是官方给出的一些原则可以帮助你做决定:
-
如果状态是用户数据,如复选框的选中状态、滑块的位置,则该状态最好由父 Widget 管理。
-
如果状态是有关界面外观效果的,例如颜色、动画,那么状态最好由 Widget 本身来管理。
-
如果某一个状态是不同 Widget 共享的则最好由它们共同的父 Widget 管理。
//------------------------ ParentWidget --------------------------------
class ParentWidget extends StatefulWidget {
const ParentWidget({super.key});
@override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;
/// 点击按钮 传入一个状态给子Widget 然后变化页面效果 虽然子Widget是无状态的但是通过父Widget可以管理他
void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
return 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),
),
),
),
);
}
}
上面的代码,父视图是有状态的Widget但是子Widget是无状态的。直接使用传值去管理无状态的Widget。具体的情况下具体的分析该谁来管理状态。
混合状态的管理,一些组件自己管理一些状态,父控件管理一些状态。
在下面 TapboxC 示例中,手指按下时,盒子的周围会出现一个深绿色的边框,抬起时,边框消失。点击完成后,盒子的颜色改变。 TapboxC 将其_active
状态导出到其父组件中,但在内部管理其_highlight
状态。这个例子有两个状态对象_ParentWidgetState
和_TapboxCState
。
_ParentWidgetStateC
类:
- 管理
_active
状态。 - 实现
_handleTapboxChanged()
,当盒子被点击时调用。 - 当点击盒子并且
_active
状态改变时调用setState()
更新UI。
_TapboxCState
对象:
-
管理
_highlight
状态。 -
GestureDetector
监听所有tap事件。当用户点下时,它添加高亮(深绿色边框);当用户释放时,会移除高亮。 -
当按下、抬起、或者取消点击时更新
_highlight
状态,调用setState()
更新UI。 -
当点击时,将状态的改变传递给父组件。
//---------------------------- ParentWidget ----------------------------
class ParentWidgetC extends StatefulWidget {
const ParentWidgetC({super.key});
@override
_ParentWidgetCState createState() => _ParentWidgetCState();
}
class _ParentWidgetCState extends State<ParentWidgetC> {
bool _active = false;
void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
return Container(
child: TapboxC(
active: _active,
// 此处回调 拿到子Widget的传递的状态值
onChanged: _handleTapboxChanged,
),
);
}
}
//----------------------------- TapboxC ------------------------------
class TapboxC extends StatefulWidget {
const TapboxC({Key? key, this.active = false, required this.onChanged})
: super(key: key);
final bool active;
final ValueChanged<bool> onChanged;
@override
_TapboxCState 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) {
// 在按下时添加绿色边框,当抬起时,取消高亮
return GestureDetector(
onTapDown: _handleTapDown, // 处理按下事件
onTapUp: _handleTapUp, // 处理抬起事件
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本身就有状态 管理他的边框的颜色的。还传递出去一个状态给父Widget。是混合管理的例子。