Flutter状态

Mattakushi4322026-10-25查看 832复制 237

何时激活:Flutter状态管理、setState、ValueNotifier、ChangeNotifier、BLoC、Riverpod、Provider、状态比较

Download (.md)

📘如何使用这个技能

  1. 1
    复制技能内容·点击上方「Copy SKILL.md」复制全部内容,或 Download 下载到本地
  2. 2
    打开你的 AI 客户端·支持 Claude Code、Trae、Cursor、ChatGPT、DeepSeek、Kimi 等任意 AI 助手
  3. 3
    粘贴为系统提示/知识文件·Claude Code:放入 .claude/skills/ 目录;普通对话:作为首条消息发送并说「请严格按这份技能文档执行」
  4. 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'),
  },
);
```
来源:skillsdirectory

评论0

登录后参与评论

还没有评论,来抢沙发吧