Search code examples
flutterdarttabbar

Cannot set dynamic height for TabBarView in flutter


I am trying to create TabBar which would be located in the middle of the page (Description widget must be at the top).

The problem is that I have to manually set the height of the Container widget which contains TabBarView. If I leave it without this height, I get error Horizontal viewport was given unbounded height..

Top level widget:

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: CustomAppBar(),
      body: SingleChildScrollView(
        child: Column(
          children: <Widget>[Description(), Tabs()],
        ),
      ),
    );
  }

Tabs widget:

class Tabs extends StatelessWidget {
  final _tabs = [
    Tab(
      icon: Icon(Icons.menu),
      text: 'Menu',
    ),
    Tab(
      icon: Icon(Icons.mode_comment),
      text: 'Reviews',
    )
  ];

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: _tabs.length,
        child: Column(
          children: <Widget>[
            TabBar(
              labelColor: PickColors.black,
              indicatorSize: TabBarIndicatorSize.tab,
              tabs: _tabs,
            ),
            Container(
              width: double.infinity,
              height: 200, // I need to remove this and make height dynamic
              child: TabBarView(
                children: <Widget>[MenuTab(), ReviewsTab()],
              ),
            ),
          ],
        ));
  }
}

Since Tabs content will be dynamic, the height will also be. I cannot use static height here.

Is there an alternative for the Container with static height? How can I make my tabs' height dynamic?


Solution

  • I have fixed this issue by changing SingleChildScrollView into ListView and writing my own TabView widget which contains tabs in Stack wrapper.

    Top level widget body wrappers changed from Column and SingleChildScrollView to ListView:

      Widget build(BuildContext context) {
        SizeConfig().init(context);
        return Scaffold(
          appBar: RestaurantInfoAppBar(),
          body: ListView(
            children: <Widget>[Description(), Tabs()],
          ),
        );
      }
    

    Tabs widget - removed Container with a static width wrapper:

      Widget build(BuildContext context) {
        return DefaultTabController(
            length: _tabs.length,
            child: Column(
              children: <Widget>[
                TabBar(
                  labelColor: PickColors.black,
                  indicatorSize: TabBarIndicatorSize.tab,
                  tabs: _tabs,
                ),
                TabsView(
                  tabIndex: _tabIndex,
                  firstTab: MenuTab(),
                  secondTab: ReviewsTab(),
                )
              ],
            ));
      }
    

    New custom TabsView component currently handles only two tabs (since I only need two) but can be easily changed to handle dynamic numbers of tabs:

    class TabsView extends StatelessWidget {
      TabsView(
          {Key key,
          @required this.tabIndex,
          @required this.firstTab,
          @required this.secondTab})
          : super(key: key);
    
      final int tabIndex;
      final Widget firstTab;
      final Widget secondTab;
    
      @override
      Widget build(BuildContext context) {
        return Stack(
          children: <Widget>[
            AnimatedContainer(
              child: firstTab,
              width: SizeConfig.screenWidth,
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
              transform: Matrix4.translationValues(
                  tabIndex == 0 ? 0 : -SizeConfig.screenWidth, 0, 0),
              duration: Duration(milliseconds: 300),
              curve: Curves.easeIn,
            ),
            AnimatedContainer(
              child: secondTab,
              width: SizeConfig.screenWidth,
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
              transform: Matrix4.translationValues(
                  tabIndex == 1 ? 0 : SizeConfig.screenWidth, 0, 0),
              duration: Duration(milliseconds: 300),
              curve: Curves.easeIn,
            )
          ],
        );
      }
    }
    

    P.S. SizeConfig is the same as MediaQuery.of(context).size.width.

    Hope this helps someone like me! :)