How to shrink first child in a Column? - flutter

I had a strange bug with a layout of a Column, after which I decided to check out documentation - was surprised to to see how Flex lays out zero-flex elements. Anyway, here is the question.
How do you shrink the top element, i.e. layout it with the minimal possible size? I.e. that can be a top menu with content pane following it below,
Column(
children: [
/// menu is given unlimited constraints, so it may try to expand or may fail to layout
menu,
Expanded(child: content)
]
)
Codepen Demo showcases the problem
Another way to formulate the question: how do you keep dominance of the Expanded widget? Here, the LimitedBox can eat as much space as it wants. This is one of the problems of the Column widget.

Try this,let me know if it works for you
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
height: MediaQuery.of(context).size.height*0.1,
width: double.infinity,
color: Colors.amber,
child: Center(child: Text('menu'))),
Column(children: [Text('wow')]),
Text(
'wow',
),
]),
));

Use ConstrainedBox and give maxHeight a value of maximum available space.
LayoutBuilder(
builder: (_, constraints) {
return Column(
children: [
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: constraints.maxHeight,
),
child: Text('Your menu'),
),
Expanded(
child: Container(color: Colors.blue),
),
],
);
},
)

When you don't set size for widget in column It use minimum size as it required. I tested your code and removed LimitedBox,SizedBox and Spacer. I just wrap your Text with Container to show result easily.
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(children: [
Container(color: Colors.red, child: Center(child: Text('menu'))),
Column(children: [Text('wow')]),
Expanded(
child: Center(
child: Text(
'wow',
),
))
]),
),
);
}

Related

How to create a scrollable Column in Flutter that if it doesn't take up all the space it expands somewhere?

I'm trying to create a scrollable column that has a widget at the bottom of the screen if it does not fill it up. However, if it does it scrolls.
First I tried with a simple Column with MainAxisSize.max and Expand with an empty Container which worked well until the column overflowed.
[...]
Column(
mainAxisSize: MainAxisSize.max,
children: [
MyWidget(),
MyWidget(),
[...],
MyWidget(),
Expand(child: Container()),
ElevatedButton()
],
)
Then I searched and found the recommended solution for making a Column scrollable is the SingleChildScrollView. This made all of my widgets in the column disappear (which I am still unsure why) until I removed the Expand which although solved one problem, introduced another.
[...]
SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
MyWidget(),
MyWidget(),
[...],
MyWidget(),
// Expand(child: Container()),
ElevatedButton()
],
)
)
So my question is, how can I make a Column both scrollable and if it does not need all the space to expand so that the last child is at the bottom of the screen?
IntrinsicHeight is too expensive to use but in this situation you have no choice. Try this:
LayoutBuilder(builder: (context, constraint) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraint.maxHeight),
child: IntrinsicHeight(
child: Column(
children: [
Container(
color: Colors.red,
height: 400,
width: double.infinity,
),
Expanded(
child: Container(
color: Colors.blue,
width: double.infinity,
),
),
],
),
),
),
);
})
in this example if you set height for blue container you will see the scrollview works, and if you use Expanded on it, it takes the available size.

Need to scroll content with two listviews

I have a screen that shows 2 different parts.
On the top (part I) there are some widgets to order or filter listviews.
On the rest of the screen (part II) there are 2 listviews.
Here you have a screenshot
What I need is to maintain Part I as it is.
Then I want to show the first text row (Atrasadas (2)), which is the first Padding widget from part II, below it I want to show the listView included in the first Expanded widget, below the second text row (A tiempo(2)), and below the second listview included in the second expanded widget from part II.
I need to know how to set the height from both listviews, and be able to scroll the complete content from part II.
I prepared an example for you. Try this code in your app and learn how to work it.
class ForExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
children: [
Expanded(
flex: 1,
child: Container(
width: double.infinity,
color: Colors.blue,
child: Column(
children: [
Container(
child: Text("This is fixed area"),
)
],
),
)),
Expanded(
flex: 2,
child: Container(
color: Colors.red,
width: double.infinity,
child: ListView.builder(
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return new Card(
child: const ListTile(
leading: const Icon(Icons.album),
title: const Text('The Enchanted Nightingale'),
subtitle: const Text(
'Music by Julie Gable. Lyrics by Sidney Stein.',
),
),
);
},
),
))
],
),
),
);
}
}

