Flutter navigationBar with Riverpod

4.3k Views Asked by At

I try to manage my states but I can't really do it. I would like to learn how to create a navigation bar at the bottom of the manage page with the riverpod package.

I manage to manage the page on which we click but I do not see how to then return the correct pager according to the selected button

Main.dart :

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:app/ui/screens/my_app/my_app_screen.dart';

void main() {
  runApp(
    const ProviderScope(child: MyApp()),
  );
}

NavigationBarScreen.dart:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

final provider = StateNotifierProvider((ref) => NavigationNotifier());

class NavigationBarScreen extends HookWidget
{
  @override
  Widget build(BuildContext context) {

    return SafeArea(
      child: Scaffold(
        body : Container(
              margin: EdgeInsets.only(left : 8, right : 8, bottom: 8),
              decoration: BoxDecoration(
                borderRadius: BorderRadius.only(
                    topRight: Radius.circular(20), topLeft: Radius.circular(20)),
                boxShadow: [
                  BoxShadow(color: AppColors.colorShadowLight, spreadRadius: 0, blurRadius: 10),
                ],
              ),
              child: ClipRRect(
                borderRadius: BorderRadius.circular(50.0),
                child: BottomNavigationBar(
                  type: BottomNavigationBarType.fixed,
                  backgroundColor: AppColors.colorBgDark,
                  fixedColor: AppColors.colorContrastOrange,
                  unselectedItemColor: AppColors.colorFontLight2,
                  currentIndex: 0,
                  showSelectedLabels: false,
                  showUnselectedLabels: false,
                  onTap: context.read(provider).selectPage,
                  items: [
                    BottomNavigationBarItem(
                      icon: Icon(Icons.home),
                      title: Text('Home'),
                    ),
                    BottomNavigationBarItem(
                      icon: Icon(Icons.settings),
                      title: Text('Settings'),
                    ),
                  ],
                ),
              ),
            ),
      ),
    );
  }
}

NavigationNotifier.dart:

import 'package:hooks_riverpod/hooks_riverpod.dart';

enum NavigationBarEvent { HOME, PROFIL}

class NavigationNotifier extends StateNotifier<PageModel> {
  NavigationNotifier() : super(defaultPage);

  static const defaultPage = PageModel(NavigationBarEvent.HOME);

  void selectPage(int i){
    switch (i) {
      case 0:
        state = PageModel(NavigationBarEvent.HOME);
        break;
      case 1:
        state = PageModel(NavigationBarEvent.PROFIL);
        break;
    }
  }
}

class PageModel {
  const PageModel(this.page);
  final NavigationBarEvent page;
}
3

There are 3 best solutions below

0
On BEST ANSWER

It appears that you have implemented all the logic for modifying the state when the user clicks on the bottom bar.

The only remaining bit is to listen to the state (likely in your Scaffold), and decide which page to show.

A possibility is to write:

class NavigationBarScreen extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final pageModel = useProvider(provider.state);

    Widget body;
    switch (pageModel.page) {
      case NavigationBarEvent.HOME:
        body = Home();
        break;
      case NavigationBarEvent.PROFIL:
        body: Profile();
        break;
    }

    return Scaffold(
      body: body,
      bottomBar: ...
    );
  }
} 

1
On

Updated Jan 27, 2022. https://pub.dev/packages/flutter_riverpod/versions/1.0.3

This is another approach that worked for me, if you are looking to control "pageview + bottom navigation bar" on drag as well:

class Index extends StateNotifier<int> {
  Index() : super(0);
  set value(int index) => state = index;
}

final indexProvider = StateNotifierProvider((ref) => Index());

class Home extends ConsumerWidget {
  static const route = '/home';
  const Home({Key? key}) : super(key: key);

  final List<Widget> fragments = const [
    Text('Page 1'),
    Text('Page 2'),
    Text('Page 3')
  ];

  final List<BottomNavigationBarItem> navItems = const [
    BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
    BottomNavigationBarItem(icon: Icon(Icons.camera), label: 'Camera'),
    BottomNavigationBarItem(icon: Icon(Icons.edit), label: 'Edit'),
  ];

  final Text title = const Text('My App');

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final PageController controller = PageController();
    final int menuIndex = ref.watch(indexProvider) as int;
    return Scaffold(
      appBar: AppBar(title: title, actions: [
        IconButton(onPressed: () {}, icon: const Icon(Icons.list))
      ]),
      body: PageView(
          controller: controller,
          children: fragments,
          onPageChanged: (i) => ref.read(indexProvider.notifier).value = i),
      bottomNavigationBar: BottomNavigationBar(
          items: navItems,
          currentIndex: menuIndex,
          onTap: (i) {
            ref.read(indexProvider.notifier).value = i;
            controller.animateToPage(i,
                duration: const Duration(seconds: 1), curve: Curves.easeInOut);
          }),
    );
  }
}
0
On

With Riverpod 2.0+

final indexProvider = NotifierProvider<IndexNotifier, int>(
  IndexNotifier.new,
);

class IndexNotifier extends Notifier<int> {
  IndexNotifier() : super();

  set value(int index) => state = index;

  @override
  int build() {
    return 0; // default index
  }
}


class MyScaffold extends ConsumerWidget {
 MyScaffold({super.key});

  List<String> routes = [
    AppRoutes.media,
    AppRoutes.home];

 void onItemTapped(int index, BuildContext context, WidgetRef ref) {
    if (kDebugMode) {
      print("index of menu is : $index");
    }
    navIndex = index;
    ref.read(indexProvider.notifier).value = index;
    Navigator.pushNamed(context, routes[index]); 
// or using if (index == 0) Navigator.push(context,MaterialPageRoute(builder: (context) =>const SecondRoute())); etc
  }

[...]

  @override
  Widget build(BuildContext context, WidgetRef ref) {

  return Scaffold(bottomNavigationBar: BottomNavigationBar(
              currentIndex: ref.watch(indexProvider),
              onTap: (index) => onItemTapped(index, context, ref),
[...]