Can't set ListView into LayoutBuilder Widget (Flutter) - flutter

I need to have some structure like this
I use LayoutBuilder to get the height of content (between App Bar and TabsBottomNavigation). Here i build Profile Info Container and want to build ListView with some List Tiles, but when I try to build it in Layout Builder I have errors in console.
If I create ListView out of LayoutBuilder it works!
Please help me to solve it.
My code below:
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: Container(
child: Column(
children: <Widget>[
Container(
height: viewportConstraints.maxHeight * .44,
color: Theme.of(context).primaryColor,
padding: EdgeInsets.only(bottom: 2),
child: Align(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildProfileImage(context),
SizedBox(height: 17),
Text(userName)
],
),
),
),
Expanded(
child: ListView(
children: <Widget>[
ListTile(
leading: Icon(Icons.print),
title: Text('asdad'),
)
],
),
)
],
),
),
);
},
);
}

Use below build method will work in your case. (I have checked and it's working, so I hope it will work in your case also and will fit in your requirement.)
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return Container(
child: Column(
children: <Widget>[
Container(
height: viewportConstraints.maxHeight * .44,
color: Theme.of(context).primaryColor,
padding: EdgeInsets.only(bottom: 2),
child: Align(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildProfileImage(context),
SizedBox(height: 17),
Text(userName),
],
),
),
),
SizedBox(height: 16),
Flexible(
child: ListView(
children: <Widget>[
Card(
child: ListTile(
leading: Icon(Icons.print),
title: Text('asdad'),
),
),
],
),
),
],
),
);
},
);
}
I think SingleChildScrollView is of no use in this case so I removed it but you can use it if you fill so.
You still need to do some UI improvement as per your wish as this is the basic structure as per your requirement.
Hope this will help you.

You are using the LayoutBuilder in a wrong way.
It's supposed to be used to change the layout with the size of the device and/or orientation.
What you are trying to do is best accomplished with MediaQuery:
MediaQuery.of(context).padding.top //APP bar height
MediaQuery.of(context).padding.bottom //Bottom bar height
MediaQuery.of(context).size.height //Screen height

Widget build(BuildContext context) {
return Column(
children:<Widget>[
Expanded(
child:LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: Container(
child: Column(
children: <Widget>[
Container(
height: viewportConstraints.maxHeight * .44,
color: Theme.of(context).primaryColor,
padding: EdgeInsets.only(bottom: 2),
child: Align(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildProfileImage(context),
SizedBox(height: 17),
Text(userName)
],
),
),
),
Expanded(
child: ListView(
children: <Widget>[
ListTile(
leading: Icon(Icons.print),
title: Text('asdad'),
)
],
),
)
],
),
),
);
},
),
),
]
);
}

Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ListView(
shrinkWrap: true,
children: <Widget>[
new Container(
height: MediaQuery.of(context).size.height/3,
color: Theme.of(context).primaryColor,
padding: EdgeInsets.only(bottom: 2),
child: Align(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildProfileImage(context),
SizedBox(height: 17),
Text(userName)
],
),
),
),
new ListView(
shrinkWrap: true,
children: <Widget>[
ListTile(
leading: Icon(Icons.print),
title: Text('asdad'),
)
],
)
],
),
);
},
);
}

If you read the error log, it says non-zero flex incoming but constraints are unbounded. To understand that clearly, imagine Flutter is trying to draw pixels of something that not finite. That's the our problem.
Widgets like ListView or SingleChildScrollView are flex widgets and has no limits unlike Column or Row.
If you have children that sizes are not definite, then you have to define flex for your ListView. For that, you can use shrinkWrap or Flexible, Expanded widgets for both ListView itself or children.
And here is the my solution, simplified as much as possible:
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return ListView(
shrinkWrap: true, // That's it
children: <Widget>[
Container(
color: Theme.of(context).primaryColor,
height: viewportConstraints.maxHeight * .44,
padding: EdgeInsets.only(bottom: 2),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildProfileImage(context),
SizedBox(height: 17),
Text(userName)
],
),
),
ListTile(
leading: Icon(Icons.print),
title: Text('asdad'),
),
ListTile(
leading: Icon(Icons.print),
title: Text('asdad'),
),
ListTile(
leading: Icon(Icons.print),
title: Text('asdad'),
)
],
);
},
);
}
Meanwhile, you have too many children that does nothing. If you want different scrolling behavior inside the Profile Info and ListTiles parts, tell me because we will must create yet another ListView for achieve that.
Also if you share your buildProfileImage widget, we can optimize your code even further, because you may even not need the LayoutBuilder widget in this case.

