Search code examples
flutterflutter-layoutflutter-navigationflutter-bottomnavigationflutter-scaffold

How to push a Route from inside the body of a scaffold with a bottomnavigationbar?


I have a MaterialApp in my main.dart within a Nav() Widget which contains a Scaffold with an appBar and a BottomNavigationBar. The NavigationBar has 4 BottomNavigationBarItems, but I have more Pages than 4 in my whole App. The other pages can be accessed via the first BottomNavigationBarItem 'Home'. But when I push the new NamedRoute the AppBar disapears. How can I solve this Problem?

I've already tried using the bottomNavigationBar in my own Widget in a new File. Problem: setState() doesn't work.

Here's some code

main.dart:

import 'package:flutter/material.dart';
import 'pages/nav.dart';
import 'route/route.dart' as route;

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Lieferantenapp',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.red,
      ),
      home: Nav(),
      onGenerateRoute: route.controller,
      //initialRoute: route.navPage,
    );
  }
}

nav.dart

import 'package:flutter/material.dart';
import 'package:lieferantenapp/components/MyAppBar.dart';
import 'package:lieferantenapp/components/MyThreeLineText.dart';
//import 'package:lieferantenapp/components/myBottomNavigationBar.dart';
import 'package:lieferantenapp/components/myCard.dart';
import 'package:lieferantenapp/pages/bestellungen.dart';
import 'package:lieferantenapp/pages/packliste.dart';
import 'package:lieferantenapp/pages/tour.dart';
import 'package:lieferantenapp/pages/home.dart';

class Nav extends StatefulWidget {
  @override
  _NavState createState() => _NavState();
}

class _NavState extends State<Nav> {
  int _selectedIndex = 0;
  List<Widget> _widgetOptions = <Widget>[
    Home(),
    Tour(),
    Bestellungen(),
    Packliste(),
  ];

  void _onItemTap(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      //Custom App Bar without any passed data
      appBar: myAppBar(context, null, null),
      backgroundColor: Colors.white,
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _selectedIndex,
        type: BottomNavigationBarType.fixed,
        fixedColor: Colors.red,
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
          BottomNavigationBarItem(
            icon: Icon(Icons.local_shipping),
            label: 'Tour',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.inventory_2),
            label: 'Packliste',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.receipt_long),
            label: 'Bestellungen',
          ),
        ],
        onTap: _onItemTap,
      ),
      body: _widgetOptions.elementAt(_selectedIndex),
    );
  }
}

home.dart:

import 'package:flutter/material.dart';
import 'package:lieferantenapp/components/MyThreeLineText.dart';
import 'package:lieferantenapp/components/myCard.dart';

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        //BlackBoard Message
        //TODO get BlackBoard Message from Server
        //myThreeLineText(Context, TextTheme, OVerline, Title, Body)
        myThreeLineText(
          context,
          Theme.of(context).textTheme,
          '12.07.2021',
          'Servus und Mahlzeit!',
          "Herzlich Willkommen in der neuen Mahlzeit LieferApp mit " +
              "optimierter Routenplanung via Google Maps.",
        ),
        Expanded(
          child: GridView.count(
            shrinkWrap: true,
            primary: false,
            padding: const EdgeInsets.all(18),
            crossAxisSpacing: 15,
            mainAxisSpacing: 15,
            crossAxisCount: 2,
            children: <Widget>[
              myCard(
                context,
                Icons.map_outlined,
                'Touren',
                Theme.of(context).textTheme,
                Color(0xff119052),
              ),
              myCard(
                context,
                Icons.local_shipping_outlined,
                'Touren',
                Theme.of(context).textTheme,
                Color(0xffFF9444),
              ),
              myCard(
                context,
                Icons.inventory_2_outlined,
                'Touren',
                Theme.of(context).textTheme,
                Color(0xff84000F),
              ),
              myCard(
                context,
                Icons.receipt_long_outlined,
                'Touren',
                Theme.of(context).textTheme,
                Color(0xffCB5E5E),
              ),
              myCard(
                context,
                Icons.bookmark_border_outlined,
                'Touren',
                Theme.of(context).textTheme,
                Color(0xffCC3021),
              ),
              myCard(
                context,
                Icons.settings_outlined,
                'Touren',
                Theme.of(context).textTheme,
                Color(0xff57BB61),
              ),
            ],
          ),
        ),
      ],
    );
  }
}

myCard.dart:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:lieferantenapp/route/route.dart' as route;

Widget myCard(BuildContext context, IconData icon, String text, TextTheme theme,
    Color cColor) {
  return GestureDetector(
    //TODO implement ROUTE ontap start Cards
    onTap: () => {},
    child: Card(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(30),
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(
            icon,
            color: Colors.white,
            size: 30,
          ),
          SizedBox(height: 25),
          Text(
            text,
            style: theme.bodyText1.apply(color: Colors.white),
          ),
        ],
      ),
      color: cColor,
    ),
  );
}

I am also open for Tips and Tricks to improve my code.

EDIT - new main.dart:

import 'package:flutter/material.dart';
import 'package:lieferantenapp/components/MyAppBar.dart';

import 'pages/nav.dart';
import 'route/route.dart' as route;

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Mahlzeit Lieferantenapp',
      theme: ThemeData(
        primarySwatch: Colors.red,
      ),
      //home: Nav(),
      //onGenerateRoute: route.controller,
      builder: (context, _) => Scaffold(
        appBar: myAppBar(context, null, null),
        body: Navigator(onGenerateRoute: route.controller),
      ),
      //initialRoute: route.navPage,
    );
  }
}

Solution

  • The app bar is part of the Scaffold which will appear only on the 4 widgets In _widgetOptions. If you don't have that Scaffold on your page there is no app bar.

    Wrapping your Navigator with your Scaffold (i.e. Scaffold on TOP of the Navigator) should do the trick.

    Note that when you're 'returning' a Widget from a build function it becomes a child. When you are using Navigator to push a Widget it becomes a sibling and will be on top of the current screen. You can visualize this clearly in the dev tools.

    EDIT: Since you're using the default MaterialApp's Navigator this won't work. You will need to create your own Navigator for this.

    i.e: Remove home parameter from MaterialApp. Use builder parameter instead. And provide a Navigator widget to the builder (wrapped by your Scaffold)

    Something like this:

    MaterialApp(builder: (context, _) => Scaffold( ....., body: Navigator( onGenerateRoutes:...)))