RAF Algowid
RAF Algowid

Reputation: 107

How do i push build context onTap on BottomNavigationBar i want to show changes when i navigate to any page from BottomNavigationBar

I used BottomNavigationBar for bottom bar which changes the current index page but I have some issue with the navigation. When I change the image on any screen, the changes show on inner pages but not show on that pages with navigate through BottomNavigationBar and I think that's because it only changes the current state, and just pops the widget

But I need to load build context of that page which navigate through BottomNavigationBar, so please help with this

Here is my code:-

Bottom Bar Class

class PersistentBottomBarScaffold extends StatefulWidget {
/// pass the required items for the tabs and BottomNavigationBar
final List<PersistentTabItem> items;

const PersistentBottomBarScaffold({Key? key, required this.items})
  : super(key: key);

@override
_PersistentBottomBarScaffoldState createState() =>
  _PersistentBottomBarScaffoldState();
}

class _PersistentBottomBarScaffoldState
extends State<PersistentBottomBarScaffold> {
int _selectedTab = 0;

var ctime;

@override
Widget build(BuildContext context) {

return WillPopScope(
  onWillPop: () async {
    /// Check if curent tab can be popped
    if (widget.items[_selectedTab].navigatorkey?.currentState?.canPop() ??
        false) {
      widget.items[_selectedTab].navigatorkey?.currentState?.pop();
      return false;
    }
    else {
      // if current tab can't be popped then use the root navigator
      return false;
    }
  },
  child: Scaffold(
    body: IndexedStack(
      index: _selectedTab,
      children: widget.items
          .map((page) => Navigator(
        key: page.navigatorkey,
        onGenerateInitialRoutes: (navigator, initialRoute) {
          return [
            MaterialPageRoute(builder: (context) => page.tab)
          ];
        },
      ))
          .toList(),
    ),
    bottomNavigationBar: BottomNavigationBar(
          backgroundColor: white,
          currentIndex: _selectedTab,
          selectedItemColor: Colors.pink,
          unselectedItemColor: Colors.purple,
          unselectedLabelStyle: TextStyle(color: Colors.purple),
          type: BottomNavigationBarType.fixed,
          selectedIconTheme: IconThemeData(color: Colors.pink),
          onTap: (index) {
            if (index == _selectedTab) {

              widget.items[index].navigatorkey?.currentState
                  ?.popUntil((route) => route.isFirst);
            } else {
              setState(() {
                _selectedTab = index;
              });
              //print(_selectedTab);
            }
          },
          items: widget.items
              .map((item) {
                return
                  BottomNavigationBarItem(
                      icon: Icon(item.icon),
                      label: item.title
                  );
              }
              ).toList(),
        ),
  ),
 );
 }
 }

 /// Model class that holds the tab info for the [PersistentBottomBarScaffold]
 class PersistentTabItem {
 final Widget tab;
 final GlobalKey<NavigatorState>? navigatorkey;
 final String title;
 final IconData icon;

 PersistentTabItem(
  {required this.tab,
    this.navigatorkey,
    required this.title,
    required this.icon,
  });
 }

Main Navigator page

class MainNavigator extends StatefulWidget {

MainNavigator({Key? key,}) : super(key: key);

@override
_MainNavigator createState() => _MainNavigator();
}

class _MainNavigator extends State<MainNavigator>{

final _tab1navigatorKey = GlobalKey<NavigatorState>();
final _tab2navigatorKey = GlobalKey<NavigatorState>();
final _tab3navigatorKey = GlobalKey<NavigatorState>();

int _selectedIndex = 0;


@override
Widget build(BuildContext context) {

return PersistentBottomBarScaffold(
  items: [
    PersistentTabItem(
        tab: PageOne(),
        icon: Icons.home,
        title: 'home',
        navigatorkey: _tab1navigatorKey,
    ),
    PersistentTabItem(
      tab: PageTwo(),
      icon: Icons.currency_rupee,
      title: 'expense',
      navigatorkey: _tab2navigatorKey,
    ),
    PersistentTabItem(
      tab: PageThree(),
      icon: Icons.person,
      title: 'staff',
      navigatorkey: _tab3navigatorKey,
    ),
  ],
);
}
}

My widget Pages

Page One

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

@override
Widget build(BuildContext context) {
return Scaffold(
  body: Container(
    child: Center(
       child: Text('PageOne'),
     ),
  ),
);
}
}

Page Two

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

@override
Widget build(BuildContext context) {
return Scaffold(
  body: Container(
    child: Center(
       child: Text('PageTwo'),
     ),
  ),
);
}
}

Page Three

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

@override
Widget build(BuildContext context) {
return Scaffold(
  body: Container(
    child: Center(
       child: Text('PageThree'),
     ),
  ),
);
}
}

I Added My Whole Code Please understand this code on PersistentBottomBarScaffold class i add BottomNavigationBar and on onTap it pop and just change the current state of widget i want when it change the widget it may also load the their build conext so that i chnage something on any page or class One Two Three they show me the changes

