Search code examples
flutterdartflutter-navigation

top TabBar inside BottomNavigationBar


I'm having trouble getting a top TabBar to render inside my BottomNavigationBar. I only want the top TabBar to be displayed inside the home BottomNavigationBarItem. I know I can setup another scaffold in the home page, but I obviously don't want a scaffold inside a scaffold. With the following code I'm getting the runtime error:

Another exception was thrown: RenderBox was not laid out: RenderViewport#d0e83 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE

home.dart:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class Home extends StatelessWidget {
  static const routeName = 'home';

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 2,
      child: Column(
        children: <Widget>[
          TabBar(
            tabs: <Widget>[
              Tab(
                text: '1',
                icon: Icon(Icons.add_a_photo),
              ),
              Tab(
                text: '2',
                icon: Icon(Icons.adb),
              ),
            ],
          ),
          TabBarView(
            children: <Widget>[
              Center(
                child: Text('1'),
              ),
              Center(
                child: Text('2'),
              )
            ],
          ),
        ],
      ),
    );
  }
}

main.dart:

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: ...,
      theme: ThemeData(primaryColor: Colors.red, accentColor: Colors.white),
      // home: Categories(),
      initialRoute: '/',
      routes: {
        '/': (ctx) => MyHomePage(0),
        ...,
      },
      // onGenerateRoute: (settings){
      //incase you're creating routes on the fly that arent named routes/generating route names during the app lifecycle
      // print(settings.arguments)
      // if(settings.name=='mealdetail'){
      //   return ... materialpageroute...
      // }
      // else
      //   return MaterialPageRoute(builder: (context) => Categories());
      // },
      onUnknownRoute: (settings) {
        //useful for catching routes as a last resort/couldnt find the page etc.../ 404 fallback page like web
        return MaterialPageRoute(builder: (context) => Home());
      },
    );
  }
}

class MyHomePage extends StatefulWidget {
  final int selectedScreenIndex;

  MyHomePage(this.selectedScreenIndex);

  @override
  _MyHomePageState createState() => _MyHomePageState(selectedScreenIndex);
}

class _MyHomePageState extends State<MyHomePage> {
  final List<Map<String, Object>> screens = [
    {'title': 'Home', 'screen': Home()},
    {...},
  ];

  _MyHomePageState(this.selectedScreenIndex);

  int selectedScreenIndex = 0;

  void selectScreen(int index) {
    setState(() {
      selectedScreenIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        // resizeToAvoidBottomInset: true,

        appBar: AppBar(
          title: Text(screens[selectedScreenIndex]['title']),
        ),
        drawer: MainDrawer(),
        body: screens[selectedScreenIndex]['screen'],
        bottomNavigationBar: BottomNavigationBar(
          onTap: selectScreen,
          backgroundColor: Theme.of(context).primaryColor,
          unselectedItemColor: Colors.white,
          selectedItemColor: Theme.of(context).accentColor,
          currentIndex: selectedScreenIndex,
          type: BottomNavigationBarType.fixed,
          items: [
            BottomNavigationBarItem(
                icon: Icon(Icons.home),
                title: Text('Home'),
                backgroundColor: Theme.of(context).primaryColor),
            ...,
          ],
        ));
  }
}

Solution

  • the easiest way to do it (not the best) is by using the inline condition in the app bar parameter , if the selected index is = 0 return app bar with tab bar , else return the normal app bar.

    Example

     appBar:selectedSreenIndex==0? AppBar(title:Text(screens[selectedScreenIndex['title'], 
         bottom: TabBar())
         : AppBar(
              title: Text(screens[selectedScreenIndex]['title']),
            ),