How to resize column in row for responsive screen? - flutter

I am build app landing page in Flutter for web. I need make page responsive for different screen size.
I am use MediaQuery for change layout for small screen so widget are layout vertical.
But for medium size screen layout is horizontal. So I need resize app screenshot when screen is make smaller. How to do?
For example: reflectly.app
Here my code so far:
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CenterText(),
SizedBox(height: 10),
Row(
children: <Widget>[
DownloadButtons() ],
),
],
),
Column(
children: <Widget>[
Screenshot(),
],
),
],
)

You need to know the LayoutBuilder component, which is what you want and fixes your needs.
LayoutBuilder takes two parameters, one is a context, the second is a constraints. With those constraints you can set show different layouts for different screens.
Check this example:
LayoutBuilder(
builder: (context, constraints) {
if(constraints.maxHeight < 768 && constraints.maxWidth < 1270) {
return smallDesignContent();
} else {
return bigDefaultDesignContent();
}
},
)
Flutter Widget of the Week - Layout Builder
Documentation

Related

Flutter SingleChildScrollViewheight to screen height

I have a Column and three elements in it. I set the mainAxisAlignment to MainAxisAlignment.spaceBetween, so that the first Element is on top of my app. The second in the middle and the third at the bottom.
Now I want to wrap that in a scroll view so that if the content of the Column is taller than the view it can be scrolled.
For example that happens when the keyboard pops up.
My code looks kinda like that:
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Element1(),
Element2(),
Element3(),
]
)
The Column expands its height over the entire screen. And that is what I also want.
But now if I wrap it in a SingelChildScrollView it shrinks to the min content height. :(
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Element1(),
Element2(),
Element3(),
]
),
)
Is there a way that the Column continues to expand over the entire view height of the screen and is scrollable as soon it's content is taller that the screen height?
You should be able to do this by using ConstrainedBox and LayoutBuilder which will force the Column to take the max available height
LayoutBuilder(
builder: (context, constraints) => SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Element1(),
Element2(),
Element3(),
]),
),
),
)

Flutter: how to center a child, but set a limit on how big it can grow on one side of the margin

I have a home/login screen which is made up of a column that fills the entire screen like so:
Column(
children: <Widget>[
Expanded(
child: Container(
child: Logo(),
),
),
showThis ? This() : That(),
],
),
The second child of the column is dynamic and can have different heights, and this screen will have inputs so the keyboard will also affect the height.
I want to center Logo() vertically within the container when it's small (e.g. when keyboard is active), but limit how much the 'top margin' is able to grow, so that when the keyboard is hidden and This()/That() is small enough, Logo() will be in a static position on the screen, say 150 from the top (no longer centred vertically).
One method I have tried was using 2 empty Expanded() above and below Logo() and wrapping the top part in a ConstraintedBox(), but I am not able to get it to behave correctly.
Have you tried with Center() and also if you wanna in a specific position use Stack, for example
Stack(
children: <Widget>[
showThis(),
Positioned(top: 150, child: Logo())
],
)
This is what ended up working for me:
Column(
children: <Widget>[
Expanded(
child: Column(
children: <Widget>[
// constrain top spacing with a max height
Flexible(child: Container(height: 160)),
_logo(),
Spacer(),
],
),
),
_bottomWidget(),
],
),

How to make a Widget to be as small as it can (wrap_content in Android)?

