前言
需求是这样的,bottomBar内有A,B,C,D页面,4个页面由tabBar管理显示,当在A页面点击按钮时让TabBar选中B页面展示,通俗来讲就是在两个不相邻的Widget里,一个Widget改变另一个Widget的状态。
思路:
解决方案1:
flutter内有Provider的状态管理,可以定义一个全局的Provider ,在Main函数内装载,放入顶层结构中,全局访问Provider改变Provider的状态,以此来更新Widget的状态显示
解决方案2:
使用event_bus 发送事件广播 ,A页面发送广播,tabBar监听广播并改变下标
下面是代码部分
方案1(Provider方案) 代码:
1.编写Provider类
//方案1 使用provider 跨页面改变tabBar选中的下标
class RootProvider extends ChangeNotifier {
int index = 0;
void changeIndex(int newIndex){
index = newIndex;
print("change");
notifyListeners();
}
}
- main函数注入编写好的Provider
runApp(
MultiProvider(
providers: [
// 注入全局顶层Provider
ChangeNotifierProvider(create: (_) => RootProvider()),
],
child: MyApp(),
)
);
- tabBar监听Provider改变index
//方案1 使用provider跨页面改变tabBar的index
@override
Widget build(BuildContext context) {
print("tabbar build");
return Consumer(
builder: (_,__,___){
// 获取更改的下标
int currentIndex = Provider.of(context, listen: false).index;
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: _buildItemList(),
type: BottomNavigationBarType.fixed,
currentIndex: currentIndex,
iconSize: 21.0,
elevation: 5.0,
selectedFontSize: 12,
unselectedFontSize: 12,
selectedItemColor: Color.fromRGBO(42, 130, 253, 1),
unselectedItemColor: Color.fromRGBO(142, 142, 147, 1),
onTap: (index){
// 点击方法内改变下标重新刷新
Provider.of(context,listen: false).changeIndex(index);
},
) ,
// 叠加布局,选中的index在最上面
body: IndexedStack(
index: currentIndex,
children: _pageList,
),
);
},
);
}
4.在A页面调用这个Provider 使tabBar改变选中下标
// 如A页面的点击函数内
GestureDetector(
onTap: () {
// 调用Provider选中第1个 即第2个页面(ps: 下标从0开始的哦~)
Provider.of(context,listen: false).changeIndex(1);
}
下面看一下方案2 使用event_bus发送事件广播的方式
方案2(event_bus方案) 代码:
1.编写消息对象,也就是想要发送的消息内容
import 'package:event_bus/event_bus.dart';
//Bus 初始化
EventBus eventBus = EventBus();
// tabBar切换选中页面
class EventTabBarIndex{
// 参数为int 即需要改变的下标
int index;
EventTabBarIndex(this.index);
}
2.在BottomNavigationBar所属的widget文件内监听通知 , 此Widget应是一个有状态的Widget
class _TabBarPageState extends State with RestorationMixin {
RootProvider provider = RootProvider();
@override
// init监听
void initState() {
// TODO: implement initState
super.initState();
eventBus.on().listen((event) {
provider.value = event.index;
_pageController.jumpToPage(event.index);
});
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => provider,
child:Scaffold(
bottomNavigationBar: Consumer(
builder:(_,provider,__){
return BottomNavigationBar(
items: _buildItemList(),
type: BottomNavigationBarType.fixed,
currentIndex: provider.value,
iconSize: 21.0,
elevation: 5.0,
selectedFontSize: 12,
unselectedFontSize: 12,
selectedItemColor: Color.fromRGBO(42, 130, 253, 1),
unselectedItemColor: Color.fromRGBO(142, 142, 147, 1),
onTap: (index) {
_pageController.jumpToPage(index);
} ,
);
},
),
body: PageView(
physics: const NeverScrollableScrollPhysics(), // 禁止滑动
controller: _pageController,
onPageChanged: (int index) => provider.value = index,
children: _pageList,
),
)
);
}
// 状态保活
@override
// TODO: implement restorationId
String? get restorationId => "root";
@override
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
registerForRestoration(provider, "tabBarCurrentIndex");
}
}
3.状态保活的Provider ,这里的Provider与方案1的Provider是不相等的
class RootProvider extends RestorableInt{
RootProvider():super(0);
}
总结
- 方案1 Provider注入全局,使整个全局都有了一个Provider可以在任何地方访问它,读取数据 。
- 方案2 使用event_bus发送事件与监听事件的方式,向事件总线中添加了一个事件,需要定义发送的消息内容,同时发送者发送数据,监听者监听事件作出相应。