Navigator.push不在作用域时,如何调用setState刷新页面?
解决从SettingsScreen返回后刷新MainScreen的问题
你的核心问题是:自定义AppBar里无法直接调用MainScreen的setState,因为两者属于不同的类。下面给你两种简单可行的解决方案:
方案一:给PlantAppBar添加返回回调
通过给自定义AppBar传入一个回调函数,当从SettingsScreen返回时触发这个回调,让MainScreen自己处理刷新逻辑。
步骤1:修改PlantAppBar代码
添加回调参数,并在跳转返回后执行回调:
class PlantAppBar extends StatelessWidget implements PreferredSizeWidget { @override final Size preferredSize = Size.fromHeight(50); final String title = 'Pflanzen'; final bool showSettingsButton; final VoidCallback? onSettingsReturn; // 新增返回回调参数 PlantAppBar(this.showSettingsButton, {this.onSettingsReturn, super.key}); @override Widget build(BuildContext context) { return AppBar( actions: [if(showSettingsButton) settingsButton(context)], // 修正原代码的语法错误 automaticallyImplyLeading: false, title: Text(title, style: TextStyle(color: Theme.of(context).colorScheme.onPrimary)), backgroundColor: Theme.of(context).colorScheme.primary, toolbarTextStyle: TextStyle(color: Theme.of(context).colorScheme.onPrimary), ); } Widget settingsButton(context){ return IconButton( onPressed: (){navigateToSettings(context);}, icon: Icon(Icons.more_vert, color: Theme.of(context).colorScheme.onPrimary,) ); } void navigateToSettings(context){ Navigator.push( context, MaterialPageRoute(builder: (context) => Settingsscreen()), ).then((_) { // 返回后执行回调 onSettingsReturn?.call(); }); } }
步骤2:在MainScreen中传入回调
创建PlantAppBar时,传入刷新逻辑的回调:
@override Widget build(BuildContext context){ return Scaffold( appBar: PlantAppBar( true, onSettingsReturn: () { // 重新获取数据,触发StreamBuilder更新UI setState(() { mainVM.fetchPlants(); }); }, ), backgroundColor: Theme.of(context).colorScheme.surface, body: StreamBuilder<List<Plant>>( stream: mainVM.plantStream, builder: (context, snapshot) { if (snapshot.hasError){ return Text("Error: ${snapshot.error}", style: TextStyle(color: Colors.red)); }else if (snapshot.hasData){ plants = snapshot.data!; return _buildListView(); }else{ return Center(child: Text("Nutze den (+) Button um deine erste Pflanze anzulegen")); } }), ); }
方案二:把跳转逻辑移到MainScreen中
让AppBar的按钮只负责触发MainScreen里的跳转方法,这样就能直接在MainScreen的State中处理返回后的刷新。
步骤1:修改PlantAppBar代码
将跳转逻辑替换为回调,让MainScreen处理:
class PlantAppBar extends StatelessWidget implements PreferredSizeWidget { @override final Size preferredSize = Size.fromHeight(50); final String title = 'Pflanzen'; final bool showSettingsButton; final VoidCallback? onSettingsPressed; // 新增点击回调 PlantAppBar(this.showSettingsButton, {this.onSettingsPressed, super.key}); @override Widget build(BuildContext context) { return AppBar( actions: [if(showSettingsButton) settingsButton(context)], automaticallyImplyLeading: false, title: Text(title, style: TextStyle(color: Theme.of(context).colorScheme.onPrimary)), backgroundColor: Theme.of(context).colorScheme.primary, toolbarTextStyle: TextStyle(color: Theme.of(context).colorScheme.onPrimary), ); } Widget settingsButton(context){ return IconButton( onPressed: onSettingsPressed, // 直接触发传入的回调 icon: Icon(Icons.more_vert, color: Theme.of(context).colorScheme.onPrimary,) ); } }
步骤2:在MainScreen中处理跳转和刷新
@override Widget build(BuildContext context){ return Scaffold( appBar: PlantAppBar( true, onSettingsPressed: () async { // 等待从SettingsScreen返回 await Navigator.push( context, MaterialPageRoute(builder: (context) => Settingsscreen()), ); // 返回后刷新数据 setState(() { mainVM.fetchPlants(); }); }, ), backgroundColor: Theme.of(context).colorScheme.surface, body: StreamBuilder<List<Plant>>( stream: mainVM.plantStream, builder: (context, snapshot) { if (snapshot.hasError){ return Text("Error: ${snapshot.error}", style: TextStyle(color: Colors.red)); }else if (snapshot.hasData){ plants = snapshot.data!; return _buildListView(); }else{ return Center(child: Text("Nutze den (+) Button um deine erste Pflanze anzulegen")); } }), ); }
补充说明
两种方案本质都是把刷新逻辑交回给MainScreen的State类,因为只有State类能调用setState。另外你的UI是通过StreamBuilder构建的,只要mainVM.fetchPlants()更新了plantStream,StreamBuilder就会自动重建UI,所以setState在这里主要是确保Widget状态更新,核心还是重新获取数据。
内容的提问来源于stack exchange,提问作者BinaryFox
相关产品推荐
相关产品推荐