How to expand container height to max?

I'm having an issue trying to make one of my containers stretch to max screen height,
home: Scaffold(
backgroundColor: Colors.teal,
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: double.infinity,
width: 100,
color: Colors.red,
)
],
),
),
),
I thought by making the height value infinity it would stretch the red container across the entirety of the safe area.
Although the container is invisible unless I put a value in height.
You should use an Expanded widget.
According to the official documentation, An Expanded widget is a widget that expands a child of a Row, Column, or Flex so that the child fills the available space.
Read more about the Expanded widget here: Expanded Widget
class Test extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.teal,
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
width: 100,
color: Colors.red,
),
)
],
),
),
);
}
}
OUTPUT
Wrapping the container to Expanded will make its size max like this :
home: Scaffold(
backgroundColor: Colors.teal,
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
width: 100,
color: Colors.red,
)
)
],
),
),
),

How to position a Widget at the bottom of a SingleChildScrollView?

I need to use SingleChildScrollView in order to be able to use keyboard_actions so that i can put a "Done" button on top of the keyboard in iOS (using a numeric keyboard at the moment)
The SingleChildScrollView will have a column as a child and then a button to be placed at the bottom. I tried using LayoutBuilder to enforce a height to the SingleChildScrollView.
LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints:
BoxConstraints(minHeight: viewportConstraints.maxHeight),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Column(),
// Spacer() ?
FlatButton()
])));
});
I tried using the BoxConstraints with the maxHeight attribute, but ultimately the widget wouldn't scrooll up when the keyboard appeared.
Side note: the Scaffold has both resizeToAvoidBottomInset and resizeToAvoidBottomPadding set to true (the default value)
The issue with SingleChildScrollView is that it shrikwrap it's children.
So to have auto size widget in between - we need to use MediaQuery to get the screen height & SizedBox to expand - SingleChildScrollView.
Here Button will be at bottom of screen.
working Code:
double height = MediaQuery.of(context).size.height;
SingleChildScrollView(
child: SizedBox(
height: height,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Column(
children: <Widget>[
Text('Dummy'),
Text('Dummy'),
Text('Dummy'),
],
),
Spacer(),
FlatButton(
onPressed: () {},
child: Text('Demo'),
)
])),
)
The chosen answer doesn't really work, as using a SizedBox restrict the max size of the Column to the height of the screen, so you can't put as many widgets as you want in it (or they will go off-screen without beeing scrollable).
This solution will work no matter of many widgets are in the column:
https://github.com/flutter/flutter/issues/18711#issuecomment-505791677
Important note: It will only work if the widgets in the column have a fixed height (for example TextInputField does NOT have a fixed height). If they have variable a height, wrap them with a Container of fixed height.
I just copied and pasted here the best solution I found, quoted by #Quentin, elaborated by #NikitaZhelonkin in issue 18711 of Flutter on Github. Simply the perfect solution!
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('AppBar'),
),
body: LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: constraints.maxWidth, minHeight: constraints.maxHeight),
child: IntrinsicHeight(
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Text('header'),
Expanded(
child: Container(
color: Colors.green,
child: Text('body'),
),
),
Text('footer'),
]
),
)
)
);
})
);
}
}
You may use this workaround appraoch also, You can use padding attribute in SingleChildScrollView like this:
SingleChildScrollView(
padding: EdgeInsets.only(top: height),
child: yourWidgets(),
while height is the distance far away from top.
You can also use this line to get mobile screen height:
double height = MediaQuery.of(context).size.height;
The best solution I have found for myself.
Center the widgets and push them from top to bottom with padding.
return Scaffold(
resizeToAvoidBottomInset: true,
//resizeToAvoidBottomPadding: false,
backgroundColor: PrimaryColor,
body: Center(
SingleChildScrollView(child:
_body())));
Widget _body() {
return Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).size.height/2.7),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Visibility(
visible:
_userProvider.stateReqVMTokenSocial !=
StateReqVM.Processing,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
...
...
...
],
)),
Visibility(
visible:
_userProvider.stateReqVMTokenSocial ==
StateReqVM.Processing,
child: CircularProgressIndicator()),
Text(
'',
style: Theme.of(context).textTheme.headline4,
),
],
)
);
}

Center widget vertically inside a SingleChildScrollView