Related

Why I got The relevant error-causing widget was: Scaffold?

Hello everyone I want to dispaly some informations inside scaffold but I got
The relevant error-causing widget was: Scaffold.
Even when I remove it I still got errors:
this is the code :
Widget build(BuildContext context) {
return Scaffold(
body: ExpansionTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
widget.model.name,
overflow: TextOverflow.ellipsis,
),
Text(
widget.model.id.toString(),
style: Theme.of(context).textTheme.caption,
)
],
),
leading: SingleChildScrollView(
child: Column(
// children: [
// Text(widget.model.mtu.toString()),
children: <Widget>[
StreamBuilder(
stream: widget.model.mtu,
initialData: 0,
builder: (c, snapshot) => ListTile(
title: const Text('MTU Size'),
subtitle: Text('${snapshot.data} bytes'),
),
// ],
),
],
),
),
trailing: TextButton(
onPressed: () {
setState(() {
buttonText = 'Connecting...';
});
widget.viewModel.establishConnectAndEnableNotify(widget.model);
},
child: Text(
buttonText,
style: TextStyle(color: AppColors.blue),
).tr(),
)));
}
}
I would be very thankful if you can help me
The issue occurring because of using ListTile, it is trying to get infinite width. You can wrap it with SizedBox and provide width. Using LayoutBuilder on top level will be better to get the parent widget width.
return Scaffold(
body: LayoutBuilder(
builder: (_, constraints) => ExpansionTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[],
),
leading: SingleChildScrollView(
child: Column(
// children: [
// Text(widget.model.mtu.toString()),
children: <Widget>[
StreamBuilder(
// stream: "widget.model.mtu",
initialData: 0,
builder: (c, snapshot) => SizedBox(
width:
constraints.maxWidth * .2, //depend on you or hard-coded value
child: ListTile(
title: Text(' w ${constraints.maxWidth}'),
subtitle: Text('${snapshot.data} bytes'),
),
),
// ],
),
],
),
),
Also, I think you don't want to have SingleChildScrollView on leading, maybe you like to use it on children

Flutter Can't create a Listview to fill height but only have fixed width

I can't work this out. It should be really simple. (This is Flutter Desktop)
I want a Row with 2 columns. The first column should be full height available but only 200 wide. Column 2 should be full height and use the remainder of the width.
I have tried all sorts but always get an overflow rather than a scrollable list.
What am I doing wrong?
This is the last iteration of my code.
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bnode Management'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('This is the title'),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
width: 200,
child: ListView.builder(
shrinkWrap: true,
itemCount: piItems.length,
itemBuilder: (BuildContext context, int index) {
return Container(width: 40, color: Colors.red, child: Text(piItems[index].id));
}),
),
),
Container(color: Colors.blue, child: const Text('Column 1')),
],
),
],
),
);
}
}
The second Column like to have max space, therefor we can use Expanded on it instead of using on 1st Column. Also, the Row is needed to have max height. For this, we will wrap this Row with Expanded.
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('This is the title'),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 200,
child: ListView.builder(
shrinkWrap: true,
itemCount: piItems.length,
itemBuilder: (BuildContext context, int index) {
return Container(
width: 40,
color: Colors.red,
child: Text(piItems[index].id));
}),
),
Expanded(
child: Container(
color: Colors.blue,
child: Column(
children: [
const Text('Column 1'),
],
),
),
)
],
),
),
],
),
I also prefer using LayoutBuilder on body in this case.
You almost got it right, just mixed some things. Let's go step by step.
I want a Row with 2 columns
Ok, that's just a Row with two children.
Row(
children: [child1, child2],
)
The first column should be full height available but only 200 wide.
It would take the full height automatically but I see that in your code example you have a Column with Text title, so we should wrap our Row with Expanded. Also, the first Column should take 200 px.
Column(
children: [
const Text('This is the title'),
Expanded(
child: Row(
children: [
Container(
width: 200,
child: child1,
),
child2,
],
),
),
],
)
Column 2 should be full height and use the remainder of the width.
So we wrap our 2nd child with Expanded inside the Row.
Column(
children: [
const Text('This is the title'),
Expanded(
child: Row(
children: [
Container(
width: 200,
child: child1,
),
Expanded(
child: child2,
),
],
),
),
],
)
The end result based on your code:
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bnode Management'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('This is the title'),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 200,
child: ListView.builder(
shrinkWrap: true,
itemCount: piItems.length,
itemBuilder: (BuildContext context, int index) {
return Container(
width: 40,
color: Colors.red,
child: Text(piItems[index].id));
},
),
),
Expanded(
child: Container(
color: Colors.blue,
child: const Text('Column 1'),
),
),
],
),
),
],
),
);
}
}

