ListView in LimitedBox vs ConstrainedBox - flutter

While trying to figure out how to limit the size of a ListView, I saw examples in Stackoverflow suggesting that doing something like placing the ListView in a LimitedBox and setting the maxHeight of the Limited box and the shrinkwrap of the ListView, would accomplish it. However, the ListView would still grow past the maxheight to accommodate the size of the children in the ListView.
Eventually, I tried an example using ConstrainedBox and setting it's maxHeight and the Shrinkwrap of the ListView; doing this seemed to accomplish the intended result of limiting the Listview height to the maxHeight setting.
Can someone help me understand why LimitedBox maxHeight does not work in this case, when ConstrainedBox does?
Here is an example of the LimitedBox that is not constraining itself to the maxHeight:
Widget _getPopupCard(){
return Hero(
tag: 0,
createRectTween: (begin,end){
return MaterialRectCenterArcTween(begin: begin, end: end);
},
child: Container(
child:Padding(
padding: EdgeInsets.all(14),
child: Material(
borderRadius: BorderRadius.circular(18),
color: Colors.white,
child: LimitedBox(
maxHeight: 300,
child: ListView(
shrinkWrap: true,
children: _getTiles(),
),
),
),
),
),
);
}
Here is an example of the ConstrainedBox version that does limit itself to the maxHeight:
Widget _getPopupCard(){
return Hero(
tag: 0,
createRectTween: (begin,end){
return MaterialRectCenterArcTween(begin: begin, end: end);
},
child: Container(
child:Padding(
padding: EdgeInsets.all(14),
child: Material(
borderRadius: BorderRadius.circular(18),
color: Colors.white,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 300.0),
child: ListView(
shrinkWrap: true,
children: _getTiles(),
),
),
),
),
),
);
}

https://api.flutter-io.cn/flutter/widgets/LimitedBox-class.html
LimitedBox : A box that limits its size only when it's unconstrained.
ConstrainedBox : which applies its constraints in all cases, not just when the incoming constraints are unbounded.
LimitedBox can only works when parent is unconstrainted and flutter cannot render infinity width/heigh, so LimitedBox must both provide MaxWidth and MaxHeight
You can wrap UnconstrainedBox to make LimitedBox works, but you really don't need this... see: https://www.woolha.com/tutorials/flutter-using-limitedbox-widget-examples
https://dartpad.dev/2cfc997a7984bf848687f209cba917ce?null_safety=true
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'LimitedBox',
home: Scaffold(
appBar: AppBar(
title:
Text('LimitedBox can only work when parent is unconstrainted')),
body: Center(
child: UnconstrainedBox( // wrap UnconstrainedBox to make LimitedBox works, but you really don't need this... https://www.woolha.com/tutorials/flutter-using-limitedbox-widget-examples
child: _getPopupCard(),
),
),
),
);
}
}
Widget _getPopupCard() {
return LimitedBox(
maxHeight: 300.0,
maxWidth: 300.0,
child: ListView(
shrinkWrap: true,
children: _getTiles(),
),
);
}
List<Widget> _getTiles() {
return List.generate(100, (int i) => i).map((int i) {
return Container(
color: i % 2 == 0 ? Colors.red : Colors.green,
child: Center(child: Text("$i")),
);
}).toList();
}
update:
Is Container the constrained parent that is overriding the LimitedBox even though I didn't set height or width on the Container?
I search this question just because I'm reading this guide: https://flutter.dev/docs/development/ui/layout/constraints
Container's layout behavior is little complex.
About this, I've run code below , seems flutter will give Container a default Constraints. The Container source tells something, maybe you should get some info from the source.
runApp(
MaterialApp(
home: UnconstrainedBox(
child: LayoutBuilder(
builder: (ctx, Constraints constraints) {
print(ctx);
print(constraints);//BoxConstraints(unconstrained)
return Text("hello world");
},
),
),
),
);
runApp(
MaterialApp(
home: Container(
color: Colors.white,
child: LayoutBuilder(
builder: (ctx, constraints) {
print(ctx);
print(constraints); // BoxConstraints(w=500.0, h=296.0)
return Text("hello world333333");
},
),
),
),
);

