Reputation: 1499
I would like to use a StatelessWidget with BottomNavigationBar which I would control from a BLOC. I can hook up the body of the Scaffold and onTap of the BottomNavigationBar to the BLOC (see the code). But I do not understand how one can set the currentIndex of BottomNavigationBar from BLOC (from Observable).
Is there any good solution or do I need to use StatefulWidget as in https://stackoverflow.com/a/53019841/936780 which is similar to my example.
The BLOC code:
class Bloc {
final _currentTabSink = PublishSubject<int>();
final _currentTabIndex = BehaviorSubject<int>();
Observable<int> get currentTabIndex => _currentTabIndex.stream;
Function(int) get setTabIndex => _currentTabSink.sink.add;
Bloc() {
_currentTabSink.stream.pipe(_currentTabIndex);
}
dispose() {
_currentTabSink.close();
_currentTabIndex.close();
}
}
Widget code:
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final bloc = Provider.of(context);
final List<Widget> _children = [
AaWidget(),
BbWidget(),
CcWidget(),
DdWidget(),
EeWidget()
];
return Scaffold(
appBar: AppBar(
title: Text(Localizations.of(context).appName),
),
body: setBody(_children, bloc), // hook to BLOC
bottomNavigationBar: BottomNavigationBar(
currentIndex: 1, // ?? how to hook up to BLOC ??
onTap: (index) {
bloc.setTabIndex(index); // hook to BLOC
},
items: [
addAa(context),
addBb(context),
addCc(context),
addDd(context),
addEE(context),
],
));
}
Widget setBody(List<Widget> children, Bloc bloc) {
return StreamBuilder(
stream: bloc.currentTabIndex,
builder: (context, AsyncSnapshot<int> snapshot) {
if (snapshot.hasData) {
return children[snapshot.data];
}
},
);
}
...
}
Upvotes: 3
Views: 2871
Reputation: 10871
I think you would need to wrap your Scaffold
in a StreamBuilder
with an initial data set and then you can access the snapshot data. Something like this (I had to make some changes to your code so it would build for me)
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final bloc = Bloc();
final List<Widget> _children = [
Container(color: Colors.blue, child: Center(child: Text("1"))),
Container(color: Colors.red, child: Center(child: Text("2"))),
Container(color: Colors.green, child: Center(child: Text("3"))),
Container(color: Colors.yellow, child: Center(child: Text("4"))),
Container(color: Colors.orange, child: Center(child: Text("5"))),
];
return StreamBuilder<int>(
stream: bloc.currentTabIndex,
initialData: 0,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
if (!snapshot.hasData) {
return Container();
}
return Scaffold(
appBar: AppBar(
title: Text("Test"),
),
body: _children[snapshot.data],
bottomNavigationBar: BottomNavigationBar(
currentIndex: snapshot.data,
onTap: (index) {
bloc.setTabIndex(index);
},
items: _children
.map((child) => BottomNavigationBarItem(
icon: Icon(Icons.add),
title: Text("Test"),
backgroundColor: Colors.black))
.toList(),
),
);
},
);
}
}
This works for me, the only downside here is that you will be rebuilding the AppBar
unnecessarily every time the index changes.
Upvotes: 2