第一章 状态管理核心概念与实战原则
1.1 状态分类与选型策略
在Flutter中,状态分为局部状态(如按钮点击计数)和全局状态(如用户登录信息),管理方案需根据场景选择:
• 简单交互:setState
(如计数器)
• 跨组件共享:Provider
或Riverpod
(如购物车)
• 复杂业务逻辑:Bloc
或GetX
(如电商订单流)
代码示例(setState基础用法):
class CounterWidget extends StatefulWidget {
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _count = 0;
void _increment() {
setState(() => _count++); // 触发UI重建
}
@override
Widget build(BuildContext context) {
return ElevatedButton(onPressed: _increment, child: Text('$_count'));
}
}
常见问题:
• 过度重建:父组件状态变化导致所有子组件刷新(解决方案:使用const
修饰无状态组件)
• 异步回调未检查mounted
:页面销毁后仍触发setState
(解决方案:添加if (mounted)
判断)
第二章 基础方案实战:购物车与登录模块
2.1 使用Provider实现购物车状态共享
步骤分解:
- 定义数据模型:
class CartItem {
final String id;
final String name;
final double price;
CartItem(this.id, this.name, this.price);
}
class CartModel extends ChangeNotifier {
final List<CartItem> _items = [];
List<CartItem> get items => _items;
void addItem(CartItem item) {
_items.add(item);
notifyListeners(); // 通知所有Consumer
}
}
- 注入全局状态:
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CartModel(),
child: MyApp(),
),
);
}
- UI层消费状态:
Consumer<CartModel>(
builder: (context, cart, child) => ListView.builder(
itemCount: cart.items.length,
itemBuilder: (_, i) => ListTile(title: Text(cart.items[i].name)),
),
)
高频问题:
• 状态不同步:未正确使用context.read<T>()
导致数据未更新(解决方案:检查Provider
作用域)
• 性能瓶颈:列表项过多导致卡顿(优化策略:使用ListView.builder
+const
组件)
2.2 登录状态持久化实战
技术方案:
• SharedPreferences存储Token:
Future<void> saveToken(String token) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('auth_token', token);
}
• 结合Provider自动登录:
class AuthProvider extends ChangeNotifier {
String? _token;
bool get isLoggedIn => _token != null;
Future<void> autoLogin() async {
final prefs = await SharedPreferences.getInstance();
_token = prefs.getString('auth_token');
notifyListeners();
}
}
第三章 进阶方案实战:复杂列表与实时通信
3.1 使用Bloc实现动态菜单栏(参考网页3)
需求场景:
• 菜单按钮分为普通按钮、单选按钮组、开关按钮三类
• 状态需跨多个组件同步(如选中工具后画布交互变化)
实现步骤:
- 定义状态与事件:
abstract class MenuEvent {}
class SelectToolEvent extends MenuEvent {
final String toolId;
SelectToolEvent(this.toolId);
}
class MenuState {
final String selectedTool;
MenuState(this.selectedTool);
}
- Bloc逻辑处理:
class MenuBloc extends Bloc<MenuEvent, MenuState> {
MenuBloc() : super(MenuState('move'));
@override
Stream<MenuState> mapEventToState(MenuEvent event) async* {
if (event is SelectToolEvent) {
yield MenuState(event.toolId); // 更新选中状态
}
}
}
- UI层集成:
BlocBuilder<MenuBloc, MenuState>(
builder: (context, state) => Row(
children: tools.map((tool) =>
IconButton(
color: tool.id == state.selectedTool ? Colors.blue : Colors.grey,
onPressed: () => context.read<MenuBloc>().add(SelectToolEvent(tool.id)),
icon: Icon(tool.icon),
)
).toList(),
),
)
关键问题:
• 状态泄露:未在dispose
中关闭Bloc流(解决方案:使用BlocProvider
自动管理生命周期)
• 跨Bloc通信:多个Bloc需协同工作(使用BlocListener
监听其他Bloc事件)
第四章 企业级实战:电商订单流与性能优化
4.1 订单状态机设计
状态流转:
待支付
→ 已支付
→ 发货中
→ 已完成
(支持取消、退款等分支)
技术实现:
class OrderBloc extends Bloc<OrderEvent, OrderState> {
OrderBloc() : super(OrderInitial());
@override
Stream<OrderState> mapEventToState(OrderEvent event) async* {
if (event is PayOrderEvent) {
yield OrderPaid(); // 调用支付API
} else if (event is CancelOrderEvent) {
yield OrderCancelled();
}
}
}
优化策略:
• 防抖处理:防止用户重复提交订单(使用RxDart
的debounceTime
)
• 本地缓存:Hive
存储未提交订单,断网后自动重试
4.2 性能调优技巧
• 列表优化:
ListView.builder(
itemCount: 1000,
itemExtent: 80, // 固定高度提升性能
prototypeItem: ListItemPrototype(), // 预计算布局
)
• 选择性重建:
Selector<CartModel, int>(
selector: (_, cart) => cart.itemCount,
builder: (_, count, __) => Text('$count'),
)
第五章 高频问题深度解析与解决方案
5.1 状态不同步问题
• 现象:多个组件显示不一致
• 根因:直接修改状态副本而非通过Action
• 解决方案:
- 使用不可变数据模型(如
freezed
生成不可变类) - 通过
Bloc
或Redux
强制单向数据流
5.2 异步操作异常
• 典型错误:
void fetchData() async {
data = await api.getData(); // 可能已dispose
setState(() {});
}
• 正确处理:
void fetchData() {
api.getData().then((value) {
if (mounted) setState(() => data = value);
});
}
5.3 跨平台存储兼容性
• Flutter Web适配:
// 使用shared_preferences_web替代Sqflite
final prefs = await SharedPreferences.getInstance();
• 加密敏感数据:
final storage = FlutterSecureStorage();
await storage.write(key: 'token', value: 'encrypted_data');