TabBarView with variable height inside a ListView
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home: Home(),
);
}
}
class Home extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return HomeState();
}
}
class HomeState extends State<Home> with SingleTickerProviderStateMixin {
TabController tabController;
@override
void initState() {
// TODO: implement initState
super.initState();
tabController = TabController(length: 3, vsync: this, initialIndex: 0);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: ListView(
children: <Widget>[
DummySection(height: 100.0,color: Colors.red,),
DummySection(height: 150.0,color: Colors.yellow,),
Container(
height: 350.0,
color: Colors.blue,
child: Column(
children: <Widget>[
TabBar(
unselectedLabelColor: Colors.blue[100],
indicator: BoxDecoration(
color: Colors.lightBlue
),
controller: tabController,
tabs: <Widget>[
Tab(text: "Home",),
Tab(text: "Fav",),
Tab(text: "Star",)
],
),
Expanded(
child: TabBarView(
controller: tabController,
children: [
DummyList(),
DummyList(),
DummyList()
]
),
)
],
),
),
DummySection(height: 100.0,color: Colors.red,),
DummySection(height: 100.0,color: Colors.pink,)
],
),
);
}
}
// Dummy List Container
class DummySection extends StatelessWidget{
Color color;
double height;
DummySection({this.color,this.height});
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
color: color,
height: height,
);
}
}
// Dummy Listing for tab
class DummyList extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return ListView(
children: <Widget>[
Card(
child: Container(
height: 200.0,
alignment: Alignment.center,
child: Text("hello"),
),
),
Card(
child: Container(
height: 200.0,
alignment: Alignment.center,
child: Text("hello"),
),
),
],
);
}
}
Don't use TabBarView
, use IndexedStack
with Visibility
instead.
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
TabController tabController;
int selectedIndex = 0;
@override
void initState() {
super.initState();
tabController = TabController(
initialIndex: selectedIndex,
length: 2,
vsync: this,
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: ListView(
children: [
Container(
height: 128,
color: Colors.blue,
),
Container(
height: 256,
color: Colors.green,
),
TabBar(
tabs: <Tab>[
Tab(text: 'Tab Left'),
Tab(text: 'Tab Right'),
],
controller: tabController,
onTap: (int index) {
setState(() {
selectedIndex = index;
tabController.animateTo(index);
});
},
),
Divider(height: 0),
IndexedStack(
children: <Widget>[
Visibility(
child: Container(
height: 200,
color: Colors.yellow,
child: Center(
child: Text('Content left'),
),
),
maintainState: true,
visible: selectedIndex == 0,
),
Visibility(
child: Container(
height: 1000,
color: Colors.red,
child: Center(
child: Text('Content right'),
),
),
maintainState: true,
visible: selectedIndex == 1,
),
],
index: selectedIndex,
),
],
),
),
);
}
}
IndexedStack
will show a single child from a list of children based on index
while Visibility
will maintain to show or hide view. When the view is hiding, so there is no excess white space to show (stack height is equal to the maximum height of its children).
Here is the dartpad https://dartpad.dev/535f06aa01257b049c7f2f9c719c9881.
You don't need to have TabView to show Tabs content. the minus of this approcach that you are loosing animations and swipes, so you will need to do it by your self if you realy will need it.
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;
@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(() {});
}
}
_listItem() {
return Container(
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: Colors.blueAccent,
),
),
height: 120,
child: Center(
child: Text('List Item', style: TextStyle(fontSize: 20.0)),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: <Widget>[
_listItem(),
TabBar(
controller: _tabController,
labelColor: Colors.redAccent,
tabs: myTabs,
),
Center(
child: [
Text('first tab'),
Column(
children: [
Text('second tab'),
...List.generate(10, (index) => Text('line: $index'))
],
),
Column(
children: [
Text('third tab'),
...List.generate(20, (index) => Text('line: $index'))
],
),
][_tabController.index],
),
_listItem(),
_listItem(),
],
),
);
}
}