flutter layout in singlechildscrollview - flutter

I have the below layout and want to push CustomPaints at the end of the page for any screen size, how to achieve that? I tried Spacer but got an error and tried Flexible but I don't know how to use them properly.
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
const HeaderShape(),
const SizedBox(
height: 50,
),
body,
SizedBox(
height: 150,
child: Stack(
children: [
CustomPaint(
painter: const FooterPainter1(),
child: Container(),
),
CustomPaint(
painter: const FooterPainter2(),
child: Container(),
),
],
),
),
],
),
),
);
}

You can not use spacer in a column widget, if you did not wrap your column widget with a container/sizedBox with a definite height.
Flexible widget will wrap your widget to take the available space of the parent widget, but will NOT force the child to fit the space.
Expanded will share the available space of the parent widget, and force the child widget to change its width/height to fill the available space.
So, wraping you column with a sizedbox with height and using spacer widget will help you to achieve what you are looking for. Check the code below:
SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height -kToolbarHeight - MediaQuery.of(context).padding.top -MediaQuery.of(context).padding.bottom,
child: Column(
children: [
Container(child: Text('My Header')),
const SizedBox(
height: 50,
),
Container(child: Text('My Body')),
Spacer(),
Container(child: Text('My Footer')),
],
),
),
)
If you are not using appbar in your page, just remove kToolbarHeight and MediaQuery.of(context).padding.top from your SizedBox height parameter.

Related

Stack() Widget not centering all sub widgets flutter

I have a Text() widget I am trying to stack on a CircularProgressIndicator()
I thought it would be as simple as just wrapping them both in a Stack() widget but the text is not centered.
I tried wrapping the Text() in a Center() widget but that did not solve the issue:
return Expanded(
child: PageView(
controller: pageViewController,
children: [
PackSummaryWeightGraph(
categoryWeightList: categoryWeightList,
totalWeight: totalWeight),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Stack(
children: [
Text((packedItemsPercent * 100)
.toString()),
SizedBox(
height: 150,
width: 150,
child: CircularProgressIndicator(
value: packedItemsPercent,
),
),
],
),
),
],
),
const Center(
child: Text('Third Page'),
),
],
),
);
Bonus points if there is a way to have the CircularProgressIndicator() fill the space without hard coding its width and height...
Stack has an alignment argument:
Stack(
alignment: Alignment.center,
children: [...],
),
If you want to fill the given space use the SizedBox.expand constructor:
SizedBox.expand(
child: CircularProgressIndicator(
value: packedItemsPercent,
),
),
One issue with the above is that if the available space has a different height than width you will end up with an oval shaped indicator rather than a circle. You can avoid this by wrapping it in an AspectRatio widget:
AspectRatio(
aspectRatio: 1,
child: SizedBox.expand(
child: CircularProgressIndicator(
value: packedItemsPercent,
),
),
),
See complete demo:
import 'package:flutter/material.dart';
void main() {
double packedItemsPercent = .67;
runApp(
MaterialApp(
home: Scaffold(
body: Stack(
alignment: Alignment.center,
children: [
Text((packedItemsPercent * 100).toString()),
AspectRatio(
aspectRatio: 1,
child: SizedBox.expand(
child: CircularProgressIndicator(
value: packedItemsPercent,
),
),
),
],
),
),
),
);
}
Make sure that any widget like Expanded or SizedBox.expand is not inside of an infinite sized container like a ListView or similar. Expanded means to take up the maximum available space, but that isn't possible if you have an infinite amount of space.
You'll need to set the alignment of the Stack to center:
Stack(
alignment: Alignment.center,
children: [
// ...
],
);
To get the circular progress indicator to fill the space, you should be able to use a FittedBox with a Positioned.fill widget:
Stack(
children: [
Positioned.fill(
child: FittedBox(
fit: BoxFit.fill,
child: CircularProgressIndicator(
value: packedItemsPercent
),
),
),
],
);

How to make SingleChildScrollView fill full screen height with blank space where required

