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
相关产品推荐
相关产品推荐