Related

How can I remove the visible border between two containers in a column?

does anyone know how to solve it so that there is no border between two elements in a column in the website? Thanks for any advice
Image
Code Here
class DesktopScreen extends StatelessWidget {
const DesktopScreen({super.key});
#override
Widget build(BuildContext context) {
return Sizer(
builder: (context, orientation, deviceType) {
return Scaffold(
body: Column(
children: [
Container(color: HexColor("#111340"), height: 5.h, child: Row(children: [],),),
Container(color: HexColor("#111340"), height: 95.h,)
],
)
);
}
);
}
}
I'm a newbie, so I apologize for any mistakes in the post
I don't think there's any error in the code you posted, the little space between the elements should not be there.
Here's your code running, there's no space between the containers:
https://zapp.run/edit/flutter-z8206198306?entry=lib/main.dart&file=lib/main.dart
I think the problem could be related to the extension you are using for providing height, or to the Sizer widget.
If you want to handle responsiveness, I suggest to look at this package: https://pub.dev/packages/layout
Use Flexible
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Flexible(
child: Container(
color: Colors.red,
),
),
Flexible(
flex: 4,
child: Container(
color: Colors.green,
),
),
],
),
);}

How to make the application responsive using Stack and Positoned - Flutter

How to use Stack and Positioned to add a shape in the SafeArea, I tried to change the color of the AppBar and connect to the shape and add mediaQuery, but still not on every screen it will be properly connected. So how to get a svg photo on the entire surface of SafeArea, and to make it responsive without using appbar, is it necessary to get the effect like in the picture below?(the code gives the effect as in the picture, but it is not responsive and consists of two parts, and I would like one part and get responsive)
Any help very much appreciated.
class Shape extends StatelessWidget {
static Route route() {
return MaterialPageRoute<void>(builder: (_) => Shape());
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
elevation: 0,
actions: [],
),
body: _profilePage(context),
);
}
Widget _profilePage(BuildContext context) {
return SafeArea(
child: Align(
child: Center(
child: Stack(
children: <Widget>[
Positioned(
width: MediaQuery.of(context).size.width * 1,
height: MediaQuery.of(context).size.height,
bottom: MediaQuery.of(context).size.width * 0.6,
child: _curved(context),
),
],
),
),
),
);
}
Widget _curved(BuildContext context) {
return SvgPicture.asset(
'assets/images/shape_purple.svg',
color: Colors.blue,
allowDrawingOutsideViewBox: true,
);
}
Use FitteBox Widget instead
FittedBox(
child: Image.asset('assets/images/background.png'),
fit: BoxFit.fill,
// decoration: BoxDecoration(
// color: Colors.white),
),

What is the difference between the GrideView.custom & GrideView.extent widget layout in flutter?

What is the difference between the GridView.custom & GridView.extent widget layout in flutter?
If you answer this question please attach the example code
and clear explanation.
GridView.extent
#override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: Text('Grid'),
),
body: Container(
child: GridView.extent(
maxCrossAxisExtent: width / 3,
children: List.generate(
20,
(index) => Container(
color: Colors.amber,
margin: const EdgeInsets.all(5),
alignment: Alignment.center,
child: Text('Widget : $index'),
),
),
),
),
);
}
In GridView.extent, we use maxCrossAxisExtent to set the maximum space we want to give to each children. Here we are giving a maxCrossAxisExtent: width / 3, that means we are giving each children 1/3 of the width available.
GridView.custom
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Grid'),
),
body: Container(
child: GridView.custom(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
childrenDelegate: SliverChildBuilderDelegate(
(context, index) {
return Container(
color: Colors.amber,
margin: const EdgeInsets.all(5),
alignment: Alignment.center,
child: Text('Widget $index'),
);
},
childCount: 200,
),
),
),
);
}
In GridView.custom, we get option to use SliverGridDelegate gridDelegate and SliverChildDelegate childrenDelegate.
For SliverGridDelegate you can use :
SliverGridDelegateWithFixedCrossAxisCount - No. of children in the cross axis
SliverGridDelegateWithMaxCrossAxisExtent - Max cross axis space for each children (Same that we re doing using GridView.extent)
For SliverChildDelegate you can use :
SliverChildBuilderDelegate - Provides builder callback to construct the children
SliverChildListDelegate - Provides option to make explicit list of children.
Both of the above code generates the same output.Grid.extent is just a shorthand to create grid layout with fixed children size while we can do the same thing using GridView.custom as it is more flexible and you can do more things using it.

