Getx: Persistent Bottom Bar

Created on 15 Nov 2020  路  11Comments  路  Source: jonataslaw/getx

How to use get navigator to implement a persistent bottom bar to navigate through different pages with route manager properties (transitions, get back ...).

I'm trying to use a Navigator widget, but the key is in use.

return Scaffold(
        body: Navigator(
          key: Get.key,
          initialRoute: '/',
          onGenerateRoute: (RouteSettings settings) {
            WidgetBuilder builder;
            // Manage your route names here
            switch (settings.name) {
              case '/':
                builder = (BuildContext context) => IntervalView();
                break;
              case '/page1':
                builder = (BuildContext context) => SettingsView();
                break;
              case '/page2':
                builder = (BuildContext context) => MoreView();
                break;
              default:
                throw Exception('Invalid route: ${settings.name}');
            }
            // You can also return a PageRouteBuilder and
            // define custom transitions between pages
            return MaterialPageRoute(
              builder: builder,
              settings: settings,
            );
          },
        ),
        floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
        floatingActionButton: CentralFab(),
        bottomNavigationBar: BottomNavBar());
  }

Flutter Version:
1.22

Getx Version : 3.15

Most helpful comment

@arcas0803, I am putting here a complete example of using Navigator with bottomNavigationBar to be useful for questions and other issues. In this example we have a main navigation and the bottomNavigationBar navigation:

import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(GetMaterialApp(
    debugShowCheckedModeBanner: false,
    initialRoute: '/home',
    defaultTransition: Transition.fade,
    getPages: [
      GetPage(
        name: '/home',
        page: () => HomePage(),
        binding: HomeBinding(),
      ),
      GetPage(
        name: '/another',
        page: () => AnotherPage(),
      ),
    ],
  ));
}

class HomeBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => HomeController());
  }
}

class HomeController extends GetxController {
  static HomeController get to => Get.find();

  var currentIndex = 0.obs;

  final pages = <String>['/browse', '/history', '/settings'];

  void changePage(int index) {
    currentIndex.value = index;
    Get.toNamed(pages[index], id: 1);
  }

  Route onGenerateRoute(RouteSettings settings) {
    if (settings.name == '/browse')
      return GetPageRoute(
        settings: settings,
        page: () => BrowsePage(),
        binding: BrowseBinding(),
      );

    if (settings.name == '/history')
      return GetPageRoute(
        settings: settings,
        page: () => HistoryPage(),
        binding: HistoryBinding(),
      );

    if (settings.name == '/settings')
      return GetPageRoute(
        settings: settings,
        page: () => SettingsPage(),
        binding: SettingsBinding(),
      );

    return null;
  }
}

class HomePage extends GetView<HomeController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Navigator(
        key: Get.nestedKey(1),
        initialRoute: '/browse',
        onGenerateRoute: controller.onGenerateRoute,
      ),
      bottomNavigationBar: Obx(
        () => BottomNavigationBar(
          items: const <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              icon: Icon(Icons.search),
              label: 'Browse',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.history),
              label: 'History',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.settings),
              label: 'Settings',
            ),
          ],
          currentIndex: controller.currentIndex.value,
          selectedItemColor: Colors.pink,
          onTap: controller.changePage,
        ),
      ),
    );
  }
}

class BrowsePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Browse')),
      body: Center(
        child: Container(
          child: Text(Get.find<BrowseController>().title.value),
        ),
      ),
    );
  }
}

class HistoryPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('History')),
      body: Center(
        child: Container(
          child: Text(Get.find<HistoryController>().title.value),
        ),
      ),
    );
  }
}

class SettingsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Settings')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              child: Text(Get.find<SettingsController>().title.value),
            ),
            RaisedButton(
              child: Text('Another Page'),
              onPressed: () => Get.toNamed('/another'),
            ),
          ],
        ),
      ),
    );
  }
}

class BrowseController extends GetxController {
  final title = 'Browser'.obs;
}

