Harshit Jain
Harshit Jain

Reputation: 71

Navigation within Tab view body Flutter

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')),
        );
      }
    }
    
   

Upvotes: 3

Views: 13474

Answers (3)

liamhp
liamhp

Reputation: 231

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

Upvotes: 0

ezzou
ezzou

Reputation: 2654

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

Upvotes: 1

Shady Aziza
Shady Aziza

Reputation: 53307

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

Upvotes: 15

Related Questions