Cannot set dynamic height for TabBarView in flutter

4,788

Solution 1

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! :)

Solution 2

I found this working fine with me

import 'package:cdc/ui/shared/app_color.dart';
import 'package:flutter/material.dart';

class OrderDetails extends StatefulWidget {
  @override
  _OrderDetailsState createState() => _OrderDetailsState();
}

class _OrderDetailsState extends State<OrderDetails>
    with SingleTickerProviderStateMixin {
  final List<Widget> myTabs = [
    Tab(text: 'one'),
    Tab(text: 'two'),
  ];

  TabController _tabController;
  int _tabIndex = 0;

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  @override
  void initState() {
    _tabController = TabController(length: 2, vsync: this);
    _tabController.addListener(_handleTabSelection);
    super.initState();
  }

  _handleTabSelection() {
    if (_tabController.indexIsChanging) {
      setState(() {
        _tabIndex = _tabController.index;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Order Detials'),
        backgroundColor: kPrimaryColor,
      ),
      body: ListView(
        padding: EdgeInsets.all(15),
        children: <Widget>[
          Container(
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(14.0),
              color: const Color(0xffffffff),
              boxShadow: [
                BoxShadow(
                  color: const Color(0x2e000000),
                  offset: Offset(0, 0),
                  blurRadius: 10,
                ),
              ],
            ),
            child: Column(
              children: <Widget>[
                TabBar(
                  controller: _tabController,
                  labelColor: Colors.redAccent,
                  tabs: myTabs,
                ),
                Container(
                  child: [
                    Text('First tab'),
                    Column(
                      children:
                          List.generate(20, (index) => Text('line: $index'))
                              .toList(),
                    ),
                  ][_tabIndex],
                ),
              ],
            ),
          ),
          Container(child: Text('another component')),
        ],
      ),
    );
  }
}
Share:
4,788
zilijonas
Author by

zilijonas

IT bachelor. React, React-Native and Flutter developer.

Updated on December 13, 2022

Comments

  • zilijonas
    zilijonas over 1 year

    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?

  • lacas
    lacas about 4 years
    any other way? this is a custom widget that not support swype :)
  • Mrinal Jain
    Mrinal Jain almost 4 years
    How to change _tabIndex when tabs change in the above example?
  • Mrinal Jain
    Mrinal Jain almost 4 years
    The TabsView is taking the height of the tab with bigger content, due to this the other tab with less content has an empty space. Any solution to it?
  • Alex Trujillo
    Alex Trujillo about 3 years
    you can change the _tabIndexby using the TabController _tabController. i solved it by using the saple at the begining. stackoverflow.com/a/62813551/5319007
  • lenz
    lenz almost 3 years
    Great idea AnimatedContainer. Works like a charm
  • Piyush Kumar
    Piyush Kumar about 2 years
    not working for me as its till throws error