16

I have a TabBar & TabBarView nested within a Block. This is because the content inside the TabBarView is dynamic and the height of the content is not known until runtime.

I cannot nest the Block inside the TabBarView because there is content preceding the TabBar that should scroll along with the TabBarView content.

Diagram:

Layout Diagram

Code:

new DefaultTabController(
  length: 3,
  child: new Block(
    children: [
      new Container(
        height: 200.0,
        decoration: new BoxDecoration(
          backgroundColor: Colors.blue[500],
        ),
      ),
      new TabBar(
        tabs: <Widget>[
          new Tab(text: '1'),
          new Tab(text: '2'),
          new Tab(text: '3'),
        ],
      ),
      new TabBarView(
        children: [
          new Text('one'),
          new Text('two'),
          new Text('three'),
        ],
      ),
    ],
  )
);

However, a TabBarView's Viewport is not constrained and requires an ancestor widget with a bounded height constraint. Unfortunately a Block does not give a bounded height because its scrollable nature.

I receive this error when running the layout:

══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY    ╞═════════════════════════════════════════════════════════
I/flutter (28715): The following assertion was thrown during   performLayout():
I/flutter (28715): RenderList object was given an infinite size during layout.
I/flutter (28715): This probably means that it is a render object that tries to be as big as possible, but it was put
I/flutter (28715): inside another render object that allows its children to pick their own size.
I/flutter (28715): The nearest ancestor providing an unbounded height constraint is: ...

Any ideas on how to best approach this?

Seth Ladd
  • 112,095
  • 66
  • 196
  • 279
Dvdwasibi
  • 10,537
  • 7
  • 22
  • 22

3 Answers3

13

I don't know what is the block (the link is not working anymore). But as I see from the question, you need to put TableView inside some scrollable component.

The easiest solution is to use tab controller + container instead of TabView Widget.

enter image description here

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

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

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

  TabController _tabController;
  int _tabIndex = 0;

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

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(
        children: <Widget>[
          Container(
            height: 120,
            child: Center(
              child: Text('something on top'),
            ),
          ),

          TabBar(
            controller: _tabController,
            labelColor: Colors.redAccent,
            isScrollable: true,
            tabs: myTabs,
          ),
          Center(
            child: [
              Text('second tab'),
              Column(
                children: List.generate(20, (index) => Text('line: $index')).toList(),
              ),
              Text('third tab')
            ][_tabIndex],
          ),
          Container(child: Text('another component')),
        ],
      ),
    );
  }
}
Kherel
  • 14,882
  • 5
  • 50
  • 80
  • it seems that it's missing the slide animation, right? Can you scroll horizontally also? – Dani May 31 '21 at 11:37
2

Currently solved this using expandable_page_view package. There will be the following widget structure:

    SingleChildScrollView(
            child: Column(children: [
    //... possibly other widgets
            TabBar(
                controller: _tabController,
                tabs: const [
                    Text('online'),
                    Text('offline'),
                ],
            ),
            ExpandablePageView(
                controller: _pageController,
                children: [
                    _buildTab(info!.onlineOptions),
                    _buildTab(info!.offlineOptions),
                ],
            ),
         ],
      ),
    )

There should be also two listeners to connect logic of changing tabs and pages:

_tabController = TabController(length: 2, vsync: this);
_pageController = PageController();
_tabController.addListener(() {
  if (_tabController.index != _pageController.page?.toInt()) {
    _pageController.animateToPage(_tabController.index, duration: const Duration(milliseconds: 100), curve: Curves.ease);
  }
});

_pageController.addListener(() {
  if (_tabController.index != _pageController.page?.toInt()) {
    _tabController.animateTo(_pageController.page?.toInt() ?? 0, duration: const Duration(milliseconds: 100));
  }
});
Valentina Konyukhova
  • 4,464
  • 2
  • 24
  • 33
  • After a few hours of pain, this solution worked perfectly for me. – BadgerHobbs Aug 30 '22 at 01:55
  • This worked for me, too!. And I got better animation after changing toInt() to round() in _pageController listener. `if (_nestedTabController.index != _pageController.page?.round())` and `_nestedTabController.animateTo(_pageController.page?.round() ?? 0` – sunken.shim Feb 19 '23 at 15:41
1

This will be made very easy by the refactored scrolling "sliver" code currently landing as part of https://github.com/flutter/flutter/projects/3.

SliverList https://docs.flutter.io/flutter/widgets/SliverList-class.html is designed to enable exactly this kind of composition and should be ready for general usage in about two weeks time.

Eric Seidel
  • 3,282
  • 1
  • 15
  • 22
  • 1
    Could you give an example of using SilverList to construct a view like this. I tried this [gist](https://gist.github.com/WenhaoWu/e1d3a1710fe9d79adc8398bf6fb59b68) , but it still requires a fixed height container to wrap the `TabBarView`. Any help would be appreciated. Thanks – WenhaoWu Dec 13 '17 at 15:06