如何在外部无状态组件中控制BottomNavigationBar导航
你的核心问题在于BottomNavigationViewModel的实例不统一:在BottomNavigationView中,你每次build时都会创建一个新的BottomNavigationViewModel()实例,而ProfileViewModel里通过locator获取的是另一个单例实例。这两个实例完全独立,所以当你在Profile页面调用_bottomNavigationBar.changeView(1)时,修改的是locator里的实例的currentIndex,但UI监听的却是BottomNavigationView里新建的那个实例,自然不会触发页面切换。
而你用IndexedStack的选择是正确的,它能保留所有子页面的状态,不会像StreamBuilder配合switch那样销毁页面,完全符合你的需求。
修复步骤
1. 修改BottomNavigationView的ViewModel获取方式
不要在ViewModelProvider中新建实例,而是使用locator获取已经注册的单例实例:
class BottomNavigationView extends StatelessWidget { @override Widget build(BuildContext context) { // 改为获取locator中的单例,而非新建实例 return ViewModelProvider<BottomNavigationViewModel>.withConsumer( viewModel: locator<BottomNavigationViewModel>(), builder: (context, model, child) => Scaffold( primary: false, body: IndexedStack( children: <Widget>[ BrowseView(), HomeView(), ProfileView(), ], index: model.currentIndex, ), bottomNavigationBar: BottomNavigationBar( type: BottomNavigationBarType.fixed, backgroundColor: Colors.white, currentIndex: model.currentIndex, onTap: model.changeView, items: [ BottomNavigationBarItem( icon: new Icon(Icons.search), title: SizedBox.shrink(), ), BottomNavigationBarItem( icon: new Icon(Icons.home), title: SizedBox.shrink(), ), BottomNavigationBarItem( icon: new Icon(WineIcon.person), title: SizedBox.shrink(), ), ], ), ), ); } }
2. 确保BottomNavigationViewModel已注册为单例
在你的依赖注入初始化代码中(比如locator_setup.dart),要把BottomNavigationViewModel注册为单例,这样locator每次获取的都是同一个实例:
// 示例:get_it的注册代码 final locator = GetIt.instance; void setupLocator() { locator.registerSingleton<BottomNavigationViewModel>(BottomNavigationViewModel()); // 其他ViewModel的注册... locator.registerFactory<ProfileViewModel>(() => ProfileViewModel()); }
3. 保留ProfileViewModel的现有代码
你的ProfileViewModel代码是正确的,它已经通过locator获取了正确的单例实例,调用changeView就能修改统一的currentIndex,进而触发UI更新:
class ProfileViewModel extends ChangeNotifier { final BottomNavigationViewModel _bottomNavigationBar = locator<BottomNavigationViewModel>(); void goHome() { _bottomNavigationBar.changeView(1); } }
为什么这样能解决问题?
修改后,BottomNavigationView监听的BottomNavigationViewModel实例和ProfileViewModel中调用的实例是同一个,当你在Profile页面点击按钮调用changeView时,notifyListeners()会通知UI更新IndexedStack的index,从而切换到Home页面,同时所有子页面的状态都会被IndexedStack保留,不会被销毁。
内容的提问来源于stack exchange,提问作者Chun Lin

