Reputation: 97
I am having real trouble getting a layout to work within Flutter.
The layout I am trying to create:
ListView
that contains a:
Container
.TabBar
.TabBarView
, where each TabBarView
contains a Column
.Here is the schematic for the layout:
Example Code
Here is a minimum example code (with exact widget definitions removed):
return DefaultTabController(
length: 2,
child: ListView(
children: [
// TOP CONTAINER //
Container(height: 30),
// TAB BAR //
const TabBar(tabs: [
Tab(child: Text("Tab 1")),
Tab(child: Text("Tab 2")),
]),
// TAB BAR VIEWS //
SizedBox(
height: MediaQuery.of(context).size.height,
child: TabBarView(
children: [
Container(height: 5000),
Container(height: 5000),
],
),
)
],
),
);
The Problem:
When the height of the window gets smaller, I get an overflow error at the bottom:
What I have Done:
Column
into a ListView
, which fixed the overflow, but resulted in two separate scrollable areas (the individual tab views and the whole page), which is not what I want - I want a single scrollable area. Setting the physics
property of this ListView
to NeverScrollablePhysics()
doesnt fix this and results in some weird behaviour.NestedScrollView
with Silvers
(from How to create a bounded scrollable TabBarView). But this results in the following exception when navigating through the tabs: The provided ScrollController is currently attached to more than one ScrollPosition.
, and produces some dodgy scroll mechanics.CustomScrollView
but that didnt work.Similar Questions that Didnt provide a working solution:
I am very confused as to why it is not working as I feel this is a very simple thing. Essentially, it is the same layout used in the Instragram app (among others) when viewing your personal profile (see: https://unblast.com/wp-content/uploads/2020/01/Instagram-UI-Profile-1.jpg).
Upvotes: 7
Views: 17938
Reputation: 2000
I have scratched my head around TabBarView
many times with a question 'How to put dynamic-height content inside TabBarView
while it is inside a scrollable container?'
Unfortunately, it is broken for such use cases as the scroll and does not work correctly and the content overflows.
Hence, my solution answers the OP question by answering how to achieve the whole page scrollable. It replaces TabBarView
with different mechanism.
My solution is to skip TabBarView
and use TabBar
and TabController
to get a Widget
from the Widget
list.
Code:
// STATEFUL WIDGET CODE
late TabController tabController; // USED FOR NAVIGATION
@override
void initState() {
tabController = TabController(
initialIndex: 0,
length: 3, // adjust your length
vsync: this,
);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('test')), // some app bar
body: SingleChildScrollView(
child: Column(
children: [
TabBar(controller: tabController),
_getTabAtIndex(tabContoller.index),
],
),
),
);
}
Widget _getTabAtIndex(int index) {
var list = [
Container(), // FIRST ITEM
Container(), // SECOND ITEM
Container(), // THIRD ITEM
];
return list[index];
}
Upvotes: 5
Reputation: 8274
From the comments you can wrap your page in a singlechildscrollview
, disable scroll physics for the listview as the parent is already scrollable.
return SingleChildScrollView(
child: DefaultTabController(
length: 2,
child: ListView(
physics: NeverScrollableScrollPhysics(),
children: [
// TOP CONTAINER //
Container(height: 30),
// TAB BAR //
const TabBar(tabs: [
Tab(child: Text("Tab 1")),
Tab(child: Text("Tab 2")),
]),
// TAB BAR VIEWS //
SizedBox(
height: MediaQuery.of(context).size.height,
child: TabBarView(
children: [
Container(height: 5000),
Container(height: 5000),
],
),
)
],
),
));
** Option2 **
you can use a customScrollView
or a nestedScrollView
DefaultTabController(
length: 2,
child:
CustomScrollView(
slivers: [
SlivertoboxAdapter(child: // TOP CONTAINER //
Container(height: 30),
SlivertoboxAdapter(child: // TAB BAR //
const TabBar(tabs: [
Tab(child: Text("Tab 1")),
Tab(child: Text("Tab 2")),
]),
//... Sliverfillremaining/slivettoboxadapter for your tabbarview
SlivertoboxAdapter(child:TabBarView(
children: [
Container(height: 5000),
Container(height: 5000),
],
),
Upvotes: 5