You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中ChangeNotifier通知时显示BottomSheet的正确实现方式

基于ChangeNotifierProvider实现等级提升弹窗的正确方式

你遇到的两个问题根源分别是:

  • Consumer的builder方法仅用于构建UI,在其中调用showBottomSheet会触发UI状态的同步变化,导致BuildContext异常或重复构建崩溃。
  • 直接监听ChangeNotifier的全局通知,会在任何变量更新触发notifyListeners时执行弹窗逻辑,且未重置触发变量导致重复弹出。

以下是两种可行的解决方案:

方案一:优化全局监听器逻辑

通过在页面的initState中添加监听器,同时做状态校验和变量重置,避免重复触发:

class YourPage extends StatefulWidget {
  @override
  State<YourPage> createState() => _YourPageState();
}

class _YourPageState extends State<YourPage> {
  late YourModel _model;

  @override
  void initState() {
    super.initState();
    // 获取模型实例,listen设为false避免不必要的重建
    _model = Provider.of<YourModel>(context, listen: false);
    _model.addListener(_onLevelUp);
  }

  void _onLevelUp() {
    final level = _model.leveledUpLevel;
    if (level != null) {
      // 仅在当前页面处于活跃状态时弹窗
      if (ModalRoute.of(context)?.isCurrent ?? false) {
        showBottomSheet(
          context: context,
          builder: (_) => Container(
            padding: EdgeInsets.all(16),
            child: Text("恭喜升级到$level级!"),
          ),
        );
        // 重置触发变量,防止后续notifyListeners重复触发
        _model.resetLeveledUpLevel();
      }
    }
  }

  @override
  void dispose() {
    _model.removeListener(_onLevelUp);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // 页面UI构建逻辑
    return Scaffold(body: Center(child: Text("当前页面")));
  }
}

对应的模型需要添加重置方法:

class YourModel extends ChangeNotifier {
  int? leveledUpLevel;

  void triggerLevelUp(int level) {
    leveledUpLevel = level;
    notifyListeners();
  }

  void resetLeveledUpLevel() {
    leveledUpLevel = null;
    // 无需notifyListeners,仅重置状态即可
  }
}

方案二:使用专用ValueNotifier监听单个变量

如果只需要监听leveledUpLevel的变化,在模型中新增一个ValueNotifier,实现精准监听:

class YourModel extends ChangeNotifier {
  final ValueNotifier<int?> _levelUpNotifier = ValueNotifier(null);
  
  // 暴露给页面监听的notifier
  ValueNotifier<int?> get levelUpNotifier => _levelUpNotifier;

  void triggerLevelUp(int level) {
    _levelUpNotifier.value = level;
    // 其他业务逻辑触发全局通知(如果需要)
    notifyListeners();
  }

  void resetLevelUp() {
    _levelUpNotifier.value = null;
  }
}

页面中监听这个专用的ValueNotifier:

class YourPage extends StatefulWidget {
  @override
  State<YourPage> createState() => _YourPageState();
}

class _YourPageState extends State<YourPage> {
  late YourModel _model;

  @override
  void initState() {
    super.initState();
    _model = Provider.of<YourModel>(context, listen: false);
    _model.levelUpNotifier.addListener(_onLevelUp);
  }

  void _onLevelUp() {
    final level = _model.levelUpNotifier.value;
    if (level != null && ModalRoute.of(context)?.isCurrent ?? false) {
      showBottomSheet(
        context: context,
        builder: (_) => Container(
          padding: EdgeInsets.all(16),
          child: Text("恭喜升级到$level级!"),
        ),
      );
      _model.resetLevelUp();
    }
  }

  @override
  void dispose() {
    _model.levelUpNotifier.removeListener(_onLevelUp);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(body: Center(child: Text("当前页面")));
  }
}

方案选择

  • 若仅需监听单个变量的变化,方案二更高效,避免了全局通知的干扰。
  • 若需要结合其他全局状态的变化逻辑,方案一在重置变量后也能稳定工作。

内容的提问来源于stack exchange,提问作者Patola

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 21:30:00