3

The app contains TabBar as well as BottomNavigationBar. When I tried to navigate within the body of tab bar view it navigates full screen.

This is the result I am trying to get when clicked on button- Expected Result

But I am getting this Current Output

Here I have attached the code-

 Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
            title: new Text("Traveler"),
            bottom: new TabBar(controller: _controller, tabs: <Tab>[
              new Tab(text: "NEW"),
              new Tab(text: "HOTELS"),
              new Tab(text: "FOOD"),
              new Tab(text: "FUN"),
            ]),
          ),
          body: new TabBarView(
            controller: _controller,
            children: <Widget>[
              new NewPage(_index),
              new HotelsPage(_index),
              new FoodPage(_index),
              new FunPage(_index),
            ],
          ),
          bottomNavigationBar: new BottomNavigationBar(
              currentIndex: _index,
              onTap: (int _index) {
                setState(() {
                  this._index = _index;
                });
              },
              items: <BottomNavigationBarItem>[
                new BottomNavigationBarItem(
                  icon: new Icon(Icons.home),
                  title: new Text("Home"),
                ),
                new BottomNavigationBarItem(
                  icon: new Icon(Icons.favorite),
                  title: new Text("Favorites"),
                ),
              ]),
        );
      }
    }
    
    class NewPage extends StatelessWidget {
      final int index;
    
      NewPage(this.index);
    
      @override
      Widget build(BuildContext context) {
        return new Center(
          child: RaisedButton(
            onPressed: (){
               Navigator.push(context,MaterialPageRoute(builder: (context)=>InsideTabViewPage()), );
            },
            child: new Text('NewPage, index: $index')),
        );
      }
    }
    
   
1
  • Let me know if you want full code Commented Jun 2, 2021 at 21:38

3 Answers 3

15

You can achieve this by creating a new Navigator widget that sits somewhere under the main app navigator,

Here is test example:

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark().copyWith(
        elevatedButtonTheme: ElevatedButtonThemeData(
            style: ButtonStyle(
          backgroundColor: MaterialStateProperty.all(Colors.blueGrey[800]),
        )),
      ),
      home: MyApp()));
}

//Store this globally
final GlobalKey<NavigatorState> _navKey = GlobalKey<NavigatorState>();

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

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

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
  late final TabController _tabController;
  @override
  void initState() {
    _tabController = TabController(length: 2, vsync: this);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Nested Navigator'),
        bottom: TabBar(
          controller: _tabController,
          tabs: [
            Tab(
              child: Text('First Tab'),
            ),
            Tab(
              child: Text('Second Tab'),
            ),
          ],
        ),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
          BottomNavigationBarItem(
              icon: Icon(Icons.favorite), label: 'Favorites'),
        ],
      ),
      body: Navigator(
        key: _navKey,
        onGenerateRoute: (_) => MaterialPageRoute(
          builder: (_) => TabBarView(
            controller: _tabController,
            children: [
              FirstPage(),
              SecondPage(),
            ],
          ),
        ),
      ),
    );
  }
}

class FirstPage extends StatelessWidget {
  const FirstPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('First Page'),
            ElevatedButton(
                onPressed: () {
                  _navKey.currentState!.push(
                    MaterialPageRoute(
                      builder: (_) => SubFirstPage(),
                    ),
                  );
                },
                child: Text('Go to Nested Page 1'))
          ],
        ),
      ),
    );
    ;
  }
}

class SecondPage extends StatelessWidget {
  const SecondPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Second Page'),
            ElevatedButton(
              onPressed: () {
                _navKey.currentState!.push(
                  MaterialPageRoute(
                    builder: (_) => SubSecondPage(),
                  ),
                );
              },
              child: Text('Go to Nested Page 2'),
            )
          ],
        ),
      ),
    );
  }
}

class SubFirstPage extends StatelessWidget {
  const SubFirstPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Nested Page'),
      ),
      body: Center(
        child: Text('From First Page'),
      ),
    );
  }
}

class SubSecondPage extends StatelessWidget {
  const SubSecondPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second Nested Page'),
      ),
      body: Center(
        child: Text('From Second Page'),
      ),
    );
  }
}

And the result is:

enter image description here

You can read more about nested navigation in Navigator 1.0 API here

2
  • how can i use "_navKey.currentState!.p...." if class SecondPage is other file ?
    – EagleCode
    Commented Mar 18, 2022 at 16:20
  • This doesn't work. Navigate to the Second Tab, tap the button and go to the sub-page, then tap on the First Tab. You are still viewing the sub-page for the second tab and not the view that should persist for the first. Commented Sep 13, 2022 at 9:07
1

A good solution to this problem is to use the go_router package from pub.dev it's a great package for routing, it's a flutter favorite package

It address specifically the nested routing issue you are asking about, by implementing something they call shell routing you can basically define some routes of your application as having a shell, that shell can be anything, like in your case a Scaffold with a bottomNavigationBar,

When such a route is invoked the Navigation will happen within that shell (having that shell stay in place during the navigation), using another Navigator independent from the Root Navigator of your application

0

Just spent some time exploring this issue. I decided to go with the GoRouter and Shell Route approach and put together a simple example in DartPad to test it out based on this.

The only drawback with this first approach is the lack of stateful navigation, i.e. if you go to inner-home and navbar over to settings and back, you're sent back to the base home page, not inner-home. Luckily, StatefulShellRoute exists and I found this example in the github repo. I took some time and worked it into a simpler example as an extension of the previous in this DartPad.

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.