Please if any one understand it help me with this Thank You

Upvotes: 1

Views: 305

Answers (1)

nramirez
nramirez

Reputation: 5620

This might be happening because your code is using Stateless widgets instead of stateful widgets.

Try the following:

  • Update PageOne, PageTwo PageThree to stateful widgets https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html
  • Modify your PersistentTabItem class to store the StatefulWidget instead of the Widget. final StatefulWidget tab;
  • Modify your IndexedStack to use the StatefulWidget instead of the Widget
  • Modify your BottomNavigationBar to use the onTap method to change the _selectedTab variable and trigger a state update

With these modifications, the state of each tab will be preserved even if you navigate to another tab and come back.

Here's the full code:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData.dark(),
      home: const MainNavigator(),
    );
  }
}

class MainNavigator extends StatefulWidget {
  const MainNavigator({
    Key? key,
  }) : super(key: key);

  @override
  _MainNavigator createState() => _MainNavigator();
}

class _MainNavigator extends State<MainNavigator> {
  final _tab1navigatorKey = GlobalKey<NavigatorState>();
  final _tab2navigatorKey = GlobalKey<NavigatorState>();
  final _tab3navigatorKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    return PersistentBottomBarScaffold(
      items: [
        PersistentTabItem(
          tab: const PageOne(),
          icon: Icons.home,
          title: 'home',
          navigatorkey: _tab1navigatorKey,
        ),
        PersistentTabItem(
          tab: const PageTwo(),
          icon: Icons.currency_rupee,
          title: 'expense',
          navigatorkey: _tab2navigatorKey,
        ),
        PersistentTabItem(
          tab: const PageThree(),
          icon: Icons.person,
          title: 'staff',
          navigatorkey: _tab3navigatorKey,
        ),
      ],
    );
  }
}

class PersistentBottomBarScaffold extends StatefulWidget {
  /// pass the required items for the tabs and BottomNavigationBar
  final List<PersistentTabItem> items;

  const PersistentBottomBarScaffold({Key? key, required this.items})
      : super(key: key);

  @override
  _PersistentBottomBarScaffoldState createState() =>
      _PersistentBottomBarScaffoldState();
}

class _PersistentBottomBarScaffoldState
    extends State<PersistentBottomBarScaffold> {
  int _selectedTab = 0;

  var ctime;

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        /// Check if curent tab can be popped
        if (widget.items[_selectedTab].navigatorkey?.currentState?.canPop() ??
            false) {
          widget.items[_selectedTab].navigatorkey?.currentState?.pop();
          return false;
        } else {
          // if current tab can't be popped then use the root navigator
          return false;
        }
      },
      child: Scaffold(
        body: IndexedStack(
          index: _selectedTab,
          children: widget.items.map((page) => page.tab).toList(),
        ),
        bottomNavigationBar: BottomNavigationBar(
          backgroundColor: Colors.white,
          currentIndex: _selectedTab,
          selectedItemColor: Colors.pink,
          unselectedItemColor: Colors.purple,
          unselectedLabelStyle: const TextStyle(color: Colors.purple),
          type: BottomNavigationBarType.fixed,
          selectedIconTheme: const IconThemeData(color: Colors.pink),
          onTap: (index) {
            setState(() {
              _selectedTab = index;
            });
          },
          items: widget.items.map((item) {
            return BottomNavigationBarItem(
                icon: Icon(item.icon), label: item.title);
          }).toList(),
        ),
      ),
    );
  }
}

/// Model class that holds the tab info for the [PersistentBottomBarScaffold]
class PersistentTabItem {
  final StatefulWidget tab;
  final GlobalKey<NavigatorState>? navigatorkey;
  final String title;
  final IconData icon;

  PersistentTabItem({
    required this.tab,
    this.navigatorkey,
    required this.title,
    required this.icon,
  });
}

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

  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}

class PageOne extends StatefulWidget {
  const PageOne({Key? key}) : super(key: key);

  @override
  State<PageOne> createState() => _PageOneState();
}

class _PageOneState extends State<PageOne> {
  // Add your state variables and methods here
  // ...

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Text('PageOne'),
      ),
    );
  }
}

class PageTwo extends StatefulWidget {
  const PageTwo({Key? key}) : super(key: key);

  @override
  State<PageTwo> createState() => _PageTwoState();
}

class _PageTwoState extends State<PageTwo> {
  // Add your state variables and methods here
  // ...

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Text('PageTwo'),
      ),
    );
  }
}

class PageThree extends StatefulWidget {
  const PageThree({Key? key}) : super(key: key);

  @override
  State<PageThree> createState() => _PageThreeState();
}

class _PageThreeState extends State<PageThree> {
  // Add your state variables and methods here
  // ...

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Text('PageThree'),
      ),
    );
  }
}

Also, you might want to use a library to manage your state for your app eg:

PS: More details on how your code is managing the state would be helpful, but this should be sufficient to solve your immediate issues.

I played with your code a bit, and this is how it looks. enter image description here

Upvotes: 0

Related Questions