Cannot put PageView into Column in Flutter

I want to put a small PageView into a Column, but I got error RenderBox was not laid out: RenderRepaintBoundary#db5f8 relayoutBoundary=up14 NEEDS-PAINT. The PageView will hold inconsistent data so I cannot set the height for it. Can anyone help me? This is my code:
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
bottomNavigationBar: BottomAppBar(
child: Container(
height: 70,
color: Colors.white,
),
),
body: FutureBuilder(
future: Data.getProductDetails(id: widget.product.id),
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator(color: Colors.grey));
} else {
List iList = [];
iList.add(widget.product.mainImg);
for (ProductImages i in snapshot.data.imgList) {
iList.add(i.image);
}
return CustomScrollView(
slivers: [
SliverAppBar(...),
SliverToBoxAdapter(
child: SingleChildScrollView(
padding: EdgeInsets.all(15),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(...),
Row(...),
SizedBox(height: 10),
brandField(snapshot),
SizedBox(height: 5),
categoryField(snapshot),
SizedBox(height: 5),
tagsField(snapshot),
SizedBox(height: 20),
pageIndicator(),
Expanded(
child: PageView(
children: [
Container(height: 400, color: Colors.red),
Container(height: 600, color: Colors.blue),
],
),
)
],
),
),
),
],
);
}
},
),
);
}
You get this error because PageView and other widgets like ListView require size constraints from its parents and Column do not provide constraints to its children. So to solve the issue you can wrap you PageView inside Flexible or Expanded like so:
Column(
children:[
Expanded(child:PageView(),),
]
)
For better understanding of constrains in flutter, read: Understanding Constrains: Flutter.dev

Flutter avoiding resize screen when i have TextFormField into PageView

in this simplified code i have two TextFormField into one of flutter PageView pages, that they are into another class which that extended from Container, not any Scaffold, now when i click into one of this TextFormFields, resizeToAvoidBottom* don't work in my code
Widget build(BuildContext context) {
return Consumer<ThemeManager>(builder: (context, theme, child) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
key: _scaffoldKey,
backgroundColor: theme.accentColor,
resizeToAvoidBottomPadding: false,
resizeToAvoidBottomInset: false,
body: NotificationListener<OverscrollIndicatorNotification>(
onNotification: (overScroll) {
overScroll.disallowGlow();
return false;
},
child: Container(
width: double.infinity,
height: double.infinity,
child: IntrinsicHeight(
child: Stack(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height - 50,
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
BuildLoginHeaderScreen(theme: theme),
Expanded(
child: PageView(
controller: _pageController,
children: <Widget>[
... // contains two TextFormFields
],
),
),
],
),
),
],
),
),
),
),
),
);
});
}
You can go around using resizeToAvoidBottomPadding by wrapping appropriate part of your code with SingleChildScrollView. For example
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
BuildLoginHeaderScreen(theme: theme),
Expanded(
child: PageView(
controller: _pageController,
children: <Widget>[
... // contains two TextFormFields
],
),
),
],
),
),
This will prevent pixel overflow when the keyboard reduces the view area.

Flutter dynamic height of ListView

