如何在 Provider 状态变化时触发副作用(toast)

最后发布时间:2026-02-28 14:47:12 浏览量:

很好 👍 你现在其实已经进入一个更高级的问题了:

如何让 Fluttertoast.showToast() 自动监听 UpdateProvider 状态变化
而不是在 onTap 里手动判断?

这本质上是:

❓ 如何在 Provider 状态变化时触发副作用(toast)

而不是在点击时触发。


🎯 正确理念

UI 里有两种东西:

  1. UI 渲染(声明式) → 用 Consumer
  2. 副作用(toast / dialog / 跳转) → 不应该写在 build 里

Consumer 只负责 build UI
但 toast 是副作用(side effect)。

所以:

❌ 不应该在 Consumer.builder 里直接写 showToast
✅ 应该监听 provider 状态变化,然后触发副作用


🚀 最优方案:用 context.listen(推荐)

如果你用的是 Provider 6+,可以用:

context.listen<UpdateProvider>((provider) {
  if (!provider.isLoading && provider.hasChecked) {
    if (!provider.isUpdateAvailable) {
      Fluttertoast.showToast(
        msg: "已是最新版本",
        gravity: ToastGravity.TOP,
      );
    }
  }
});

但这个不能写在 build 里。


✅ 推荐结构(干净架构版)

① Provider 增加一个状态字段

bool _justChecked = false;
bool get justChecked => _justChecked;

在检查完成时:

finally {
  _isLoading = false;
  _justChecked = true;
  notifyListeners();
}

② 在页面里用 Selector 监听变化

更优雅方式:

@override
void initState() {
  super.initState();

  WidgetsBinding.instance.addPostFrameCallback((_) {
    context.read<UpdateProvider>().addListener(_updateListener);
  });
}

void _updateListener() {
  final provider = context.read<UpdateProvider>();

  if (!provider.isLoading && provider.justChecked) {
    if (!provider.isUpdateAvailable) {
      Fluttertoast.showToast(
        msg: "已是最新版本",
        gravity: ToastGravity.TOP,
      );
    }
  }
}

记得 dispose:

@override
void dispose() {
  context.read<UpdateProvider>().removeListener(_updateListener);
  super.dispose();
}

🔥 更高级 & 最优雅写法(推荐架构)

让 Provider 直接暴露一个:

String? _message;
String? get message => _message;

检查完成后:

if (!_isUpdateAvailable) {
  _message = "已是最新版本";
}
notifyListeners();

然后 UI 里:

Consumer<UpdateProvider>(
  builder: (context, update, child) {
    if (update.message != null) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        Fluttertoast.showToast(msg: update.message!);
        update.clearMessage();
      });
    }

    return ListTile(...);
  },
);

这里的关键是:

WidgetsBinding.instance.addPostFrameCallback

避免在 build 过程中直接触发副作用。


🧠 最佳实践总结

场景推荐方式
点击后立刻判断await 返回值(你现在的写法)
自动监听状态变化addListener
复杂状态流message + clearMessage 模式
更现代用 Riverpod 的 ref.listen

🎯 我的建议(最清晰版本)

你现在这个场景其实很简单:

👉 保持你现在的 await 返回值写法就够了

不要过度设计。

自动监听适用于:

  • 自动检查更新(App 启动时)
  • 后台轮询更新
  • 多个页面共享状态

如果只是手动点击一次:

await 返回值是最干净的。


如果你愿意,我可以给你画一个:

Flutter 状态管理三层架构图:

UI(声明式)

Provider(状态)

Service(业务逻辑)

理解这个后,你写 Flutter 会非常顺畅。