ListView or SingleChildScrollView of variable size

I want to have a widget of variable height that contains a ListView or a SingleChildScrollView or anything that scrolls.
I tried making it like this:
Container(
color: Colors.pink,
child: Column(
children: [
Container(color: Colors.orange, child: Text("Header")),
SingleChildScrollView(
child: Container(
height: 10000,
color: Colors.green,
child: Text("the height of this content could be anything")),
),
Container(color: Colors.blue, child: Text("Footer")),
],
),
)
This causes an overflow because the SingleChildScrollView expands to height of 10000 pixels. If I enclose it in an Expanded then it works fine but then if its child's height is for example 200 instead of 10000, it will still expand the parent widget to the entire height of the screen.
Is it possible to have the height of the scroll/list adjust itself to its content and only expand to the entire screen if it needs to?
You can do it if you know the size of the footer and header widget and using LayoutBuilder widget to get the constraints.
#override
Widget build(BuildContext newcontext) {
return Center(
child: Scaffold(
body: Container(
color: Colors.pink,
child: LayoutBuilder(
builder: (_, constraints) {
final sizeHeader = 150.0;
final sizeFooter = 150.0;
final sizeList = 1000.0;
final available =
constraints.maxHeight - (sizeHeader + sizeFooter);
Widget _buildCenterWidget() {
return Container(
height: sizeList,
color: Colors.green,
child: Text("the height of this content could be anything"),
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
height: sizeHeader,
color: Colors.orange,
child: Text("Header")),
available < sizeList
? Expanded(
child: _buildCenterWidget(),
)
: _buildCenterWidget(),
Container(
height: sizeFooter,
color: Colors.blue,
child: Text("Footer")),
],
);
},
)),
),
);
}
You can use ConstrainedBox, to specify minHeight, maxHeight for your widget. Remember that none of your widget should have infinite height/width, that spoils the UI, may also throw error

Specific min and max size for expanded widgets in Column

