skip to Main Content

I have 4 pages in a GNav() Bottom-Navigation-Bar and I would like to navigate to different page via click of a button.
I am aware that there might be similar questions on stackoverflow already, however I was unable to implement any of them successfully which is why I am posting here as my "last effort".

First off, my Class RootPage() which is where I am defining my GNav bar and the pages linked to each tab.

import 'package:anitan/pages/menu_bar_pages/profile_page.dart';
import 'package:anitan/pages/menu_bar_pages/search_page.dart';
import 'package:anitan/pages/menu_bar_pages/settings_pages/settings_page.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:google_nav_bar/google_nav_bar.dart';
import 'home_page.dart';

class RootPage extends StatefulWidget {
  RootPage({super.key});

  @override
  State<RootPage> createState() => _RootPageState();
}

class _RootPageState extends State<RootPage>{
  int currentPage = 0;

  List<Widget> pages = const [
    HomePage(),
    ProfilePage(),
    SearchPage(),
    SettingsPage()
  ];

  final user = FirebaseAuth.instance.currentUser!;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
          body: pages[currentPage],
          bottomNavigationBar: Container(
            color: Colors.green,
            child: Padding(
              padding:
                  const EdgeInsets.symmetric(horizontal: 15.0, vertical: 15),
              child: GNav(
                backgroundColor: Colors.green,
                color: Colors.white,
                activeColor: Colors.white,
                tabBackgroundColor: Colors.green.shade800,
                gap: 8,
                onTabChange: (index) => setState(() => currentPage = index),
                selectedIndex: currentPage,
                padding: const EdgeInsets.all(16),
                tabs: const [
                  GButton(icon: Icons.home, text: "Home"),
                  GButton(icon: Icons.person, text: "My Page"),
                  GButton(icon: Icons.search, text: "Browse"),
                  GButton(icon: Icons.settings, text: "Settings"),
                ],
              ),
            ),
          ),
    );
  }
}

Next up, the HomePage which has a button. When clicked, I want to change the selected tab of my GNav bar. Pushing the page etc. will lead to the Navigation Bar disappearing which is why this isn’t an option.

import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key,});
 

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: const Text("Home Page"),
        automaticallyImplyLeading: false,
        leading: IconButton(
          onPressed: () {
            Navigator.of(context).pop();
          },
          icon: const Icon(Icons.arrow_back_ios),
        ),
        actions: [
          IconButton(
            onPressed: () {
              debugPrint("appBar Button");
            },
            icon: const Icon(Icons.info_outline),
          ),
        ],
      ),
      body: SafeArea(
        child: ElevatedButton(onPressed: (() => print("Change Page Here")), child: Text("Change-Page")),
      ),
    );
  }
}

I tried a few things like implementing a StreamController or using a GlobalKey. But I would like to the GNav Bar instead of using a Tab controller because of its design.

In theory, what I am trying to do seems simple:
I would like to currentPage to a new index and call setState() to show the changes.

Can anyone help me understand how I can access change the index and update the selected page in my RootPage?
I have spent 2 days looking into various solutions but I can’t get any of them to work.

Many thanks!

Edit:
Please find the code of my main.dart below.

import 'package:anitan/pages/menu_bar_pages/root_page.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
//import 'firebase_options.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        debugShowCheckedModeBanner: false,
        theme: ThemeData(primarySwatch: Colors.green),
        home: RootPage(),
        );
  }
}

2

Answers


  1. Chosen as BEST ANSWER

    Given the reply from Yeasin Sheikh I implemented a little workaround which seems to be working fine:

    First, as recommended by Yeasin, I added a VoidCallback function onTap to my HomePage class:

    class HomePage extends StatefulWidget {
      final VoidCallback onTap;
      const HomePage({super.key, required this.onTap});
    
      @override
      State<HomePage> createState() => _HomePageState();
    }
    

    Then I called this onTap function clicking the button:

    class _HomePageState extends State<HomePage> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: SafeArea(
            child:
                ElevatedButton(onPressed: widget.onTap(), child: Text("Change-Page")),
          ),
        );
      }
    }
    

    In my rootPage class, I then created my HomePage. When the onTap function of that page is being called, I am calling a method to refresh the page (setState) and changing the currentPage index as part of it. This required me to initialize the List of my pages using "late".

    class _RootPageState extends State<RootPage>{
      int currentPage = 0;
    
      late List<Widget> pages = [
        HomePage(onTap:(){  
          _changePage(2);
        } ),
        const ProfilePage(),
        const SearchPage(),
        const SettingsPage()
      ];
    
      _changePage(int id){
        setState(() {
          currentPage = id;
        });
      }
    

    To keep everything centralized, I switched to using the same method to update the index in the onTabChange part of the GNav bar as well.

    onTabChange: (index) => setState(() => _changePage(index)),
    

    Clicking the button now switches the page. The bottom navigation bar (GNav) will stay on the screen and can be used as usual.

    Thank you for your support in figuring this out!


  2. HomePage is already a part of RootPage which is needed to be on the same route.You can use callback method to handle click-event/changing page.

    class HomePage extends StatefulWidget {
      final VoidCallback onTap;
      const HomePage({
        super.key,
        required this.onTap,
      });
    
      @override
      State<HomePage> createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: SafeArea(
            child:
                ElevatedButton(onPressed: widget.onTap, child: Text("Change-Page")),
          ),
        );
      }
    }
    

    Now you will get a callback method while creating HomePage

     int currentPage = 0;
    
      List<Widget> pages = [
        HomePage(onTap:(){  
           // page 
        } ),
    
      ];
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search