djalmafreestyler
djalmafreestyler

Reputation: 1909

Flutter Web - How to use a TabBarView inside a SingleChildScrollView -> Column?

I have a SingleChildScrollView with a Column as child with a lots of children, two of them are a TabBar and a TabBarView. The TabBar renders ok but when I add a TabBarView I got the error:

======== Exception caught by rendering library =====================================================
The following assertion was thrown during performResize():
Horizontal viewport was given unbounded height.

Viewports expand in the cross axis to fill their container and constrain their children to match their extent in the cross axis. In this case, a horizontal viewport was given an unlimited amount of vertical space in which to expand.
The relevant error-causing widget was: 
  TabBarView file:///C:/Users/user/Documents/FlutterProjects/singlechildscrollview/lib/main.dart:83:13
When the exception was thrown, this was the stack: 
#0      RenderViewport.computeDryLayout.<anonymous closure> (package:flutter/src/rendering/viewport.dart:1418:15)
#1      RenderViewport.computeDryLayout (package:flutter/src/rendering/viewport.dart:1430:6)
#2      RenderBox.performResize (package:flutter/src/rendering/box.dart:2332:12)
#3      RenderObject.layout (package:flutter/src/rendering/object.dart:1758:9)
#4      RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
...
The following RenderObject was being processed when the exception was fired: RenderViewport#4db57 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
...  needs compositing
...  parentData: <none> (can use size)
...  constraints: BoxConstraints(0.0<=w<=568.0, 0.0<=h<=Infinity)
...  size: MISSING
...  axisDirection: right
...  crossAxisDirection: down
...  offset: _PagePosition#05511(range: null..null, viewport: null, ScrollableState, _ForceImplicitScrollPhysics -> PageScrollPhysics -> PageScrollPhysics -> ClampingScrollPhysics -> ClampingScrollPhysics -> RangeMaintainingScrollPhysics, IdleScrollActivity#083c3, ScrollDirection.idle)
...  anchor: 0.0
RenderObject: RenderViewport#4db57 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
  needs compositing
  parentData: <none> (can use size)
  constraints: BoxConstraints(0.0<=w<=568.0, 0.0<=h<=Infinity)
  size: MISSING
  axisDirection: right
  crossAxisDirection: down
  offset: _PagePosition#05511(range: null..null, viewport: null, ScrollableState, _ForceImplicitScrollPhysics -> PageScrollPhysics -> PageScrollPhysics -> ClampingScrollPhysics -> ClampingScrollPhysics -> RangeMaintainingScrollPhysics, IdleScrollActivity#083c3, ScrollDirection.idle)
  anchor: 0.0
...  center child: _RenderSliverFractionalPadding#c51bd NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
...    parentData: paintOffset=Offset(0.0, 0.0)
...    constraints: MISSING
...    geometry: null
...    child: RenderSliverFillViewport#29be2 NEEDS-LAYOUT NEEDS-PAINT
...      parentData: paintOffset=Offset(0.0, 0.0)
...      constraints: MISSING
...      geometry: null
...      no children current live
====================================================================================================

======== Exception caught by rendering library =====================================================
The following assertion was thrown during performLayout():
RenderBox was not laid out: RenderViewport#4db57 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1930 pos 12: 'hasSize'

I know that this error is related to the TabBarView doesn't have a height, I cannot set a specific height since the content from each tab will be dynamic or use any other ScrollView, I already tried to wrap it into a Flexible with fit: FlexFit.loose and set to all Column's mainAxisSize: MainAxisSize.min but it still doesn't work.

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
  Widget build(BuildContext context) {

    return Scaffold(
      body: SingleChildScrollView(
        child: Column(
          children: [
            
            /// Other widgets here...then

            TabBar(
              controller: tabController,
              tabs: [

                Tab(
                  child: Text(
                      "Tab 1",
                      style: TextStyle(
                        color: Colors.black
                      ),
                  ),
                ),

                Tab(
                  child: Text(
                    "Tab 1",
                    style: TextStyle(
                        color: Colors.black
                    ),
                  ),
                ),


              ],
            ),

            TabBarView(
              controller: tabController,
              children: [

                /// Each content from each tab will have a dynamic height
                Container(),
                Container()

              ],
            )


          ],
        ),
      ),

    );
  }
}

Upvotes: 2

Views: 5172

Answers (1)

Bach
Bach

Reputation: 3326

You can use this sample app as example. In short, I'm nesting both TabBar and TabBarView within a NestedScrollView that take full size of the screen. This way you can have a dynamic body within the 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()
            ],
          ),
        ),
      ),
    );
  }
}

Upvotes: 4

Related Questions