I really like the BLoC pattern and I am trying to understand it. But I can't seem to figure it out exactly how it should apply with the BottomNavigationBar
.
Making a list of navigation pages and setting the current index on navigation bar tap event causes the whole app to redraw because of setState()
.
Can I use the Navigator
to show the clicked navigation page without losing the navigation bar ?
Did anyone use the BLoC pattern with the BottomNavigationBar
? How do I do this ? I'd love to see a sample code.
I finally got it. I'm putting the whole code here to help others.
First read this wonderful article from didier boelens : https://www.didierboelens.com/2018/08/reactive-programming---streams---bloc/
using his bloc provider and base bloc create blocs. mine is like the following:
import 'dart:async';
import 'bloc_provider.dart';
import 'package:rxdart/rxdart.dart';
class NewsfeedBloc implements BlocBase {
BehaviorSubject<int> _ctrl = new BehaviorSubject<int>();
NewsfeedBloc(
// listen _ctrl event and do other business logic
);
void dispose() {
_ctrl.close();
}
}
then create the page that will use the bloc:
import 'package:flutter/material.dart';
import '../blocs/newsfeed_bloc.dart';
import '../blocs/bloc_provider.dart';
class NewsfeedPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final NewsfeedBloc bloc = BlocProvider.of<NewsfeedBloc>(context);
// here you should use a stream builder or such to build the ui
return Container(
child: Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.album),
title: Text('The Enchanted Nightingale'),
subtitle: Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
),
ButtonTheme.bar(
// make buttons use the appropriate styles for cards
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('BUY TICKETS'),
onPressed: () {/* do something with the bloc */},
),
FlatButton(
child: const Text('LISTEN'),
onPressed: () {/* do something with the bloc */},
),
],
),
),
],
),
),
);
}
}
and finally the main.dart file containing a navigationbottombar and a drawer:
import 'dart:async';
import 'package:flutter/material.dart';
import 'blocs/bloc_provider.dart';
import 'blocs/application_bloc.dart';
import 'blocs/newsfeed_bloc.dart';
import 'blocs/tracking_bloc.dart';
import 'blocs/notifications_bloc.dart';
import 'blocs/item1_bloc.dart';
import 'blocs/item2_bloc.dart';
import 'pages/newsfeed.dart';
import 'pages/tracking.dart';
import 'pages/notifications.dart';
import 'pages/item1.dart';
import 'pages/item2.dart';
Future<void> main() async {
return runApp(BlocProvider<ApplicationBloc>(
bloc: ApplicationBloc(),
child: MyApp(),
));
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
// define your blocs here so that you dont lose the state when your app rebuilds for some reason. thanks boformer for pointing that out.
NewsfeedBloc _newsfeedBloc;
PageController _pageController;
var _page = 0;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Movies',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: new Scaffold(
appBar: AppBar(
title: new Text('App Title'),
),
body: PageView(
children: <Widget>[
BlocProvider<NewsfeedBloc>(
bloc: _newsfeedBloc(),
child: NewsfeedPage(),
),
// ...
],
controller: _pageController,
onPageChanged: onPageChanged,
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icon(Icons.timeline),
title: Text("Timeline"),
),
BottomNavigationBarItem(
icon: Icon(Icons.art_track),
title: Text("Some Page"),
),
BottomNavigationBarItem(
icon: Icon(Icons.notifications),
title: Text("Notifications"),
),
],
onTap: navigationTapped,
currentIndex: _page,
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Settings'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Item 1'),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
return BlocProvider<Item1Bloc>(
bloc: Item1Bloc(),
child: Item1Page(),
);
}
},
),
ListTile(
title: Text('Item 2'),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
return BlocProvider<Item2Bloc>(
bloc: Item2Bloc(),
child: Item2Page(),
);
}
},
),
],
),
),
),
);
}
void navigationTapped(int page) {
_pageController.animateToPage(
page,
duration: Duration(milliseconds: 300),
curve: Curves.easeIn,
);
}
void onPageChanged(int page) {
setState(() {
this._page = page;
});
}
@override
void initState() {
super.initState();
_pageController = new PageController();
_newsfeedBloc = NewsfeedBloc();
}
@override
void dispose() {
super.dispose();
_pageController.dispose();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With