I need to set a Column width in flutter, I have to do a layout with 3 sections, one should be 20% of the screen, the other one 60% and the last one 20%.
I know that those 3 columns should be into a row, but I don't know a way to set the size, when I do that, the 3 columns take the same size.
I will appreciate any feedback.
Instead of hard-coding the size, I would suggest using Flex like
Row(
children: <Widget>[
Expanded(
flex: 2, // 20%
child: Container(color: Colors.red),
),
Expanded(
flex: 6, // 60%
child: Container(color: Colors.green),
),
Expanded(
flex: 2, // 20%
child: Container(color: Colors.blue),
)
],
)
Which will produce like below,
Limiting the width of a Column could be
Limiting the width of Column itself, use SizedBox
SizedBox(
width: 100, // set this
child: Column(...),
)
2 (A). Limiting width of children inside Column, without hardcoding values
Row(
children: <Widget>[
Expanded(
flex: 3, // takes 30% of available width
child: Child1(),
),
Expanded(
flex: 7, // takes 70% of available width
child: Child2(),
),
],
)
2 (B). Limiting width of children inside Column, with hardcoding values.
Row(
children: <Widget>[
SizedBox(
width: 100, // hard coding child width
child: Child1(),
),
SizedBox(
width: 200, // hard coding child width
child: Child2(),
),
],
)
Just wrap it with a Row
Row( // this row has full width
children: [
Column(
children: [...]
)
])
This is not an answer to the original question but demonstrating a similar use case.
I have a container and I want to expand the width until certain value. If width gets bigger I want container to be always in the middle. This is useful when rendering forms especially on web and desktop applications.
import 'package:flutter/material.dart';
import 'dart:math' as math;
var index = 0;
Widget buildContainer() { // Just a placeholder with random colour
index++;
return Container(
height: 60,
margin: const EdgeInsets.only(right: 5),
color: Colors.primaries[math.Random().nextInt(Colors.primaries.length)],
child: Text("$index"),
);
}
Widget containers() {
return Row(
children: [
Expanded(child: buildContainer(),
flex: 2), // <- Control the width of each item. See other answers.
Expanded(child: buildContainer(), flex: 3,)
],
);
}
class FormLayout extends StatelessWidget {
const FormLayout({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center( //<- Centre the form
child: SizedBox(
width: 400, //<- Limit the width
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [containers()]),
),
);
}
}
Related
I'm getting images from list and display it using Gridview, and I want if my imageList lenght is less than 4 then a button will display after the last image, that should be in row. here is my code. I warp the row widget into Expanded, but it gives error on where i call addProductsImages and that's wrap on column widget.
List imageList=["assets/img/pepsi2.jpg","assets/img/pepsi2.jpg"];
addProductsImages(){
if(imageList.length!=0){
Row(
children: [
SizedBox(
height:80,
width: MediaQuery.of(context).size.width*0.8,
child: GridView.builder(
shrinkWrap:true,
itemCount: imageList.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount:4, ),
itemBuilder: (BuildContext context,int index){
return Padding(
padding: const EdgeInsets.all(8.0),
child: Stack(
children:[
ClipRRect(borderRadius: BorderRadius.circular(10.0),
child: Image(image: AssetImage(imageList[index]))),
Align(
alignment: Alignment.topRight,
child: buildCancelIcon(
color,
() {
setState(() {
imageList.removeAt(index);
print(imageList[index]);
});
},
Icons.cancel
))]
),
);})),
Container(
width: MediaQuery.of(context).size.width * 0.5,
child: Align(
alignment: Alignment.topLeft,
child: customImageButton(
context,
"+",
() {
pickMultipleImage();},
MediaQuery.of(context).size.width * 0.3,
MediaQuery.of(context).size.height * 0.13)),
),
SizedBox10(),
]);}
else {
return Padding(
padding: const EdgeInsets.only(left: 70),
child:
Row(crossAxisAlignment: CrossAxisAlignment.center, children: []));
}
}
Error:
A RenderFlex overflowed by 151 pixels on the right.
Ouptut:
please help how to do this.
remove your width of SizedBox and wrap Expanded to it:
Expanded(child:
SizedBox(
height:80,
child: GridView.builder(
I want to recreate a ListView as shown below. However when I'm trying to replicate it, my text is overflowing and the image isn't getting aligned with the text.
This is my code:
Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Card(
color: Colors.red,
child: ListView.builder(
padding: EdgeInsets.all(10.0),
itemCount: filteredUsers.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>\[
Row(
children: <Widget>\[
CircleAvatar(),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>\[
Text(
'Hello John',
style: TextStyle(
fontSize: 14.0,
color: Colors.grey,
),
),
SizedBox(
height: 5.0,
),
Text(
'the fox ran over the tree and jumped over the dog and cat that were sleeping under it ',
style: TextStyle(
fontSize: 16.0,
color: Colors.black,
),
),
SizedBox(
height: 5.0,
),
\],
),
\],
),
\],
),
);
},
),
),
),
),
This is my output:
Trying to achieve something similar to this:
There is ListTile available for the exact purpose.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: ListView(children: [
DemoListTile(),
DemoListTile(),
DemoListTile(),
DemoListTile(),
])),
);
}
}
class DemoListTile extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListTile(
title: const Text("Hello World"),
isThreeLine:
true, //will fix the alignment if the subtitle text is too big
subtitle: const Text(
"Flutter demo big ss text here and there to break things everywhere, but still can't break things here"),
leading: CircleAvatar(backgroundColor: Colors.blue),
trailing: Icon(Icons.arrow_right));
}
}
which gives
To make text overflowing i use often the AutoSizeText Package:
https://pub.dev/packages/auto_size_text
With the AutoSizeText Widget your code can look like this:
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Container(
width: MediaQuery.of(context).size.width - 120,
child: AutoSizeText(
'the fox ran over the tree and jumped over the dog and cat that were sleeping under it ',
style: TextStyle(
fontSize: 16.0,
color: Colors.black,
),
textAlign: TextAlign.justify,
maxLines: 5,
),
),
),
With MediaQuery you can give the Container a dynamic width depending on your device width.
Optionale: You can give the Container a padding and you can set the textAlign property to justify for better look.
As someone already has mentioned, ListTile() would do the job perfectly.
If you want to stick with your own layout for the ListView elements, I would recommend you to you use the flex()-Widget for position the different parts inside the Column and to use the Expanded()-Widget for moving something (like an arrow) to the end of the list element.
Of course you can also use flex()-Widgets for the row to push the arrow to the end of the list element.
To prevent text overflow, just use the TextOverflow.ellipsis or something similar.
Basically i want to animate an AnimatedContainer's height between 2 values. But here is the problem. When my state is 1 i know the height so i can animate but when my state is 0 i want animated container to expand to available space. I tried to wrap my animated container with Expanded widget but that didn't work.
class _PreviewScreenState extends State<PreviewScreen> {
var selectedTab = 1;
#override
Widget build(BuildContext context) {
double imageWidth = MediaQuery.of(context).size.width;
double imageHeight = selectedTab == 1 ? imageWidth : null;
return Scaffold(
body: DefaultTabController(
length: 3,
initialIndex: selectedTab,
child: Background(
child: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
title: Text('SHARE'),
),
Expanded(
child: AnimatedContainer(
height: imageHeight,
duration: Duration(milliseconds: 600),
color: Colors.red,
),
),
TabBar(
labelStyle: TextStyle(fontSize: 13),
indicator: BoxDecoration(
color: Colors.white24,
borderRadius: BorderRadius.circular(40),
),
onTap: (index) {
setState(() {
selectedTab = index;
});
},
tabs: <Widget>[
Tab(child: Text('INSTAGRAM')),
Tab(child: Text('SQUARE')),
Tab(child: Text('OTHER'))
],
),
Container(
height: 100,
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: ShareButton(
onPressed: () {},
),
),
),
)
],
),
),
),
),
);
}
}
You can use a Flexible widget instead of an Expanded widget. It gives the child "the flexibility to expand to fill the available space in the main axis, but, unlike Expanded, Flexible does not require the child to fill the available space."
Also, you should switch from AnimatedContainer to AnimatedSize as AnimatedContainer throws an error interpolating between double.infinity and a constant height.
So this
Expanded(
child: AnimatedContainer(
height: imageHeight,
duration: Duration(milliseconds: 600),
color: Colors.red,
),
),
will be come
Flexible(
child: AnimatedSize(
vsync: this,
duration: Duration(milliseconds: 600),
child: Container(
height: imageHeight,
color: Colors.red,),
),
),
For this to work, your _PreviewScreenState has to use the SingleTickerProviderStateMixin mixin and your imageHeight logic will have to change from null to double.infinity for the filling the available space.
i.e you will have:
class _PreviewScreenState extends State<PreviewScreen> with SingleTickerProviderStateMixin{
//rest of your code
}
and
double imageHeight = selectedTab == 1 ? imageWidth : double.infinity;
Here is a DartPad demonstration: https://dartpad.dev/bf4f969f76ab3092d0b1960bfdbf7825
I am new to animations in Flutter and did not figure out how to animate a widget movement between two states.
If I run the code below, the column's first child will go invisible and the RaisedButton will appear where the first widget was before.
How do I tell Flutter to animate this so that the RaisedButton will move upwards instead of just appearing there?
All solutions I found were way too complicated in my opinion...
bool visible = true;
Widget widget1, widget2;
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Visibility(
visible: visible,
child: widget1,
),
RaisedButton(
child: widget2,
onPressed: () => setState(() => visible = !visible),
),
],
)
I hope this helps
double height = 200;
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
AnimatedContainer(
duration: Duration(seconds: 3),
height: height,
child: Container(height: 200, width: 200, color: Colors.blue),
),
RaisedButton(
child: Container(height: 200, width: 200, color: Colors.yellow),
onPressed: () => setState(() => height = height == 0 ? 200 : 0),
),
],
);
}
Here is the result:
Widget build(context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 300,
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: color ?? Colors.blue,
borderRadius: BorderRadius.circular(10)
),
child: msg
)
],
);
}
This is build method of my widget and It renders this UI depending on what I pass as msg paramter
Loading text
Some very long text
Now the issue I am facing is that I am not able to wrap the text inside this container/blue box without setting it's width but If I set width of container/blue box then it will always stay that wide no matter how short the text is.
Now is there a way to set maxWidth (like let's say 500) of container/blue box? So that the blue box will become as wide as required for small text like "Loading" and for long text it will expand till it reaches width of 500 units and then will wrap the text ?
Required output for long text:
For small text like loading I dont want any change in UI but for long text I want it to look like this.
You can add a constraint to the Container Widget with the preferred maxWidth like this:
Widget build(context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
constraints: BoxConstraints(minWidth: 100, maxWidth: 200),
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: color ?? Colors.blue,
borderRadius: BorderRadius.circular(10)
),
child: msg
)
],
);
}
Use ConstrainedBox with BoxConstraints maxWidth, wrapped in a Flexible() widget. The Flexible widget allows for the box to resize for smaller screens as well.
Flexible(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 150),
child: Container(
color : Colors.blue,
child: Text('Your Text here'),
),
),
),
This required to be inside Row or Column Widget
1. Row
Row(
children: [
Container(
constraints: BoxConstraints(maxWidth: 300),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0)),
child: Text("Long Text....")
),
],
);
2. Column
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
constraints: BoxConstraints(maxWidth: 300),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0)),
child: Text("Long Text....")
),
],
);
Here in both examples, it's working because Column and Row allow it's children to expand to it's given constraint/size.
Note: If a child wants a different size from its parent and the parent doesn’t have enough information to align it, then the child’s size might be ignored.
Here's a simple method I use for long texts:
ConstrainedBox(
constraints: BoxConstraints(maxWidth: 200),
child: Container(
child: Text(
'Long string of text',
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
)
Note the maxLines attribute which might be helpful should you wish to show just a single line of text.
Use Constraints property in Container widget.
use maxWidth & minWidth value to achieve the requirement.
Container(
constraints: BoxConstraints(minWidth: 150, maxWidth: 300),
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5)),
child: Text("")
)
This is how i fixed it
Center(
child: Container(
child: this.child,
width: 300,
height: double.infinity,
),
)
For me at least putting the Constrained box in IntrinsicWidth has solved issue.
IntrinsicWidth(
child: Container(
constraints: BoxConstraints(maxWidth: 200),
child: Row(
children: [
],
),
),
);
Maybe use a Flexible around the text and then wrap the Flexible in a fixed size containter? Just an idea.
I know it's late, but someone may get help in the future.
I used the auto_size_text plugin to get it done:
AutoSizeText(
'A really long String',
style: TextStyle(fontSize: 30),
minFontSize: 18,
maxLines: 4,
overflow: TextOverflow.ellipsis,
)
In my case my solution was this:
Wrapped in Row and add Spacer (in comment bubble widget I didn't define either width any max-width. it grows depends on the text.
to limit width added padding to comment bubble.
return Row(
children: [
CommentBubble(
chatText: snapshot.get('text'),
sentAt: snapshot.get('sentAt'),
sendBy: snapshot.get('sendBy'),
),
Spacer(),
],
);