Flutter 状态复用指北

问题背景:状态复用为什么重要

在 Flutter 中,StatefulWidget 提供了 initStatedidUpdateWidgetdisposebuild 等生命周期方法。虽然功能强大,但管理复杂的状态逻辑会导致大量重复的样板代码:

dart 复制代码
class _MyWidgetState extends State<MyWidget> with TickerProviderStateMixin {
  late AnimationController _controller;
  late TextEditingController _textController;
  late ScrollController _scrollController;
  StreamSubscription? _subscription;
  
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: widget.duration);
    _textController = TextEditingController(text: widget.initialText);
    _scrollController = ScrollController();
    _subscription = widget.stream.listen(_onData);
  }
  
  @override
  void didUpdateWidget(MyWidget old) {
    super.didUpdateWidget(old);
    if (old.duration != widget.duration) {
      _controller.duration = widget.duration;
    }
    // 更多同步逻辑...
  }
  
  @override
  void dispose() {
    _controller.dispose();
    _textController.dispose();
    _scrollController.dispose();
    _subscription?.cancel();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    // ...
  }
}

这种模式存在几个问题:

  1. 代码重复 - 每个 Widget 都需要类似的初始化/销毁配对
  2. 容易出错 - 很容易忘记清理资源
  3. 无法复用 - 逻辑和特定 Widget 耦合
  4. 难以测试 - 状态逻辑和 UI 混在一起

方案一:React Hooks (flutter_hooks)

flutter_hooks 包将 React 的 Hooks 模式带到了 Flutter:

dart 复制代码
class BookPage extends HookWidget {
  final int bookId;
  
  @override
  Widget build(BuildContext context) {
    final controller = useAnimationController(duration: Duration(seconds: 1));
    final snapshot = useFuture(fetchBook(bookId));
    
    return Text('${snapshot.data}');
  }
}

Hooks 的优势

  • ✅ 简洁且声明式
  • ✅ 自动清理资源
  • ✅ 可组合(自定义 hooks)
  • ✅ 对 React 开发者友好

Hooks 的局限

  • ❌ 需要 HookWidget 基类
  • ❌ 执行顺序很重要(不能有条件判断/循环)
  • ❌ "黑魔法" - 依赖 Element 内部机制
  • ❌ Flutter 开发者需要学习成本

方案二:LifecycleObserver 模式

state_lifecycle_observer 包采用面向对象的方式,灵感来自 Android 的 LifecycleObserver:

dart 复制代码
class _BookPageState extends State<BookPage> with LifecycleOwnerMixin {
  late AnimControllerObserver animObserver;
  late FutureObserver<Book> fetchObserver;
  
  @override
  void initState() {
    super.initState();
    animObserver = AnimControllerObserver(this, duration: () => Duration(seconds: 1));
    fetchObserver = FutureObserver(this, future: () => fetchBook(widget.bookId));
  }
  
  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Text('${fetchObserver.target.data}');
  }
}

Observer 的优势

  • ✅ 标准 StatefulWidget - 无需特殊基类
  • ✅ 无执行顺序限制
  • ✅ dispose 时自动清理
  • key 参数支持值变化时自动重建
  • ✅ 可组合(观察者可以创建嵌套观察者)
  • ✅ 低魔法 - 只是 mixin + 列表管理

Observer 的局限

  • ❌ 比 hooks 更冗长
  • ❌ 需要显式调用 super.build(context)

特性对比

特性 flutter_hooks state_lifecycle_observer
范式 函数式 面向对象
基类 必须使用 HookWidget 标准 StatefulWidget
生命周期 隐式 (useEffect) 显式 (buildTarget, onDispose)
学习曲线 中等(Hooks 规则) 低(标准 Flutter)
执行顺序 严格(不能有条件判断) 灵活
可组合性 ✅ 自定义 hooks ✅ 嵌套 observers
自动清理
基于 Key 重建 useMemoized(key:) key 参数
运行时值变化 ✅ 内置支持 ✅ 函数构建器模式

处理运行时值变化

一个常见的挑战是响应 Widget 属性的变化。两种方案都能优雅地处理:

Hooks 方式

dart 复制代码
class BookPage extends HookWidget {
  final int bookId;
  
  @override
  Widget build(BuildContext context) {
    // bookId 变化时自动重新获取
    final snapshot = useFuture(
      useMemoized(() => fetchBook(bookId), [bookId]),
    );
    return Text('${snapshot.data}');
  }
}

Observer 方式

dart 复制代码
class FetchObserver extends LifecycleObserver<AsyncSnapshot<Response>> {
  final int Function() bookId;
  