I have a rendering issue with render tree similar to Column>PageView>Column, where the last Column is inside a page of the PageView.
The PageView isn't being rendered correctly, so I get an exception (Horizontal viewport was given unbounded height.) as the framework can't calculate its sizes, I can fix it by wrapping it into a Flexible or an Expanded, but I don't want the PageView to take the whole screen, I want it to be as small as possible and on the center on the screen.
Here's a representation of my problem:
// This code throws an exception:
class Widget_1_Has_Problem extends StatelessWidget {
final PageController pageController = PageController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.done,size: 112),
SizedBox(height: 32),
PageView(
physics: NeverScrollableScrollPhysics(),
controller: pageController,
children: <Widget>[
// Many widgets go here, I am just simplifying with a Column.
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
SizedBox(height: 16),
Text('TEXT2 TEXT2 TEXT2 TEXT2 TEXT2 TEXT2'),
],
),
],
),
],
),
);
}
}
This is what I wanted to achieve (I removed the PageView in order to show it):
class Widget_2_No_PageView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.done,size: 112),
SizedBox(height: 32),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
SizedBox(height: 16),
Text('TEXT2 TEXT2 TEXT2 TEXT2 TEXT2 TEXT2'),
],
),
],
),
);
}
}
And this is how I can fix it, but it's not perfect, I will show later:
class Widget_3_With_Flex_Not_Perfect_Solution extends StatelessWidget {
final PageController pageController = PageController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Spacer(flex: 2,),
Icon(Icons.done,size: 112),
SizedBox(height: 32),
Flexible(
flex: 1,
child: PageView(
physics: NeverScrollableScrollPhysics(),
controller: pageController,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
SizedBox(height: 16),
Text('TEXT2 TEXT2 TEXT2 TEXT2 TEXT2 TEXT2'),
],
),
],
),
),
Spacer(flex: 2,),
],
),
);
}
}
This solution isn't perfect because Spacer and Flexible or Expanded always try to maintain it's proportionality of the flex weight, which means that for smaller displays the Spacer won't vanish, it will remain there, whereas the first and desired image won't have such a void space. As we can see in the below image, the Spacer is always there. Also I have to calculate the flex factor for every change in this design, whereas in my first code example the widget will size itself to the center of the screen automatically.
How can I instruct PageView then to be as small as it can be, instead of expand as much as it wants, as that's the only solution I can find online?
The PageView uses SliverFillViewport to wrap around given children.
This SliverFillViewport doesn't care about how small the children are. It only cares how much space it can occupy from its parent. So the Flex and Align widget are useless.
Currently with PageView, it most likely not possible unless we do some calculation and use SizedBox or ConstrainedBox.
I also found this discussion
This can be done, but not with PageView. I had to use SingleChildScrollView with a Row. The reason for that is: if you want to size your PageView properly, you need to build and layout every child it has. PageView does not do that, but Row does, you can even decide how smaller elements should be positioned. PageController also works if you properly set widths of children in the row.
Note that this will only work nicely if you have small amount of pages.
class SizingPageView extends StatelessWidget {
final ScrollPhysics physics;
final PageController controller;
final List<Widget> children;
const SizingPageView({Key key, this.physics, this.controller, this.children})
: super(key: key);
#override
Widget build(BuildContext context) {
Widget transform(Widget input) {
return SizedBox(
width: MediaQuery.of(context).size.width,
child: input,
);
}
return SingleChildScrollView(
physics: physics ?? PageScrollPhysics(),
controller: controller,
scrollDirection: Axis.horizontal,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: children.map(transform).toList(),
),
);
}
}
class Solution extends StatelessWidget {
final PageController pageController = PageController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Spacer(
flex: 2,
),
Icon(Icons.done, size: 112),
SizedBox(height: 32),
Text('BEFORE SCROLL'),
SizingPageView(
physics: NeverScrollableScrollPhysics(),
controller: pageController,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
SizedBox(height: 16),
Text('TEXT2 TEXT2 TEXT2 TEXT2 TEXT2 TEXT2'),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
],
),
Text('Third'),
],
),
Text('AFTER SCROLL'),
Spacer(
flex: 2,
),
FlatButton(
child: Text('Next'),
onPressed: () {
var next = pageController.page.round() + 1;
if (next >= 3) {
next = 0;
}
pageController.animateToPage(
next,
duration: Duration(seconds: 1),
curve: Curves.easeInOut,
);
},
)
],
),
);
}
}
You are asking for items inside of the PageView to set the size of PageView itself. This is not going to work with the standard class. If you look at its source, you'll find that uses Viewport inside Scrollable. Viewport grabs maximum size and ignores the size of the children (Slivers). You'll need to use a different approach or maybe create a custom PageView, perhaps one that uses ShrinkWrappingViewport.
https://api.flutter.dev/flutter/widgets/ShrinkWrappingViewport-class.html
By wrapping PageView inside SizedBox, we can get rid of the exception you are getting. (Horizontal viewport was given unbounded height.).
SizedBox(height: 32, child:
PageView(
physics: NeverScrollableScrollPhysics(),
controller: pageController,
children: <Widget>[
// Many widgets go here, I am just simplifying with a Column.
Column(
...
But that will give you a bottom overflow exception of 16 pixels since you have another SizedBox with fixed height inside Column in PageView.
In order to resolve this issue, let's just for time being replace top SizedBox with Container and add color property to it to see how much space and height is available to us.
So, instead of hardcoding the sizedbox height to 32, we can make use of MediaQuery to calculate height for us.
If you print height of the top SizedBox, it will come out to be 597
SizedBox(height: MediaQuery.of(context).size.height,
I for demo, divided the height with 5 to get 119.4 height of the SizedBox widget that contains PageView, which resolves the renderflow exception as well as maintains pageview's location at center of the screen.
If we again use Container to see now how much space and height is available to us, it will look like:
So with this approach, you will not be required to use Expanded or Flexible widgets as you mentioned and won't need to change much of your code.
The working code is as below:
h = MediaQuery.of(context).size.height / 5;
print(h); // 119.4
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.done,size: 112),
SizedBox(height: h, child:
PageView(
physics: NeverScrollableScrollPhysics(),
controller: pageController,
children: <Widget>[
// Many widgets go here, I am just simplifying with a Column.
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('TEXT TEXT TEXT TEXT TEXT TEXT'),
SizedBox(height: 16),
Text('TEXT2 TEXT2 TEXT2 TEXT2 TEXT2 TEXT2'),
],
),
],
),
),
],
),
);
You might need to adjust height per your need, but I hope this resolves your issue and answers your question.
You can simply wrap your widget with Flexible widget https://api.flutter.dev/flutter/widgets/Flexible-class.html

