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

Flutter中如何在按钮onPressed回调里使用ViewModelWidget?

问题

我使用stacked包中的ViewModelWidget类,页面主体里能正常显示继承自ViewModelWidget的TextWidget,但通过按钮onPressed打开的底部弹窗中无法显示,报错提示无法访问ViewModel。

相关代码

main.dart

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: MyView(),
    );
  }
}

ViewModel代码

class MyViewModel extends BaseViewModel {
  String title = 'Hello';

  void updateTitle() {
    title = 'Bye';
    notifyListeners();
  }
}

页面与Widget代码

class MyView extends StatelessWidget {
  const MyView({super.key});

  @override
  Widget build(BuildContext context) {
    return ViewModelBuilder<MyViewModel>.reactive(
      viewModelBuilder: () => MyViewModel(),
      builder: (context, model, child) => Scaffold(
        appBar: AppBar(
          title: const Text('My View'),
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            const TextWidget(),
            ElevatedButton(
              onPressed: () {
                showModalBottomSheet<void>(
                  context: context,
                  builder: (BuildContext context) {
                    return const SizedBox(child: Center(child: TextWidget(),), height: 200,);
                  },
                );
              },
              child: const Text('Show Modal'),
            ),
          ],
        ),
      ),
    );
  }
}

class TextWidget extends ViewModelWidget<MyViewModel> {
  const TextWidget({super.key});

  @override
  Widget build(BuildContext context, MyViewModel viewModel) {
    return Text(viewModel.title);
  }
}

报错截图

报错截图

解决方案

问题核心是showModalBottomSheet会创建独立的上下文,不在原ViewModelBuilder的作用域内,导致ViewModelWidget无法找到对应的ViewModel实例。以下是几种可行解决方式:

方法1:直接传递ViewModel实例给弹窗Widget

修改TextWidget支持接收外部传入的ViewModel,再在弹窗中传递已有实例:

// 修改TextWidget
class TextWidget extends ViewModelWidget<MyViewModel> {
  final MyViewModel? viewModel;
  const TextWidget({super.key, this.viewModel});

  @override
  Widget build(BuildContext context, MyViewModel viewModel) {
    final usedViewModel = this.viewModel ?? viewModel;
    return Text(usedViewModel.title);
  }
}

// 修改弹窗代码
ElevatedButton(
  onPressed: () {
    showModalBottomSheet<void>(
      context: context,
      builder: (BuildContext context) {
        return SizedBox(
          height: 200,
          child: Center(child: TextWidget(viewModel: model)),
        );
      },
    );
  },
  child: const Text('Show Modal'),
),

方法2:弹窗内用ViewModelBuilder复用实例

如果需要弹窗Widget响应ViewModel变化,可在弹窗内用ViewModelBuilder包裹,复用已有实例:

ElevatedButton(
  onPressed: () {
    showModalBottomSheet<void>(
      context: context,
      builder: (BuildContext context) {
        return ViewModelBuilder<MyViewModel>.reactive(
          viewModelBuilder: () => model,
          builder: (context, vm, child) {
            return SizedBox(
              height: 200,
              child: Center(child: TextWidget()),
            );
          },
        );
      },
    );
  },
  child: const Text('Show Modal'),
),

方法3:用Stacked的getViewModel方法获取实例

在弹窗上下文里直接获取已有ViewModel实例:

ElevatedButton(
  onPressed: () {
    showModalBottomSheet<void>(
      context: context,
      builder: (BuildContext context) {
        final viewModel = getViewModel<MyViewModel>(context);
        return SizedBox(
          height: 200,
          child: Center(child: TextWidget(viewModel: viewModel)),
        );
      },
    );
  },
  child: const Text('Show Modal'),
),

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 17:50:22