In my flutter app I need a layout where I'm able to scroll if all the widgets are too long for the screen,so I added a SingleChildScrollView. But, if the widgets are smaller and leave a lot of space, I want the last row to be pinned to the bottom of the screen with blank space between the last two items. So I added a Spacer to help with that.
However that causes an error, because the SingleChildScrollView doesn't like the Spacer. I've tried everything I know, but I can't find a layout that satisfies both conditions without an error. Can someone suggest a solution please?
Code below - you may have to alter the size (or number) of the containers to demonstrate the issue on your device.
class _TestMain extends State<TestMain> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: SingleChildScrollView(
child: Column(
children: [
Container(
color: Colors.blue,
height: 100,
),
Container(
color: Colors.yellow,
height: 100,
),
Container(
color: Colors.green,
height: 100,
),
Container(
color: Colors.pink,
height: 100,
),
// comment out these four containers to demonstrate issue
Container(
color: Colors.blue,
height: 100,
),
Container(
color: Colors.yellow,
height: 100,
),
Container(
color: Colors.green,
height: 100,
),
Container(
color: Colors.pink,
height: 100,
),
// SingleChildScrollView won't allow the Spacer
//const Spacer(),
Container(
color: Colors.blue,
height: 100,
child: Text("I'm always fixed to the bottom of the screen!"),
),
],
),
),
),
),
);
}
}
The error if I add the Spacer is:
======== Exception caught by rendering library =====================================================
The following assertion was thrown during performLayout():
RenderFlex children have non-zero flex but incoming height constraints are unbounded.
When a column is in a parent that does not provide a finite height constraint, for example if it is in a vertical scrollable, it will try to shrink-wrap its children along the vertical axis. Setting a flex on a child (e.g. using Expanded) indicates that the child is to expand to fill the remaining space in the vertical direction.
These two directives are mutually exclusive. If a parent is to shrink-wrap its child, the child cannot simultaneously expand to fit its parent.
Consider setting mainAxisSize to MainAxisSize.min and using FlexFit.loose fits for the flexible children (using Flexible rather than Expanded). This will allow the flexible children to size themselves to less than the infinite remaining space they would otherwise be forced to take, and then will cause the RenderFlex to shrink-wrap the children rather than expanding to fit the maximum constraints provided by the parent.
You can make use of CustomScrollView and SliverFillRemaining slightly differently to avoid letting the screen scroll when it's partially filled. This way we can make use of Spacer and Expanded widgets too!
Scaffold(
body: SafeArea(
child: CustomScrollView(
slivers: [
SliverFillRemaining(
hasScrollBody: false,
child: Column(
children: [
Text("I'm at top part"),
Text("I'm at top part"),
Text("I'm at top part"),
const Spacer(),
Text("I'm at bottom"),
],
),
)
],
),
),
)
You can use
bottomNavigationBar from Scaffold.
return Scaffold(
bottomNavigationBar: Container(
color: Colors.blue,
height: 100,
child: Text("I'm always fixed to the bottom of the screen!"),
),
body: SafeArea(...
And
You can use CustomScrollView which is better than SingleChildScrollView; and use SliverFillRemaining will do the trick with Align widget.
class TestMain extends StatefulWidget {
TestMain({Key? key}) : super(key: key);
#override
State<TestMain> createState() => _TestMain();
}
class _TestMain extends State<TestMain> {
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: Container(
color: Colors.blue,
height: 100,
child: Text("I'm always fixed to the bottom of the screen!"),
),
body: SafeArea(
child: CustomScrollView(
slivers: [
/// your top level items
// SliverList(delegate: SliverChildBuilderDelegate((context, index) => ,)),
SliverList(
// better use `SliverChildBuilderDelegate`
delegate: SliverChildListDelegate([
// you can put regular container here
Container(
color: Colors.amber,
height: 100,
),
])),
SliverFillRemaining(
child: Align(
alignment: Alignment.bottomCenter,
child: Container(
color: Colors.blue,
height: 100,
child: Text("I'm always fixed to the bottom of the screen!"),
),
),
)
],
),
),
);
}
}