I have a Column with a set of Expanded widgets.
Is there a way to control the range in which they expand? I want one widget to expand only to a certain size and make the rest available to other widgets.
EDIT:
Because I got two probably misleading answers, I’d like to clarify. I want something like this:
Expanded(flex: 1, minSize: 50, maxSize: 200, child: ...)
That means that this expanded widget takes a flex of 1, but should never be smaller than 50 and bigger than 200.
When using ConstrainedBox in Rows my minWidth is ignored and the maxWidth is used as a fixed size.
You are looking for ConstrainedBox.
You can create a List of Widgets with both ConstrainedBox and Expanded, as following:
Row(
children: [
ConstrainedBox(
child: Container(color: Colors.red),
constraints: BoxConstraints(
minWidth: 50,
maxWidth: 100,
),
),
Expanded(
child: Container(color: Colors.green),
),
Expanded(
child: Container(color: Colors.blue),
),
],
),
As far as I know, there's no elegant pre-built way in Flutter to do this.
The answer by #HugoPassos is only partially complete. A ConstrainedBox will not change its size unless its content changes size. I believe what you're looking for is for the box to be say 1 / 4 of the width of row if 1/4 of the row is greater than the min and higher than the max.
Here's a working main.dart that get's the job done with width in a row, though you could just as easily use height in a column:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({required this.title});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: LayoutBuilder(builder: (context, constraints) {
return Center(
child: Row(
children: [
ConstrainedWidthFlexible(
minWidth: 50,
maxWidth: 200,
flex: 1,
flexSum: 4,
outerConstraints: constraints,
child: SizeLogger(
child: Container(
color: Colors.red,
width: Size.infinite.width,
height: Size.infinite.height,
child: Text('click me to log my width')),
),
),
Flexible(
flex: 1,
fit: FlexFit.tight,
child: Container(color: Colors.green),
),
Flexible(
flex: 2,
fit: FlexFit.tight,
child: Container(color: Colors.blue),
),
],
));
}));
}
}
class SizeLogger extends StatelessWidget {
final Widget child;
SizeLogger({required this.child});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => {print('context.size!.width ${context.size!.width}')},
child: child);
}
}
class ConstrainedWidthFlexible extends StatelessWidget {
final double minWidth;
final double maxWidth;
final int flex;
final int flexSum;
final Widget child;
final BoxConstraints outerConstraints;
ConstrainedWidthFlexible(
{required this.minWidth,
required this.maxWidth,
required this.flex,
required this.flexSum,
required this.outerConstraints,
required this.child});
#override
Widget build(BuildContext context) {
return ConstrainedBox(
constraints: BoxConstraints(
minWidth: minWidth,
maxWidth: maxWidth,
),
child: Container(
width: _getWidth(outerConstraints.maxWidth),
child: child,
),
);
}
double _getWidth(double outerContainerWidth) {
return outerContainerWidth * flex / flexSum;
}
}
In short: there is no simple answer without calulating the size.
First you need to know: Widget with Size dominate the avialable size in Row/Column, then Flexiable/Expanded share the remaining space.
Column(
children:[
Flexiable(...
Expanded(...
SizedBox(... // <- dominate the avialable size first
]
)
And the parent widget dominate the size of the child widget:
Column(
children:[
Flexiable(flex: 1),
Flexiable(
flex: 1,
child: SizedBox(... // size can't be larger than 1/2
]
)
It is the choise problem if the size exceed or insufficient. I can show some simple examples below:
(BTW: I replace ConstraintedBox with SizedBox because we only use maxWidth/maxHeight. check Understanding constraints)
Flex with max size
In this case is simple and can use only Flexible + SizedBox
Row(
children: [
Flexible(flex: 1, child: _textWidget('Flex:1')),
Flexible(
flex: 1,
child: SizedBox(
width: 300,
child: _textWidget('Flex: 1, max: 300'),
),
),
],
),
Flex with min/max size
For the case need the total size(from LayoutBuilder) and the percentage of the widget size.
LayoutBuilder(
builder: (context, constraint) {
final maxWidth = constraint.maxWidth;
return Row(
children: [
Flexible(flex: 1, child: _textWidget('Flex:1')),
SizedBox(
width: (maxWidth / 3).clamp(200, 300),
child: _textWidget('Flex:1, min: 200, max: 300'),
),
SizedBox(
width: (maxWidth / 3).clamp(200, 300),
child: _textWidget('Flex:1, min: 200, max: 300'),
),
],
);
}
)
Code Example
https://dartpad.dev/?id=f098f9764acda1bcc58017aa0bc0ec09
Yes! There is a way to control maxHeight and maxWidth inside a Row or Column (unbounded Widgets). You could use the Widget LimitedBox in which your maxHeight and maxWidth parameters only works inside unbounded Widgets.
https://api.flutter.dev/flutter/widgets/LimitedBox-class.html
Column(
children: [
LimitedBox(
maxHeight: 200,
maxWidth: 200,
child: Container(),
)
],
),
This worked for me. Please, Check it out.
Expanded(
child: Align(
alignment: Alignment.topCenter,
child: Container(
child: ConstrainedBox(
constraints:
BoxConstraints(maxHeight: 500),
child: Container(
child: DesiredWidget(),
),
),
),
),
)
Instead of directly expanding the desired widget, you should expand and align a container, then set the constrainedbox as a child of the container and then insert the desired widget as a child of the constrainedbox.
This way i managed to render the widget precisely as big as it needs to be, but never exceeding 500 height.
You can use constraint box to use the range of min and max width like below:
Row(
children: <Widget>[
Text("Text 1"),
ConstrainedBox(
constraints: BoxConstraints(maxHeight: 30, maxWidth: 40, minWidth: 30),
),
Text("Text 2")
],
)