Related
I am trying to have tabs layout similar to Twitter profile page, where tabs layout shows in the middle of the page. To start with, I have created a basic layout with column as parent and other widgets including tabs as child. Code looks similar to below
Column(
children: [
Text("some text");
//some image widget
ElevatedButton(
style: style,
onPressed: () {},
child: Text("click me"),
);
DefaultTabController(
length: 2,
child: Flexible( //without flexible wrapper, it throws unbounded height issue.
child: Column(
children: [
TabBar(
labelColor: Colors.pink,
indicatorColor: Colors.pink,
tabs: [
Tab(child: Text("Tweets")),
Tab(child: Text("Tweets and replies")),
],
),
Expanded(
child: new TabBarView(children: [
//some widgets here
],),
)
],
),
),
);
]
)
but with above code issue is that, tabs layout doesn't fill remaining space, but just fill some fixed space.
i want to cover the remaining space with tabs, till bottom. if i remove remaining children of parent column and just keep tab, it fill all available space as expected.
If you face any issue, do flutter clean and restart
Result
Test widget
class SWidget extends StatelessWidget {
const SWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text("some text"),
//some image widget
ElevatedButton(
// style: style,
onPressed: () {},
child: Text("click me"),
),
DefaultTabController(
length: 2,
child: Flexible(
//without flexible wrapper, it throws unbounded height issue.
child: Column(
children: [
TabBar(
labelColor: Colors.pink,
indicatorColor: Colors.pink,
tabs: [
Tab(child: Text("Tweets")),
Tab(child: Text("Tweets and replies")),
],
),
Expanded(
child: new TabBarView(
children: [
//some widgets here
Container(
child: Text("item 1"),
color: Colors.deepPurple,
),
Container(
child: Column(
children: [
Text("item 2"),
Text("item 2"),
],
),
color: Colors.deepOrange,
),
],
),
)
],
),
),
),
],
),
);
}
}
Let me know if it does solve the issue.
Here's a picture of what I'm trying to acheive.
However I want both lists in the TabBarView to expand to the bottom of the screen. The only way I can get it to work now, is with a Container with a fixed height. If I use MediaQuery.of(context).size.height, then it expands off the bottom of the page and I get the yellow black overflow thing.
If I wrap the TabBarView in an Expanded widget instead of the fixed size Container, I get a RenderFlex children have non-zero flex but incoming height constraints are unbounded. error. If I wrap each ListView in Expanded (while the fixed height Container is removed), I get a Horizontal viewport was given unbounded height. error. Any idea what I should try next?
Here's the code.
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
alignment: Alignment.centerLeft,
margin: const EdgeInsets.only(bottom: 15, top: 6),
padding: const EdgeInsets.only(left: 15),
child: const Text('Home'),
),
DefaultTabController(
length: 2,
initialIndex: 0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
child: TabBar(
isScrollable: true,
tabs: [
Tab(text: 'Subscriptions'),
Tab(text: 'Recently Visited'),
],
),
),
Container(
height: 500,
decoration: BoxDecoration(
border: Border(
top: BorderSide(color: Colors.grey, width: 0.5))),
child: TabBarView(
children: <Widget>[
buildAList(),
buildAList()
],
),
),
],
),
),
],
),
),
);
}
Widget buildAList() {
return ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: List<Widget>.generate(
100,
(i) => Text(
'Item $i',
style: TextStyle(fontSize: 20),
),
).toList(),
);
}
All I changed in the code below is I wrapped your TabBarView's Container widget with an Expanded widget and then wrapped the DefaultTabController with another Expanded widget. If you're going to put an Expanded widget somewhere, you should probably wrap it's parent with an Expanded widget too if it's inside of a flexible widget such as a Row or Column.
I also changed your buildAList function to use a ListView.builder so that you didn't have to "generate" a list.
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
alignment: Alignment.centerLeft,
margin: const EdgeInsets.only(bottom: 15, top: 6),
padding: const EdgeInsets.only(left: 15),
child: const Text('Home'),
),
Expanded(
child: DefaultTabController(
length: 2,
initialIndex: 0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
child: TabBar(
isScrollable: true,
tabs: [
Tab(text: 'Subscriptions'),
Tab(text: 'Recently Visited'),
],
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(color: Colors.grey, width: 0.5),
),
),
child: TabBarView(
children: <Widget>[
buildAList(),
buildAList(),
],
),
),
),
],
),
),
),
],
),
),
);
}
Widget buildAList() {
return ListView.builder(
shrinkWrap: true,
itemCount: 100,
itemBuilder: (c, i) {
return Text(
"Item $i",
style: TextStyle(fontSize: 20),
);
},
);
}
I have a screen where the bottom half of the content is wrapped in a TabBarView while the top half remains constant. I want to be able to edit text in the bottom half, but the soft keyboard doesn't resize the entire page - it only attempts to eat into the TabBarView's space
My code looks like this:
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(
'Edit Profile',
),
),
//major column
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
SizedBox(height: 20),
UserInfo()
TabBar(
labelPadding: EdgeInsets.zero,
labelColor: Colors.black,
tabs: [
Tab(text: 'INFO'),
Tab(text: 'BIO'),
Tab(text: 'ACCOUNT'),
],
),
Expanded(
//All tab view content
child: TabBarView(
children: [
Info(),
Bio(),
Account(),
],
),
),
),
);
I think you can wrap it inside a SingleChildScrollView:
SingleChildScrollView(
child:
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
SizedBox(height: 20),
UserInfo()
TabBar(
labelPadding: EdgeInsets.zero,
labelColor: Colors.black,
tabs: [
Tab(text: 'INFO'),
Tab(text: 'BIO'),
Tab(text: 'ACCOUNT'),
],
),
Expanded(
//All tab view content
child: TabBarView(
children: [
Info(),
Bio(),
Account(),
],
),
),
)
Or You can wrap the UserInfo() inside an Expanded:
Expanded(
child: UserInfo()
),
I'm want to display a tabbar as it is used with the appbar, but in the middle of the scaffold body. Is it possible?
I have this code, but the TabBarView breaks the ui. If I comment the TabBarView, the TabBars are displayed correctly. What is wrong with the code? In case it's possible to use it this way..
#override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: <Widget>[
Text('Some other random content'),
TabBar(
controller: _tabController,
isScrollable: true,
tabs: <Widget>[
Tab(
child: Text(
'Posts',
style: TextStyle(
fontSize: 18.0,
color: Colors.black87,
),
),
),
Tab(
child: Text(
'Fotos',
style: TextStyle(
fontSize: 18.0,
color: Colors.black87,
),
),
),
],
),
TabBarView(
controller: _tabController,
children: <Widget>[
Center(
child: Text("User"),
),
Center(
child: Text("Email"),
),
],
),
],
),
);
}
Just Change ListView to Column and add SingleChildScrollView
SingleChildScrollView(
child: Column(
children: <Widget>[]
),
)
Hey i guess it's already too late but maybe it helps others.
If you just want to have your tabbar e.g. in the middle of the screen to divide your screen in 2 sections (top: content, bottom: tabbar) you can place your scaffold inside a column, e.g. following:
#override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
flex: 1,
child: Center(
child: Text("Header"),
),
),
Expanded(
flex: 1,
child: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
flexibleSpace: TabBar(
tabs: [
Tab(text: "First"),
Tab(text: "Second"),
Tab(text: "Third"),
],
),
),
body: TabBarView(
children: [
Center(child: Text("First screen")),
Center(child: Text("Second screen")),
Center(child: Text("Third screen")),
],
),
),
),
),
],
);
}
Let me know if this fits your needs / if you need further help :-)
I'm trying to make a profile page, where the users info is at the top. And then have a tab view below that for different views.
This is the code I'm using at the moment, when I take the TabBarView out it doesn't through an error, and if I wrap the TabBarView in an Expanded the error RenderFlex children have non-zero flex but incoming height constraints are unbounded. comes up.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(''),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.all(10.0),
child: Row(
children: <Widget>[
CircleAvatar(
minRadius: 45.0,
backgroundImage: NetworkImage(
'https://www.ienglishstatus.com/wp-content/uploads/2018/04/Anonymous-Whatsapp-profile-picture.jpg'),
),
Padding(
padding: EdgeInsets.only(left: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Testing Name',
style: TextStyle(
fontSize: 22.0,
color: Colors.grey.shade800,
),
),
Text(
'#testing_username',
style: TextStyle(
fontSize: 13.0,
color: Colors.grey.shade800,
),
),
],
),
),
],
),
),
DefaultTabController(
length: 3,
child: Column(
children: <Widget>[
TabBar(
tabs: <Widget>[
Tab(
icon: Padding(
padding: EdgeInsets.all(6.0),
child: Image.asset(
"assets/images/icons/butterlike.png",
color: Colors.grey.shade800,
),
),
),
Tab(
icon: Padding(
padding: EdgeInsets.all(6.0),
child: Image.asset(
"assets/images/icons/butterlike.png",
color: Colors.grey.shade800,
),
),
),
Tab(
icon: Padding(
padding: EdgeInsets.all(6.0),
child: Image.asset(
"assets/images/icons/butterlike.png",
color: Colors.grey.shade800,
),
),
),
],
),
TabBarView(
children: <Widget>[
Container(
color: Colors.grey,
),
Container(
color: Colors.green,
),
Container(
color: Colors.purple,
),
],
),
],
),
)
],
),
);
}
I did try a variation of this but couldn't get it to work.
The error description is clear, the TabBarView doesn't have a bounded height. the parent widget also doesn't have a bounded height. So, the Expanded widget will not solve this issue.
EDIT: below solutions are for above question(with columns).In general cases, use a ListView with shrinkWrap: true.(Or any other widgets with shrinkWrap) As #Konstantin Kozirev mentioned correctly, the shrinkWrap causes some performance issues. look for a better updated solution.
There are some options:
1st Solution:
Wrap the parent widget(Column) with a limited height widget like SizedBox or AspectRatio. Then use the Expanded widget like this:
Expanded(
child: TabBarView(...),
)
2nd Solution:
Use a bounded widget like SizedBox or AspectRatio on the TabBarView itself:
SizedBox(
height: 300.0,
child: TabBarView(...),
)
Note Your can also calcuate the height dynamicly if the height is not static.
I solved it by adding TabBar inside Container and TabBarView inside Expanded:
DefaultTabController(
length: 3,
child: Column(
children: <Widget>[
Container(child: TabBar(..)),
Expanded(child: TabBarView(..)),
],
),
);
try to use IndexedStack instead of TabBarView
i tried Expanded, shrinkWrap = true , ...
but no one work's fine
just try example.
Example:
class Product extends StatefulWidget {
#override
_ProductState createState() => _ProductState();
}
class _ProductState extends State<Product> with SingleTickerProviderStateMixin {
TabController tabController;
int selectedIndex = 0;
#override
void initState() {
super.initState();
tabController = TabController(length: 5, vsync: this, initialIndex: 0);
}
#override
void dispose() {
tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
initialIndex: 0,
child: Scaffold(
body: ListView(
shrinkWrap: true,
children: [
TabBar(
tabs: <Widget>[
Tab(
text: 'one',
),
Tab(
text: 'two',
),
Tab(
text: 'three',
),
],
controller: tabController,
onTap: (index) {
setState(() {
selectedIndex = index;
tabController.animateTo(index);
});
},
),
IndexedStack(
children: <Widget>[
Visibility(
child: Text('test1'),
maintainState: true,
visible: selectedIndex == 0,
),
Visibility(
child: Text('test2'),
maintainState: true,
visible: selectedIndex == 1,
),
Visibility(
child: Text('test3'),
maintainState: true,
visible: selectedIndex == 2,
),
],
index: selectedIndex,
),
],
),
),
);
}
}
special thank's to #arbalest
based on #Yamin answer I used SizeBox Like below to get full page
SizedBox.expand(
child: TabBarView(),
)
or any other size :
SizedBox(
height: height:MediaQuery.of(context).size.height // or every other size ,
child: TabBarView(),
)
The error message in console mentions this: "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".
Clearly, the horizontal viewport here is referring to the TabBarView widget which is not given a height constraint.
So wrap both the TabBar and TabBarView widgets in Expanded widgets and give appropriate flex values to them to let them share their parent's height.
Concretely,
DefaultTabController(
length: 3,
child: Column(
children: <Widget>[
Expanded(
flex: 1,
child: TabBar(
tabs: <Widget>[
Tab(
icon: Padding(
padding: EdgeInsets.all(6.0),
child: Image.asset(
"assets/images/icons/butterlike.png",
color: Colors.grey.shade800,
),
),
),
Tab(
icon: Padding(
padding: EdgeInsets.all(6.0),
child: Image.asset(
"assets/images/icons/butterlike.png",
color: Colors.grey.shade800,
),
),
),
Tab(
icon: Padding(
padding: EdgeInsets.all(6.0),
child: Image.asset(
"assets/images/icons/butterlike.png",
color: Colors.grey.shade800,
),
),
),
],
),
),
Expanded(
flex: 9,
child: TabBarView(
children: <Widget>[
Container(
color: Colors.grey,
),
Container(
color: Colors.green,
),
Container(
color: Colors.purple,
),
],
),
)
],
),
)