Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BottomNavigationBar with BLoC pattern

Tags:

flutter

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.

like image 817
Mightee Avatar asked Mar 05 '23 04:03

Mightee


1 Answers

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();
  }
}
like image 98
Mightee Avatar answered Mar 22 '23 22:03

Mightee