I am developing a Flutter application and I want to dynamically adjust the height of the ListView.
I want the list to have a maximum height of 200. If the height is more than that, user will have to scroll to reach the bottom. If height is below 200, it will take only as much space as needed.
Preview of application: Screenshot. As you can see Restaurants nearby is pushed to the very bottom of the page. I want the ListView to only take height of 200 or less so that the content below isn't pushed to the bottom.
Here is my current code:
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Restaurants nearby',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Enter restaurant manually'),
onPressed: () {
print('Button pressed');
},
),
],
),
Flexible(
child: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.cyan,
),
title: Text('Test restaurant'),
subtitle: Text('80m'),
);
},
itemCount: 15,
),
),
Text(
'Restaurants nearby',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
],
),
);
}
You are using Flexible widget, that's why your ListView expands. You have to change Flexible to ConstrainedBox and add shrinkWrap: true to your ListView.
ConstrainedBox(
constraints: BoxConstraints(maxHeight: 200, minHeight: 56.0),
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.cyan,
),
title: Text('Test restaurant'),
subtitle: Text('80m'),
);
},
itemCount: 15,
),
),
More info here: https://api.flutter.dev/flutter/widgets/ConstrainedBox-class.html
You can use LimitedBox
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(...),
Divider(),
Row(...),
LimitedBox(
maxHeight: 200.0,
child: ListView.builder(...),
),
Text(...),
],
),
Recommended solution in case when the incoming constraints are unbounded
Consider wrapping the ListView into this
LimitedBox(
maxHeight: 200,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.cyan,
),
title: Text('Test restaurant'),
subtitle: Text('80m'),
);
},
itemCount: _itemsCount,
),
),
]
)
),
Note that:
shrinkWrap of ListView is set to true
mainAxisSize of Column is set to MainAxisSize.min
maxHeight of LimitedBox is set to 200
A complete snippet:
import 'package:flutter/material.dart';
class DebugWidget extends StatefulWidget {
#override
_DebugWidgetState createState() => _DebugWidgetState();
}
class _DebugWidgetState extends State<DebugWidget> {
int _itemsCount = 1;
#override
Widget build(BuildContext context) {
Widget child = Container(
padding: EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Restaurants nearby',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Enter restaurant manually'),
onPressed: () {
print('Button pressed');
},
),
RaisedButton(
child: Text('+1'),
onPressed: () {
setState(() {
_itemsCount += 1;
});
},
),
RaisedButton(
child: Text('-1'),
onPressed: () {
setState(() {
_itemsCount -= 1;
});
},
),
],
),
LimitedBox(
maxHeight: 200,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.cyan,
),
title: Text('Test restaurant'),
subtitle: Text('80m'),
);
},
itemCount: _itemsCount,
),
),
]
)
),
Text(
'Restaurants nearby',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
],
),
);
return Scaffold(
body: child,
);
}
}
I found a solution to this problem. you should wrap your ListView with LimittedBox or ConstraintBox and give them maxHeight and set shrinkWrap property of ListView to true. the solution would be something like this.
LimitedBox(
maxHeight: 200,
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.cyan,
),
title: Text('Test restaurant'),
subtitle: Text('80m'),
);
},
itemCount: 15,
),
),
I see that this question has never been answered only with giving a fixed height, so here is what works for me.
For some reason if you set the shrinkwrap to true it doesn't look like it is working but it does, the problem is in the padding settings of the ListView, set the padding to edgeinstets.zero. That fixes it for me.
Wrap inside a Flexible
ShrinkWrap true
Padding, zero
and if needed the column to MainAxisSize.min.
Hope it helps some people.
Example of my code:
Flexible(
child: Container(
decoration: BStyles.cardDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text(
'PRODUCTION TASKS',
),
const SizedBox(
height: textVerticalSpacing,
),
Flexible(
child: ListView.builder(
itemCount: recentTasks.length,
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return TaskCard(
task: recentTasks[index],
widthInfo: MediaQuery.of(context).size.width * 0.6,
);
},
),
),
const SizedBox(
height: itemSpacing,
),
Align(
alignment: Alignment.centerRight,
child: InkWell(
onTap: () { },
child: const Text(
'View more',
),
),
),
const SizedBox(
height: textVerticalSpacing,
),
],
),
),
),
),
ConstrainedBox(
constraints: BoxConstraints(maxHeight: 200.0),
child: [your child here],
)
This make your child's height not bigger than 200.0
You can always size the ListView container as % of the viewport (assuming of course that the other widgets also are sized in the same manner):
return Container(
height: MediaQuery.of(context).size.height * 0.75,
child: ListView.builder(
itemBuilder: (ctx, index) {
return Card(...