Flutter: How to add a column widget inside a bottom navigation bar

I've tried the following and got the out, but the body went blank.
Is there any way to add a coloumn or a listview inside a bottom navigation bar
Column(
mainAxisAlignment:MainAxisAlignment.end,
children: <Widget>[
Row(
children: <Widget>[
Expanded(
//button1
),
Expanded(
//button2
),
Expanded(
//button3)
),
Row(
children: <Widget>[
Expanded(
//button4
),
Expanded(
//button5)
],
)
],
),
Apart from the issue posed by Expanded widget (as pointed out in the answer by alexandreohara),
Column's mainAxisSize attribute is set to MainAxisSize.max by default. That means the column will try to occupy all the space available (the entire screen height).
Setting Column's mainAxisSize to MainAxisSize.min will wrap the Column widget to occupy the least possible space(vertically) and not span the entire screen.
Following is the way it can be done:
Column(
mainAxisSize: MainAxisSize.min,
children: [...]
);
There's no problem using a Column in a BottomNavigationBar. I think that your problem is wrapping everything in Expanded. It doesn't know the width of the widgets inside, and because of that, it will return blank.
Try to remove those Expanded widgets and try this:
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween
children: [FlatButton(...), FlatButton(...)],
)

Flutter equivalent of layout_gravity="bottom"

I am trying to render a dribbble sample.
In normal android using XML, I can make any part stick to any side of another view. If I were using ConstraintLayout, I could make the bottom part stick to the bottom of the parent and let the top part expand to available height.
The design on both emulators is Flutter code.
On larger screens, there is some empty space on the bottom. How do I remove that?
Currently, I am using Flexible with flex values but it doesn't look right. How to make this reactive?. Is the use of Flexible correct?
The code can found here on pastebin
There is nothing like android:layout_gravity in Flutter, however you can pretty much achieve anything in Flutter. So, here you can use use Spacer(). Like:
return Column(
children: <Widget>[
Spacer(),
Align(alignment: Alignment.centerRight, child: Text("123")),
YourButtonsWidget(),
],
);
try to change CalculatorPrototype's build function like this
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min, // actually this line and the next one doesn't needed
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: display,
),
Container(
child: keypad,
),
],
),
);
}