How to make the entire page in flutter scrollable

I want to make the entire page in flutter scrollable, while the height in each element inside remains dynamic.
Now the two Expanded elements have fixed height and are vertically scrollable - I want to make them extend to their regular height and be able to scroll down to the next element.
body: Scaffold(
body: Column(
children: <Widget>[
Text(),
IntrinsicHeight( //This is fixed height and doesn't move
child: Text(),
),
new Divider(height: 0.1),
Text(),
Expanded( //This is now vertically scrollable in its own box
child: Text()
),
Expanded( //This is now vertically scrollable in its own box
child: Text()
),
],
),
),
I tired several versions of wrapping the first column in SingleChildScrollView but for some reason it doesn't work.
Try below code hope its helpful to you. Wrap your Column() inside IntrinsicHeight() widget,
Refere IntrinsicHeight here
Refere SingleChildScrollView here, this widget is used for Scrolling the Widget..
return Scaffold(
body: SingleChildScrollView(
child: IntrinsicHeight(
child: Column(
children: <Widget>[
Text(
'Try',
),
IntrinsicHeight(
child: Text(
'Done',
),
),
new Divider(height: 0.1),
Text(
'data',
),
Expanded(
child: Text(
'Okk',
),
),
Expanded(
child: Text(
'Yes',
),
),
],
),
),
),
);
Wrap your column with a SingleChildScrollView(), so select Column than wrap it with a widget witch is SingleChildScrollView()
Wrap your Column widget using IntrinsicHeight widget. Then wrap IntrinstHeight with SingleChildScrollView.
This work fine for me
body: SingleChildScrollView(
child: IntrinsicHeight(
child: Column(
children: <Widget>[
Text("tes"),
IntrinsicHeight( //This is fixed height and doesn't move
child: Text("tes"),
),
new Divider(height: 0.1),
Text("tes"),
Expanded( //This is now vertically scrollable in its own box
child: Text("tes")
),
Expanded( //This is now vertically scrollable in its own box
child: Text("tes")
),
],
),
),
),
Note: Put the code inside Scaffold body

Row mainAxisAlignment not working with FittedBox

Row mainAxisAlignment is not working after wrapping with FittedBox
Please explain me why this is happening.
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
height: 20,
child: FittedBox(
fit: BoxFit.fill,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('THis is the thing'),
Text('THis is another thing'),
],
),
),
)
);
}
Expected
Output
If you really want to use Row inside FittedBox with effect of mainAxisAllignment.spaceBetween or any, you have to manually set spaceBetween using SizedBox with width as per spacing. Also set fit property to BoxFit.fitHeight .
Container(
height: 20,
child: FittedBox(
alignment: Alignment.center,
fit: BoxFit.fitHeight,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('THis is the thing',style: TextStyle(fontSize: 20),),
SizedBox(width: 300,),
Text('THis is another thing',style: TextStyle(fontSize: 20,),),
],
),
),
)
Note: Set allignment property to specify positioning. for MainAxisAllignment.start and MainAxisAllignment.start.
Eg: Alignment.topLeft to get MainAxisAllignment.start effect.
To answer your question,
Row mainAxisAlignment is not working after wrapping with FittedBox
Please explain me why this is happening.
Container always fills the entire width of the screen size. If it has child, then it will wraps up itself to match the width of its child.
Another interesting widget FittedBox will fits its children based on the size of the children within itself by scaling based on available space.
Solution
To achieve the expected result, you can force set the width of Container widget to double.infinity
Snippet
body: Container(
height: 20,
width: double.infinity,
child: FittedBox(
fit: BoxFit.fill,
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [Text('THis is the ROW'), Text('THis is another ')],
),
),
)
NOTE : If texts are small it will be enlarged to occupy the rest of the space because it is inside the FittedBox and vice-versa.

How to shrink first child in a Column?

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