class HistoryController extends GetxController {
  final title = 'History'.obs;
}

class SettingsController extends GetxController {
  final title = 'Settings'.obs;
}

class BrowseBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => BrowseController());
  }
}

class HistoryBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => HistoryController());
  }
}

class SettingsBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => SettingsController());
  }
}

class AnotherPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text('Another Page')), body: Container());
  }
}

All 11 comments

Hi @arcas0803, see if this can help you:

https://github.com/jonataslaw/getx/issues/552#issuecomment-686158966

Thanks @eduardoflorence i'm working on it.
But the real problem with the example is that I can't use the properties of routes in GetX. Bindings, get back, middleware(i don't know if it works, I will discover).
What I was looking for is using the getx router system with the persistent bottom bar.

You can use bindings with GetPageRoute. See an example:

https://github.com/jonataslaw/getx/issues/298#issuecomment-650658609

If possible, add more parts of your code so that we can understand what you want and the steps to reproduce

Navigator need a NestedKey.
Never use Get.key t么 nestedNavigator.

key: nestedKey(1),

To navigator: Get.toNamed('/',id: 1)

Please read the docs and example to nestedNavigators

Ok, here is my come for the home view:

class HomeView extends GetView<HomeController> {
  final _navigatorKey = GlobalKey<NavigatorState>();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Navigator(
          key: _navigatorKey,
          initialRoute: Routes.WORKOUTS,
          onGenerateRoute: (RouteSettings settings) {
            WidgetBuilder builder;
            // Manage your route names here
            switch (settings.name) {
              case '/':
                return GetPageRoute(
                    settings: settings,
                    binding: WorkoutsBinding(),
                    page: () => WorkoutsView());
                break;
              case Routes.WORKOUTS:
                return GetPageRoute(
                    settings: settings,
                    binding: WorkoutsBinding(),
                    page: () => WorkoutsView());
                break;
              case Routes.INTERVAL:
                return GetPageRoute(
                    settings: settings,
                    binding: IntervalBinding(),
                    page: () => IntervalView());
                break;

              default:
                throw Exception('Invalid route: ${settings.name}');
            }
          },
        ),
        floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
        floatingActionButton: CentralFab(),
        bottomNavigationBar: BottomNavBar());
  }
}

It's not working, maybe because I use a the wrong Key.
I'm using GetX Pattern so I have a:
-AppPages with all getPage

  • App Routes with all my Routes

In GetMaterialApp:

GetMaterialApp(
      debugShowCheckedModeBanner: false,
      title: "Application",
      initialRoute: Routes.SPLASH,
      getPages: AppPages.routes,
      locale: localeOption(ui.window.locale),
      translations: Literals(),
      theme: appTheme,
    ),

The BottomBar just throw a Get.ToNamed().
I just don麓t know if the new navigator in the HomeView will work as the navigator on the GetMaterialApp.
By the way, thanks for the attention.

I'm going to use widgets for the pages inside the bottom bar and pages for the pages that don't need it.
You can close this.
Thanks.

@arcas0803, I am putting here a complete example of using Navigator with bottomNavigationBar to be useful for questions and other issues. In this example we have a main navigation and the bottomNavigationBar navigation:

import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(GetMaterialApp(
    debugShowCheckedModeBanner: false,
    initialRoute: '/home',
    defaultTransition: Transition.fade,
    getPages: [
      GetPage(
        name: '/home',
        page: () => HomePage(),
        binding: HomeBinding(),
      ),
      GetPage(
        name: '/another',
        page: () => AnotherPage(),
      ),
    ],
  ));
}

class HomeBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => HomeController());
  }
}

class HomeController extends GetxController {
  static HomeController get to => Get.find();

  var currentIndex = 0.obs;

  final pages = <String>['/browse', '/history', '/settings'];

  void changePage(int index) {
    currentIndex.value = index;
    Get.toNamed(pages[index], id: 1);
  }