  FetchObserver(super.state, {required this.bookId}) 
      : super(key: bookId);  // 用于变化检测的 Key
  
  @override
  AsyncSnapshot<Response> buildTarget() {
    http.get(Uri.parse('https://api/books/${bookId()}')).then((response) {
      target = AsyncSnapshot.withData(ConnectionState.done, response);
      safeSetState(() {});
    });
    return const AsyncSnapshot.waiting();
  }
}

// 使用方式
class _BookPageState extends State<BookPage> with LifecycleOwnerMixin {
  FetchObserver? fetchObserver;
  
  @override
  Widget build(BuildContext context) {
    super.build(context);
    fetchObserver ??= FetchObserver(this, bookId: () => widget.bookId);
    return Text('${fetchObserver!.target.data}');
  }
}

widget.bookId 变化时:

  1. build() 触发 onBuild()
  2. onBuild() 检测到 key() 值变化
  3. onDisposeTarget() 清理旧资源
  4. buildTarget() 创建新资源

可组合性:从简单部件构建复杂状态

两种方案都支持可组合性 - 从更简单、可复用的部件构建复杂的状态逻辑。

Hooks:自定义 Hooks

dart 复制代码
AsyncSnapshot<User> useUser(int userId) {
  final snapshot = useFuture(
    useMemoized(() => fetchUser(userId), [userId]),
  );
  return snapshot;
}

// 使用
class UserPage extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final user = useUser(123);
    return Text('${user.data?.name}');
  }
}

Observer:嵌套观察者

dart 复制代码
class UserProfileObserver extends LifecycleObserver<void> {
  late final TextEditingControllerObserver nameController;
  late final FutureObserver<UserData> dataFetcher;

  UserProfileObserver(super.state, {required int Function() userId});

  @override
  void onInitState() {
    super.onInitState();
    // 子观察者通过 Zone 机制自动注册
    nameController = TextEditingControllerObserver(state);
    dataFetcher = FutureObserver(state, future: () => fetchUserData(userId()));
  }

  @override
  void buildTarget() {}
}

// 使用
class _MyPageState extends State<MyPage> with LifecycleOwnerMixin {
  late final profileObserver = UserProfileObserver(this, userId: () => widget.userId);
  
  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Column(
      children: [
        TextField(controller: profileObserver.nameController.target),
        Text('${profileObserver.dataFetcher.target.data}'),
      ],
    );
  }
}

如何选择?

选择 flutter_hooks 如果:

  • 你来自 React 背景
  • 你偏好函数式编程风格
  • 你想要最简洁的语法
  • 你能接受 "Hooks 规则"

选择 state_lifecycle_observer 如果:

  • 你偏好 OOP 和标准 Flutter 模式
  • 你需要条件性地创建观察者
  • 你想要渐进式的迁移路径
  • 你更看重显式而非隐式的行为
  • 你想避免第三方 Element 逻辑

总结

flutter_hooksstate_lifecycle_observer 都解决了同一个根本问题:让有状态逻辑可复用、可组合。它们代表了不同的哲学:

  • Hooks:函数式、简洁、魔法多
  • Observers:面向对象、显式、标准 Flutter

没有哪个是绝对"更好"的 - 根据你的团队偏好和现有代码库来选择。重要的是使用某种状态复用模式,而不是在各个 Widget 之间复制粘贴样板代码。


资源

相关推荐
花开彼岸天~3 小时前
Flutter跨平台开发鸿蒙化定位服务组件使用指南
flutter·开源·harmonyos
我是人机不吃鸭梨4 小时前
Flutter 桌面端开发终极指南(2025版):构建跨平台企业级应用的完整解决方案
开发语言·javascript·人工智能·flutter·架构
Rysxt_5 小时前
Flutter框架:重塑跨平台开发的新纪元
flutter
名字被你们想完了5 小时前
Flutter 实现一个容器内部元素可平移、缩放和旋转等功能(九)
前端·flutter
kirk_wang6 小时前
Flutter flutter_sound 库在鸿蒙平台的音频录制与播放适配实践
flutter·移动开发·跨平台·arkts·鸿蒙
花开彼岸天~21 小时前
Flutter跨平台开发鸿蒙化定位组件使用指南
flutter·华为·harmonyos
hudawei9961 天前
flutter路由传参接收时机
开发语言·flutter·异步
花开彼岸天~1 天前
Flutter跨平台开发鸿蒙化日志测试组件使用指南
flutter·elasticsearch·harmonyos
昼-枕1 天前
【实战分享】我用Flutter为小餐馆开发的点餐系统
flutter