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

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,
),
],
)
);
}

Related

Flutter BoxConstraints maxWidth in CustomScrollView not working

I am using a CustomScrollView and tried to limit its children's size by using BoxConstraints but they are simply ignored. My text expands to the width of the screen and I don't understand why.
This is my code:
SliverToBoxAdapter(
child: ConstrainedBox(
constraints: maxWidthConstraint,
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.project.description,
),
],
),
),
),
),
What am I missing here? Let me know if you need any more info!
The parent forces it to be the full width so you can wrap it with UnconstrainedBox or Align or Center widget to get rid of that behavior.
SliverToBoxAdapter(
child: UnconstrainedBox( // this should allow it to have its own constraints
child: ConstrainedBox(
constraints: maxWidthConstraint,
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.project.description,
),
],
),
),
),
),
)

What is the optimal way to create a scrollable column with space between items?

Something I encounter alot is creating a scrollable column with space between items.
SingleChildScrollView(child:
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: ...))
This doesn't work unless you give the Column a fixed height.
ListViews don't set space between.
What is the best way to do this? Appreciate if someone can advise. Thank you in advance!
You can try ListView.separated. It has separatorBuilder to give space between items.
More info on official doc
https://api.flutter.dev/flutter/widgets/ListView/ListView.separated.html
If you like to use spaceEvenly, just use
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [..]
But to make it scrollable,
You can include SizedBox on column to have fixed/dynamic space between item.
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("top widget"),
SizedBox(
height: 200,
),
Text("bottom widget"),
],
),
);
}
Or better
return Scaffold(
body: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return SingleChildScrollView(
child: Column(
children: [
Text("top widget"),
SizedBox(
height: constraints.maxHeight * .4,
),
Text("bottom widget"),
],
),
);
},
),
);
More about layout

SingleChildScrollView show white

i need to fix overflowed pixel, but when i use SingleChildScrollView, it just show white screen
Widget build(BuildContext context) {
SizeConfig().init(context);
return Scaffold(
appBar: buildAppBar(),
body:
Container(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Flexible(flex:5, child: Textselamatdatang(), ),
Flexible(flex:4, child:
Container(
child: Column(
children: <Widget>[
Loginformcontrainer(),
SizedBox(height: 30,),
Loginbutton()
],
),
)
) ),
],
),
),
),
);
}
This is for your Flexibles Widgets . In SingleChildScrollView you should define height for objects and you can't use Expanded or Flexible Widget.
I removed Your Flexible Widgets and your code works fine

Stretch and start FlatButton.icon

In a Column there's a group of FlatButtons created with FlatButton.icon()
so that it has image and text laid beside each other,
I need the FlatButton's contents to be aligned to the left, but the button is stretched horizontally,
setting the column's crossAxisAlignment to CrossAxisAlignment.stretch stretches the button's width as I want, but centers the button's content:
and setting it to CrossAxisAlignment.start aligns the button's contents to the left but the button's width gets shrinked:
is it possible to align the button contents to the left but the button be stretched? or I have to use other combinations of widgets (InkWell for example?)
you can use a normal FlatButton() constructor that has the child property and then you can have a row as the child and make it's mainAxisAlignment:MainAxisAlignment.start and make your text and icon it's children's, like below:
Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FlatButton(
color: Colors.grey,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Icon(Icons.check),
SizedBox(width: 10),
Text('Recharge and balance')
],
), onPressed: () {},
)
],
),
)
the result:
It is a bit of a hack, but you could use a regular FlatButton widget and do something like this could work:
class SampleWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ConstrainedBox(
constraints: BoxConstraints(
maxWidth: 200,
),
child: FlatButton(
onPressed: () {},
child: Row(
children: <Widget>[
Icon(Icons.home),
Text('Recharge and balance'),
Spacer(),
],
),
),
),
],
);
}
}
Spacer widget will take up the remaining space of row. All you have to do from here is just adjust the padding of the Icon to be the same as FlatButton.icon
Try changing padding property of Flatbutton to Edgeinsets.only(left: 0.0)
padding: Edgeinsets.only(left: 0.0),

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.