  Route onGenerateRoute(RouteSettings settings) {
    if (settings.name == '/browse')
      return GetPageRoute(
        settings: settings,
        page: () => BrowsePage(),
        binding: BrowseBinding(),
      );

    if (settings.name == '/history')
      return GetPageRoute(
        settings: settings,
        page: () => HistoryPage(),
        binding: HistoryBinding(),
      );

    if (settings.name == '/settings')
      return GetPageRoute(
        settings: settings,
        page: () => SettingsPage(),
        binding: SettingsBinding(),
      );

    return null;
  }
}

class HomePage extends GetView<HomeController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Navigator(
        key: Get.nestedKey(1),
        initialRoute: '/browse',
        onGenerateRoute: controller.onGenerateRoute,
      ),
      bottomNavigationBar: Obx(
        () => BottomNavigationBar(
          items: const <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              icon: Icon(Icons.search),
              label: 'Browse',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.history),
              label: 'History',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.settings),
              label: 'Settings',
            ),
          ],
          currentIndex: controller.currentIndex.value,
          selectedItemColor: Colors.pink,
          onTap: controller.changePage,
        ),
      ),
    );
  }
}

class BrowsePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Browse')),
      body: Center(
        child: Container(
          child: Text(Get.find<BrowseController>().title.value),
        ),
      ),
    );
  }
}

class HistoryPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('History')),
      body: Center(
        child: Container(
          child: Text(Get.find<HistoryController>().title.value),
        ),
      ),
    );
  }
}

class SettingsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Settings')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              child: Text(Get.find<SettingsController>().title.value),
            ),
            RaisedButton(
              child: Text('Another Page'),
              onPressed: () => Get.toNamed('/another'),
            ),
          ],
        ),
      ),
    );
  }
}

class BrowseController extends GetxController {
  final title = 'Browser'.obs;
}

class HistoryController extends GetxController {
  final title = 'History'.obs;
}

class SettingsController extends GetxController {
  final title = 'Settings'.obs;
}

class BrowseBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => BrowseController());
  }
}

class HistoryBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => HistoryController());
  }
}

class SettingsBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => SettingsController());
  }
}

class AnotherPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text('Another Page')), body: Container());
  }
}

@eduardoflorence Thanks for the example. If you are gone put these example on the docs, try to implement the onBack method with:

Scaffold(
    body: WillPopScope(
      onWillPop: () async {
        if (_navigatorKey.currentState.canPop()) {
          _navigatorKey.currentState.pop();
          return false;
        }
        return true;
      },
      child: Navigator(
        // ...
      ),
    ),

@arcas0803, I am putting here a complete example of using Navigator with bottomNavigationBar to be useful for questions and other issues. In this example we have a main navigation and the bottomNavigationBar navigation:

import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(GetMaterialApp(
    debugShowCheckedModeBanner: false,
    initialRoute: '/home',
    defaultTransition: Transition.fade,
    getPages: [
      GetPage(
        name: '/home',
        page: () => HomePage(),
        binding: HomeBinding(),
      ),
      GetPage(
        name: '/another',
        page: () => AnotherPage(),
      ),
    ],
  ));
}...

@eduardoflorence Thanks for example.
Is it possible that the back button appears only in details pages (not in main pages)?
Thanks again

@eduardoflorence Thanks for example.
Is it possible that the back button appears only in details pages (not in main pages)?
Thanks again

Hi @aligitshow, use automaticallyImplyLeading property of the Appbar in each main page. See an example:

class BrowsePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Browse'), automaticallyImplyLeading: false,),   // Here
      body: Center(
        child: Container(
          child: Text(Get.find<BrowseController>().title.value),
        ),
      ),
    );
  }
}

Thanks @eduardoflorence
By using automaticallyImplyLeading property back button disappear but if you click Browse item in bottom navbar in the Browse page its navigate to the Browse page again!
My goal is bottom navbar appears only in main pages like Home, About, Settings. ... and back button appears in detail pages with best and correct way of navigation in Getx.

Was this page helpful?
0 / 5 - 0 ratings