I'm new to Flutter so I train myself by making a simple form. I realized while I was debugging on my iPhone the virtual keyboard triggered an error: "A RenderFlex overflowed by 29 pixels on the bottom". I fixed this issue by wrapping my Container inside a SingleChildScrollView.
The problem now is my Column's content is no longer centered. I can't figure out why ...
Here's my code to help you to understand :
List<Widget> _buildBody() {
var listWidget = List<Widget>();
SingleChildScrollView singleChild = SingleChildScrollView(
padding: EdgeInsets.only(top: 1.0),
child: Container(
alignment: Alignment.center,
margin: EdgeInsets.all(30.0),
padding: EdgeInsets.all(10.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 50.0),
child: Image.asset(
'assets/github.png',
width: 100.0,
height: 100.0,
),
),
Container(
margin: EdgeInsets.only(bottom: 10.0),
child: TextFormField(
controller: _usernameController,
autofocus: true,
decoration: InputDecoration(
hintText: 'Username',
suffixIcon: Icon(Icons.account_circle)))),
Container(
child: TextFormField(
controller: _passwordController,
obscureText: true,
decoration: InputDecoration(
hintText: 'Password', suffixIcon: Icon(Icons.vpn_key)),
),
),
Container(
margin: EdgeInsets.only(top: 10.0),
child: RaisedButton(
splashColor: Colors.greenAccent,
color: Colors.blue,
child: Text('Submit'),
onPressed: () {
_handleSubmit();
},
),
)
],
),
),
));
listWidget.add(singleChild);
if (_requesting) {
var modal = new Stack(
children: [
new Opacity(
opacity: 0.3,
child: const ModalBarrier(dismissible: false, color: Colors.grey),
),
new Center(
child: new CircularProgressIndicator(),
),
],
);
listWidget.add(modal);
}
return listWidget;
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Github Login'),
),
body: Stack(
children: _buildBody(),
));
}
I added the property "mainAxisAlignment: MainAxisAlignment.center" to my Column. It worked well before I wrapped it into the SingleChildScrollView.
If someone could help me and explain me why it doesn't work anymore I would really appreciated it :)
ArtiomLK Suggested a solution in comments which helped me:
wrap SingleChildScrollView in a Center. The widgets tree is:
Center( child: SingleChildScrollView( child: Column(...)))
None of the others helped.
Solution:
Put your top level Stack inside Center widget.
body: Center(child: Stack(
children: _buildBody(),
)));
Tip to debug:
Use Flutter Inspector to find where the layout is going wrong.
I edited your code a bit(to make to work in my local) and then I inspected. It showed like below
We have a Stack and SingleChildScrollView as per code(refer to the right side of the diagram where the stack of widgets are displayed). As size is determined by SingleChildScrollView(contents inside it), Stack occupies only a little space and by default, it aligned at top. So put it under Center, the whole Stack view will come in the center.
You can use "Center" widget like as
return Center(
child: SingleChildScrollView()
)
There's a section about it in the official docs:
Using SingleChildScrollView with a Column
Source: https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html
However I found that using a combination of the above worked better. Namely confining the Column to the minimum size possible (MainAxisSize.min). This should be fine, as the SingleChildScrollView will take up as much space as is possible on screen, before scrolling can happen.
Widget _buildPortraitPage() {
return Center(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min, // <-- notice 'min' here. Important
children: [
Flexible(
child: Image.asset('assets/images/my-image.png'),
),
Flexible(
child: Text('My content'),
),
],
),
),
);
}
The layout engine in Flutter is pretty hard to figure out.
use LayoutBuilder widget to wrap all the widget and use Spacer widget before and after your centered widget inside the Column
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('AppBar'),
),
body: LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: constraints.maxWidth, minHeight: constraints.maxHeight),
child: IntrinsicHeight(
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Spacer(),
Container(
child: //your widget
),
Spacer()
]
),
)
)
);
})
);
}
}
Just wrap the Column in a Center. I used that for my apps and it seems to center the contents of my Column even inside a SingleChildScrollView.
There's another way to achieve this:
Center(
child: ListView(
shrinkWrap: true,
children: []
)
)
wrap SingleChildScrollView with center
child: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
use SingleChildScrollView widget
body: Center(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
You can use SingleChildScrollView -> ConstrainedBox(with min height and width) -> Center
Wrap your SingleChildScrollView(), with center. and here you go.