Flutter状态
Mattakushi4322026-10-25查看 832复制 237
何时激活:Flutter状态管理、setState、ValueNotifier、ChangeNotifier、BLoC、Riverpod、Provider、状态比较
📘如何使用这个技能
- 1复制技能内容·点击上方「Copy SKILL.md」复制全部内容,或 Download 下载到本地
- 2打开你的 AI 客户端·支持 Claude Code、Trae、Cursor、ChatGPT、DeepSeek、Kimi 等任意 AI 助手
- 3粘贴为系统提示/知识文件·Claude Code:放入 .claude/skills/ 目录;普通对话:作为首条消息发送并说「请严格按这份技能文档执行」
- 4直接提出你的需求·AI 会按技能定义的工作流、格式和约束来处理,无需再解释背景
💡 提示:技能生效后,每次对话只需一句需求,AI 会按 SKILL.md 的流程执行。
SKILL.md4663 chars
---
name: flutter-state
description: "When to activate: Flutter state management, setState, ValueNotifier, ChangeNotifier, BLoC, Riverpod, Provider, state comparison"
origin: zakr
metadata:
namespace: zakr/devtools-pack
version: 1.0.0
category: engineering
language: dart
platforms: [claude-code, codex, gemini-cli, cursor, aider, windsurf]
---
# Flutter State Management
## Decision Guide
| Scope | Solution |
|-------|----------|
| Local UI state (expand/collapse, tab index) | `setState` |
| Single value shared across siblings | `ValueNotifier` + `ValueListenableBuilder` |
| Domain model shared across features | `ChangeNotifier` + `ListenableBuilder` |
| Complex app state, async, DI | Riverpod (recommended) or BLoC |
| Legacy apps | `provider` package |
## setState — Local Only
```dart
class ToggleButton extends StatefulWidget {
const ToggleButton({super.key});
@override
State<ToggleButton> createState() => _ToggleButtonState();
}
class _ToggleButtonState extends State<ToggleButton> {
bool _on = false;
@override
Widget build(BuildContext context) => Switch(
value: _on,
onChanged: (v) => setState(() => _on = v),
);
}
```
## ValueNotifier — Lightweight Observable
```dart
final counter = ValueNotifier<int>(0);
// Widget
ValueListenableBuilder<int>(
valueListenable: counter,
builder: (context, value, _) => Text('$value'),
);
// Mutate
counter.value++;
// Dispose when done (in State.dispose or Provider)
counter.dispose();
```
## ChangeNotifier — Multiple Fields
```dart
class CartNotifier extends ChangeNotifier {
final List<Item> _items = [];
List<Item> get items => List.unmodifiable(_items);
int get count => _items.length;
double get total => _items.fold(0, (s, i) => s + i.price);
void add(Item item) {
_items.add(item);
notifyListeners();
}
void remove(Item item) {
_items.remove(item);
notifyListeners();
}
}
// With provider package
ChangeNotifierProvider(create: (_) => CartNotifier()),
// Usage
ListenableBuilder(
listenable: cart,
builder: (context, _) => Text('${cart.count} items'),
);
```
## Riverpod — Recommended
```dart
// Define
@riverpod
class Cart extends _$Cart {
@override
List<Item> build() => [];
void add(Item item) => state = [...state, item];
void remove(Item item) => state = state.where((i) => i.id != item.id).toList();
}
// Consume
class CartIcon extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(cartProvider).length;
return Badge(label: Text('$count'), child: const Icon(Icons.shopping_cart));
}
}
```
## BLoC — Event-Driven
```dart
// Events
sealed class CartEvent {}
class AddItemEvent extends CartEvent { final Item item; AddItemEvent(this.item); }
class RemoveItemEvent extends CartEvent { final Item item; RemoveItemEvent(this.item); }
// State
class CartState { final List<Item> items; const CartState(this.items); }
// BLoC
class CartBloc extends Bloc<CartEvent, CartState> {
CartBloc() : super(const CartState([])) {
on<AddItemEvent>((event, emit) {
emit(CartState([...state.items, event.item]));
});
on<RemoveItemEvent>((event, emit) {
emit(CartState(state.items.where((i) => i.id != event.item.id).toList()));
});
}
}
// Widget
BlocBuilder<CartBloc, CartState>(
builder: (context, state) => Text('${state.items.length} items'),
);
context.read<CartBloc>().add(AddItemEvent(item));
```
## Inherited State Anti-Patterns
```dart
// BAD: Lifting state too high unnecessarily
class AppState extends ChangeNotifier {
bool isMenuOpen = false; // local nav state doesn't belong at app level
}
// BAD: Calling setState after async without checking mounted
void _load() async {
final data = await fetch();
setState(() => _data = data); // may crash if widget is disposed
}
// GOOD:
void _load() async {
final data = await fetch();
if (!mounted) return;
setState(() => _data = data);
}
```
## Reactive Streams
```dart
// StreamBuilder for live data
StreamBuilder<List<Message>>(
stream: chatService.messages,
builder: (context, snapshot) {
if (snapshot.hasError) return Text('Error: ${snapshot.error}');
if (!snapshot.hasData) return const CircularProgressIndicator();
return MessageList(messages: snapshot.data!);
},
);
// FutureBuilder
FutureBuilder<User>(
future: userService.fetchCurrentUser(),
builder: (context, snapshot) => switch (snapshot.connectionState) {
ConnectionState.waiting => const CircularProgressIndicator(),
ConnectionState.done when snapshot.hasData => UserCard(user: snapshot.data!),
_ => const Text('Error'),
},
);
```