Home > database >  How to have dynamic height of TabBarView content in flutter
How to have dynamic height of TabBarView content in flutter

Time:10-28

What i'm trying to achieve is that, depending on the content of the TabBarView component, expand it to take as much space as it needs ( height ) and make the whole screen scrollable, not only the Container of the TabBarView.

In the attached example I have a Container which wraps the TabBar component. It has a fixed height at the moment because it throws errors if not. Actually that's the problem i want to fix. Get rid of that fix height, and have it somehow dinamically set.

Here is my code with comments:

import 'package:flutter/material.dart';
import 'package:livescore/models/leaguesModel/leagues_model.dart';
import 'package:livescore/models/livescores/livescore_data_model.dart';
import 'package:livescore/pages/matchDetailsPage/components/matchDetailsHeaderWidgets/match_details_header.dart';
import 'package:livescore/pages/matchDetailsPage/components/matchDetailsStatsWidgets/match_details_stats_component.dart';

// ignore: must_be_immutable
class MatchDetailsPage extends StatefulWidget {
  LeaguesModelData leaguesModelData;
  LivescoreDataModel matchDetails;
  MatchDetailsPage(this.leaguesModelData, this.matchDetails, {super.key});

  @override
  State<MatchDetailsPage> createState() => _MatchDetailsPageState();
}

class _MatchDetailsPageState extends State<MatchDetailsPage>
    with SingleTickerProviderStateMixin {
  late TabController _tabController;

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Container(
                                                                        // whole screen background and border
          height: MediaQuery.of(context).size.height,
          decoration: BoxDecoration(
            border: Border.all(
                color: Theme.of(context).colorScheme.primary, width: 5),
          ),
          child: Column(
            children: <Widget>[
              MatchDetailsHeader(
                  widget.leaguesModelData,
                  widget
                      .matchDetails),                                               // upper side of the screen, above the TabBar component
              Padding(
                padding: const EdgeInsets.only(top: 30, right: 20, left: 20),
                child: Container(
                                                                                      // the container representing the TabBar zone
                  height: 400,
                  width: MediaQuery.of(context).size.width,
                  decoration: BoxDecoration(
                    border: Border.all(color: Colors.white),
                    color: Colors.white,
                    borderRadius: const BorderRadius.all(Radius.circular(10)),
                  ),
                  child: Padding(
                    padding: const EdgeInsets.only(left: 10, right: 10),
                    child: Column(
                      children: [
                        Padding(
                          padding: const EdgeInsets.only(top: 15, bottom: 20),
                          child: SizedBox(
                            child: TabBar(
                              labelPadding:
                                  const EdgeInsets.symmetric(horizontal: 10),
                              isScrollable: true,
                              indicatorWeight: 0,
                              labelStyle: const TextStyle(fontSize: 13),
                              unselectedLabelColor: Colors.grey.shade700,
                              labelColor: Colors.white,
                              indicator: const ShapeDecoration(
                                  shape: RoundedRectangleBorder(
                                      borderRadius: BorderRadius.all(
                                          Radius.circular(15))),
                                  color: Colors.pink),
                              tabs: const [
                                Tab(
                                  text: "Stats",
                                ),
                                Tab(
                                  text: "Info",
                                ),
                                Tab(
                                  text: "Line-up",
                                ),
                                Tab(
                                  text: "H2H",
                                ),
                                Tab(
                                  text: "Table",
                                ),
                                Tab(
                                  text: "News",
                                ),
                                Tab(
                                  text: "Info",
                                ),
                                Tab(
                                  text: "Info",
                                ),
                                Tab(
                                  text: "Info",
                                ),
                                Tab(
                                  text: "Info",
                                ),
                              ],
                              controller: _tabController,
                              indicatorSize: TabBarIndicatorSize.tab,
                            ),
                          ),
                        ),
                        Expanded(
                          flex: 1,
                          child: TabBarView(
                            physics: const NeverScrollableScrollPhysics(),
                            controller: _tabController,
                            children: [
                              (widget.matchDetails.stats != null &&
                                      widget
                                          .matchDetails.stats!.data.isNotEmpty)
                                  ? MatchDetailsStatsComponent(
                                      widget.matchDetails.localTeam.data.id,
                                      widget.matchDetails.visitorTeam.data.id,
                                      widget.matchDetails.stats)
                                  : Column(
                                      children: const [
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                        Text("stats not available yet"),
                                      ],
                                    ),
                              const Text("abcd"),
                              const Text("abcd"),
                              const Text("abcd"),
                              const Text("abcd"),
                              const Text("abcd"),
                              const Text("abcd"),
                              const Text("abcd"),
                              const Text("abcd"),
                              const Text("abcd"),
                            ],
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

I tried getting it work with NestedScrollView, ListView, have ScrollPhysics on the parent SingleChildScrollView and NeverScrollableScrollPhysics on the TabBarView component, but didn't get it to work.

This image represents the screen at the moment, when the content doens't exceeds it's limits.

This image shows the boundry error that is thrown when the content exceeds it's limits. When this is the case, i would the TabBarView container to expand and make the whole screen scrollable

CodePudding user response:

Use Layout Builder as a parent of SingleChildScrollView like this and remove fixed height of Container. Follow the docs

https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html

CodePudding user response:

You can use this simple structure as a reference. In detail, I'm using both TabBar and TabBarView within a NestedScrollView which is able to take the full height. In this way, you can achieve dynamic height with TabBarView.

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(home: MyHomePage()));
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  late TabController tabController;

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: NestedScrollView(
        headerSliverBuilder: (context, value) {
          return [
            SliverToBoxAdapter(
              child: TabBar(
                controller: tabController,
                labelColor: Colors.redAccent,
                isScrollable: true,
                tabs: [
                  Tab(
                    child: Text(
                      "Tab 1",
                      style: TextStyle(color: Colors.black),
                    ),
                  ),
                  Tab(
                    child: Text(
                      "Tab 1",
                      style: TextStyle(color: Colors.black),
                    ),
                  ),
                ],
              ),
            ),
          ];
        },
        body: Container(
          child: TabBarView(
            controller: tabController,
            children: [
              /// Each content from each tab will have a dynamic height
              Container(),
              Container()
            ],
          ),
        ),
      ),
    );
  }
}
  • Related