2

So I'm creating an screen with 5 BottomNavigationBarItem(s) that would ideally persist, while the AppBar changes according to the design. Here's my code:

class ExploreState extends State<ExploreScreen> {
  int _selectedIndexForBottomNavigationBar = 0;

  void _onItemTappedForBottomNavigationBar(int index) {
    setState(() {
      _selectedIndexForBottomNavigationBar = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new DefaultTabController(
      length: 3,
      child: new Scaffold(
        appBar: AppBar(
          bottom: ColoredTabBar(
            color: Colors.white,
            tabBar: TabBar(
              labelColor: Colors.grey[900],
              indicator: UnderlineTabIndicator(
                borderSide: BorderSide(color: kPrimaryColor, width: 2.0),
              ),
              tabs: <Widget>[
                Tab(text: "Job Posts"),
                Tab(
                  text: "Agencies",
                ),
                Tab(
                  text: "All Applicants",
                ),
              ],
            ),
          ),
          elevation: 0.7,
          centerTitle: true,
          title: Text(
            "Looking For",
            style: TextStyle(fontSize: 18),
          ),
          actions: <Widget>[
            Icon(Icons.search),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 5.0),
            ),
          ],
        ),
        body: TabBarView(
          children: <Widget>[
            Jobs(),
            Agencies(),
            Applicants(),
          ],
        ),
        bottomNavigationBar: BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          items: <BottomNavigationBarItem>[
            _bottomNavigationBarItem(
                AssetImage("assets/images/search-job.png"), 'Explore'),
            _bottomNavigationBarItem(
                AssetImage("assets/images/message.png"), 'Messaging'),
            _bottomNavigationBarItem(
                AssetImage("assets/images/forums.png"), 'Forums'),
            _bottomNavigationBarItem(
                AssetImage("assets/images/notifications.png"), 'Notifications'),
            _bottomNavigationBarItem(
                AssetImage("assets/images/profile.png"), 'Edit Profile'),
          ],
          currentIndex: _selectedIndexForBottomNavigationBar,
          selectedItemColor: kPrimaryColor,
          onTap: _onItemTappedForBottomNavigationBar,
          selectedFontSize: 0,
        ),
      ),
    );
  }

  BottomNavigationBarItem _bottomNavigationBarItem(
      AssetImage icon, String label) {
    return BottomNavigationBarItem(
      activeIcon: _navItemIcon(
          icon, label, [kPrimaryColor, kPrimaryWelcomeColor], Colors.white),
      icon: _navItemIcon(
          icon, label, [Colors.white, Colors.white], Color(0xFF4E5969)),
      label: "",
    );
  }

  Row _navItemIcon(AssetImage icon, String label, List<Color> backgrondColor,
      Color? foregroundColor) {
    return Row(
      children: [
        Expanded(
          child: Container(
            decoration: BoxDecoration(
              gradient: LinearGradient(
                begin: Alignment.bottomLeft,
                end: Alignment.topRight,
                colors: backgrondColor,
                transform: GradientRotation(1.5),
              ),
            ),
            child: Padding(
              padding: const EdgeInsets.all(15.0),
              child: Column(
                children: [
                  ImageIcon(
                    icon,
                    size: 25,
                    color: foregroundColor,
                  ),
                  Text(
                    label,
                    style: TextStyle(color: foregroundColor, fontSize: 8),
                  )
                ],
              ),
            ),
          ),
        ),
      ],
    );
  }
}

class ColoredTabBar extends Container implements PreferredSizeWidget {
  ColoredTabBar({this.color = Colors.white, this.tabBar});

  final Color color;
  final TabBar? tabBar;

  @override
  Size get preferredSize => tabBar!.preferredSize;

  @override
  Widget build(BuildContext context) => Container(
        color: color,
        child: tabBar,
      );
}

Upon Login, they land on the 'Explore' screen but was wondering if this structure is ideal. What I was thinking was a 'common' scaffold for a blank body, 5 BottomNavigationBarItems and a changing AppBar Menu items (for instance, "Messaging" has "Conversations" and "Appointments" AppBar items)

How do we go about properly coding this.

enter image description here

Here is a Sample for the "Messaging" BottomNavigationBarItem.

enter image description here

Odinovsky
  • 551
  • 1
  • 6
  • 23

1 Answers1

2

One possible approach:

  1. Create one Scaffold with your BottomNavigationBar.
  2. Use an IndexedStack (see here) as the body of your Scaffold.
  3. Create the different bodies (Explore, Messaging etc.) and use setState when user clicks on one of your BottomNavigationBarItems and change the current index for IndexedStack.
  4. Also create different AppBar widgets for the different bodies (for example in an array), assign it to the Scaffold based on a state value and in the previously mentioned setState update also its value.

This will work until you can or want to keep all these widgets in the same Stateless Widget class. Chances are good that later you will want to separate them into different classes so that your code is more easy to read. In that case you can use ChangeNotifier, ChangeNotifierProvider and Consumer to communicate state changes between your widgets, as explained here.

Peter Koltai
  • 8,296
  • 2
  • 10
  • 20
  • It is also possible that you implement your tabs within the different bodies and then you don't need to update the common appbar. It can be a better solution. – Peter Koltai Sep 21 '21 at 11:05
  • Thanks! I've implemented the BottomNavigation as TabBar (and not update it), then different TabBarViews on the top, separating the widgets (organized Explore, Messaging, etc along with its body and their own respective topbar) using same TabController. – Odinovsky Sep 21 '21 at 18:40
  • Great, you are welcome. – Peter Koltai Sep 21 '21 at 18:49
  • @Peter Koltai "different bodies" but the advice is to have just one `Scaffold` https://stackoverflow.com/a/60562085/4895256 – MwBakker May 04 '22 at 19:09
  • 1
    @MwBakker I don't see the contradiction. In this case there is only one `Scaffold`, which has a dynamically changing `body` and `appBar`. OP looked for a solution without having different routes. – Peter Koltai May 04 '22 at 19:23
  • you're right, I misread: assumed body meant new `Scaffold` – MwBakker May 04 '22 at 19:28