how to widget height fill to parent (or get parent size) in Flutter? - flutter

I am new to flutter, and I having trouble with my ListView. My ListView contains many items, the item of ListView includes question content and some comments of users.
The item height is automatically decided by its children height, also the first children of the item (deepOrange Container below) height is dependent to parent item height (is't the item of ListView), its height must be fill to parent item.
The items size of ListView dependent to its children items size,
also the first widget (Container below) in the item is dependent to parent item too.
How i can do this?
I'm very very sorry, English is not my primate language, my English is very poor. Here is my code:
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class MainPage extends StatefulWidget {
const MainPage({Key key, this.title}) : super(key: key);
final String title;
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
var items = List.generate(10, (index) => index);
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: items.map((item) => Row(
/*I can't compute the real rendered height of the item by it's children content, because it's possible contain many replay message, different font size etc.*/
children: <Widget>[
Container(
width: 5.0,
/**
* I want to fill the height to parent widget of the container,
* (I want to make an timeline widget, it's should max height to its parent.)
* but i can't get parent items size, or double.infinity etc,
*/
height: 80.0, // How to fill the height to parent of the property? or how to get parent item size (or actually rendered height)?
color: Colors.deepOrange,
),
SizedBox(width: 10.0,),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text("there is long comment of users, there is long comment of users, there is long comment of users, there is long comment of users, there is long comment of users, $item"),
/*
Also here is a lists of replay message by another user.
* */
Column(children: <Widget>[
Text("replay 1"),
Text("replay 2"),
Text("replay 3"),
Text("replay N"),
],),
Row(
children: <Widget>[
IconButton(icon: Icon(Icons.favorite)),
IconButton(icon: Icon(Icons.delete)),
IconButton(icon: Icon(Icons.message)),
],
)
],
),
),
],
),).toList(),
),
);
}
}
I simply want to fit that purple Container's height to the parent container.
Thank you in advance.

Wrap your list in a container. Then set the height to the screen size. You can get that through the MediaQuery.
var screenSize = MediaQuery.of(context).size;
Container(
height: screenSize.height,
width: screenSize.width,
child: ListView(...));

Related

How to expand fixed aspect ratio widget in row as much as possible while leaving minimum amount of space for second widget?

I have two widgets I need to place side by side (will shift to vertical depending on screen size). The first widget has a fixed aspect ratio - this widget should expand as much as possible to fill the space - up until some minimum size constraint for the second widget (pixels preferred, but percentage is workable)
One, will have a fixed aspect ratio of 1 / 1.2 - for the sake of example, we'll keep it simple:
AspectRatio(
aspectRatio: 1 / 1.2,
child: ColoredBox(color: Colors.red),
)
The second widget will be a list of rows of small text items - example implementation with blue for visibility:
ColoredBox(
color: Colors.blue,
child: ListView(
children: [
Row(
children: const [
Expanded(child: Center(child: Text("small"))),
Expanded(child: Center(child: Text("text"))),
]
),
Row(
children: const [
Expanded(child: Center(child: Text("will"))),
Expanded(child: Center(child: Text("be"))),
],
),
Row(
children: const [
Expanded(child: Center(child: Text("here"))),
Expanded(child: Center(child: Text(""))), // avoid unnecessary widget tree structure changes when item is added here?
]
),
]
)
)
So, the first thing I did was just throw them in a Flex widget, wrapped in Flexible:
Flex(
direction: getDirection(),
children: [
Flexible(child: MyFixedAspectRatioWidget()),
Flexible(child: MyStringGridWidget()),
]
)
This works ok for some screen aspect ratios, but:
As the screen width is increased, I get a white bar along the right hand side.
As the width is decreased, I get white bars along the top and bottom
of the fixed aspect ratio widget.
The widgets will always have an equal width.
What I would like to happen:
As the width is increased, the string grid widget expands to fill the remaining space.
As the width is decreased, the string grid decreases in width, while the fixed aspect
ratio widget stays the same size, up until that would mean the string grid
widget would have less than some minimum size, and only then begin
to decrease the size of the aspect ratio widget (white bars on top and bottom).
What I've tried to get this behavior:
Setting a much larger flex for the fixed aspect ratio widget, while using Expanded for the string grid widget.
I was hoping that the large flex would allow it to take up as much size as it wants, while the Expanded, would cause the string grid to fill any remaining space.
At first this seemed promising - the AspectRatio grid had the behavior I wanted, filling all available space while keeping its aspect ratio, and also leaving a minimum amount of space for the string grid! The string grid however, would never expand beyond 1/10th of the width of the fixed aspect ratio widget.
Flex(
direction: getDirection(),
children: [
Flexible(flex: 10, child: MyFixedAspectRatioWidget()),
Expanded(flex: 1, child: MyStringGridWidget()),
]
);
I've tried various other combinations of flex and Expanded, but no luck improving on the initial take. Placing the ListView within Expanded just throws an exception.
Using ConstrainedBox with a very large maxWidth for the aspect ratio widget, and Expanded for the string grid widget seems to work properly when increasing the width; however, it leads to an overflow when decreasing the width, and does not save space for the grid.
I'm sure I could get this working with LayoutBuilder and SizedBox, but I was wondering if there's any built in way to get this kind of behavior.
Full example
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Example App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage()
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}): super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return const Scaffold(
body: BothWidgets()
);
}
}
Axis getDirection() => Axis.horizontal;
class BothWidgets extends StatelessWidget {
const BothWidgets({super.key});
#override
Widget build(BuildContext context) {
return Flex(
direction: getDirection(),
children: [
const Flexible(
flex: 10,
child: AspectRatio(
aspectRatio: 1 / 1.2,
child: ColoredBox(color: Colors.red),
),
),
Expanded(
flex: 1,
child: ColoredBox(
color: Colors.blue,
child: ListView(
children: [
Row(
children: const [
Expanded(child: Center(child: Text("small"))),
Expanded(child: Center(child: Text("text"))),
]
),
Row(
children: const [
Expanded(child: Center(child: Text("will"))),
Expanded(child: Center(child: Text("be"))),
],
),
Row(
children: const [
Expanded(child: Center(child: Text("here"))),
Expanded(child: Center(child: Text(""))), // avoid unnecessary widget tree structure changes when item is added here?
]
),
]
),
)
),
]
);
}
}

How to fix a widget to the right side of a ListView on Flutter Web

I have a Flutter Web application where I need to show a widget on the right side of a ListView when I click an item and this widget should always be visible on screen. I can achieve my objective puting both on a Row and using a scrollable only for the ListView, but that requires the ListView to be wrapped by a widget with defined height.
Defining a container with height to wrap the ListView breaks the responsiveness when I resize the browser, as the container doesn't fit the height of the screen.
I thought of using the shrinkWrap property of the ListView so I don't have to wrap it in a widget with predefined height, but that makes the whole Row scrollable vertically, eventually causing the widget to leave the viewport.
I would appreciate if somebody knows how could I keep this right side widget fixed on screen so I can achieve my objective without losing responsiveness.
Here's something similitar to what I've got so far:
class PageLayout extends StatefulWidget {
const PageLayout({Key? key, required this.items}) : super(key: key);
final List<String> items;
#override
State<PageLayout> createState() => _PageLayoutState();
}
class _PageLayoutState extends State<PageLayout> {
final rightSideWidget = Container(
decoration: BoxDecoration(
color: Colors.red,
border: Border.all(color: Colors.white, width: 2),
),
height: 200);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.49,
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (context, index) => Container(
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(color: Colors.white, width: 2),
),
height: 200,
child: Center(
child: Text(
widget.items[index],
style: const TextStyle(color: Colors.white),
),
),
),
itemCount: widget.items.length,
),
),
Expanded(child: rightSideWidget),
],
),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
I want rightSideWidget to be always centered on screen or follow the scroll.
You can divide your screen into two sections, right section and left section; thereby being able to control behaviour of widgets in both sections.
Divide the overall screen into 2 proportional sections using a Row
widget
Put this Row widget inside a Container with height equal to screen height for preserving responsiveness | Use MediaQuery to get current height of page
Now left hand section can individually scroll, and on click of any option from this section you can define behaviour for right section; while keeping the left section constant throughout page lifecycle

flutter image placeholder (FadeInImage) without setting fixed size?

How can I use something like FadeInImage to setup and hold the layout of a page before images have downloaded? As expected, just using Image.network causes the page to jump around once the images load and become visible. I don't have set image sizes (i allow them to resize based on screen/etc) and want to avoid setting a fixed height. The images load and show fine using FadeInImage however the screen still jumps a lot.
#override
Widget build(BuildContext context) {
return
Scaffold(
appBar: AppBar(
title: Text('Welcome!'),
),
drawer: sideDrawer(),
body: new SingleChildScrollView(
padding: const EdgeInsets.all(8.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
SizedBox(height: 28),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(width: 64),
Flexible( // tried Expanded too
child:
FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image: 'https://www.xyzserver.com/images/dummyimage.png',
fit: BoxFit.scaleDown,
),
),
SizedBox(width: 64),
],
),
SizedBox(height: 28),
Text("stuff below the image"),
],
),
)
);
}
When using "Expanded" the image row/area is very tall vertically (the text "stuff below the image" is at the bottom of the page so the page jumps up when the image loads. When using "Flexible" the image row/area is somewhat smaller and the page jumps down when the image loads.
In the image I'm playing around with now, it's a horizontal image that is larger than the available screen space, so it will get scaled down. I guess I was thinking that since flutter can calculate the max width of what's available to the expanded/flexible, it should be able to calculate the height, but as I write this I'm thinking that's impossible since it doesn't know the height/width ratio so it can't predict the height.
How can I set this up so that images can be resized and show correctly and the page doesn't jump around? I can't imagine using fixed height/width settings is the way to go. Maybe my approach to images is all wrong and I should always use a set height/width although that can be rather difficult when people are allowed to upload their own images/etc.
Thanks!
Check this one
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Uint8List? imageData;
Future<Uint8List> dosometinhdd() async {
return (await rootBundle.load('assets/images/a.png')).buffer.asUint8List();
}
#override
void initState() {
dosometinhdd().then((value) {
setState(() {
imageData = value;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Welcome!'),
),
body: new SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height * 0.8,
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
imageData != null
? Expanded(
child: FadeInImage.memoryNetwork(
placeholder: imageData!,
image:
'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Image_created_with_a_mobile_phone.png/1200px-Image_created_with_a_mobile_phone.png',
fit: BoxFit.scaleDown,
),
)
: Container(),
Text("stuff below the image"),
],
),
),
));
}

Dynamic size of container - Flutter

I'm new to flutter and came across an issue with Container widget's size that has Row and Column as it's child. I'm aiming to render a Column and Row widget with dynamic data. The code is as below.
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(35),
color: Colors.orange.shade400,
),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 15.0, horizontal: 25.0),
child: Column(
children: <Widget>[
Text("PEAK/THURSDAY"),
Text("\$24,355"),
weeklyExpenseBuilder(), // Row with Text widgets
],
),
),
)
Not fixing the size of the container allows it to take up all the screen space but fixing it's height leads to RenderFlex overflow error. Is there any way to let the Container widget take only as much space as it's children take.
The design that i'm aiming to looks something like this
The mainAxisSize property of a Row or Column is max by default.
Try setting mainAxisSize: MainAxisSize.min on both the row and column and adding some spacing.
You should always make give size related to width and height of the screen to make the app perfectly responsive
for example you can create a variable
double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;
and use it for you Container or fontsize for example
import 'package:flutter/material.dart';
class TestWidget extends StatefulWidget {
const TestWidget({Key? key}) : super(key: key);
#override
_TestWidgetState createState() => _TestWidgetState();
}
class _TestWidgetState extends State<TestWidget> {
#override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;
return Container(
height: height,
width: width / 2,
child: Text('test',
style: TextStyle(
fontSize: width * 0.035,
)),
);
}
}
And for avoiding overflow you can always user Wrap or Expanded(inside a row or column).

How to align widget to another widget in Flutter?

I have a RaisedButton widget inside of a Center widget as one of the widgets in a Column of widgets. I want to add a CircularProgressIndicator to the right side of this button and show it when the button is pressed. Yet I want to leave the button centred when the progress bar is shown. In other words I want the button always be in the center and the progress bar aligned to this button.
I tried to use a Row here but this pushes the button and it becomes not centred any more.
EDIT1: Looking at the result of the solution provided by #Anil Chauhan (thanks for the answer):
Like I said before that I tried to use Row like he did, the problem is that in this case the button is not in the centred in the screen and is pushed by the progress bar. And I need it to stay in the middle of it's row.
EDIT2: #Anil Chauhan edited answer now works for a specific case in which the button is predetermined size. But if the size of the button is changed based on the language of the text (in apps that have several languages) this solution will not work.
This is the reason the question I asked is: "How to align widget to another widget". Because if I could that I don't have to worry about the button text size any more.
What would be the right way to handle this in Flutter?
class MyPage extends StatefulWidget {
#override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
bool _showIndicator = false;
void _onButtonClicked() {
setState(() {
_showIndicator = !_showIndicator;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Row(
children: <Widget>[
const Expanded(child: SizedBox()),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: RaisedButton(
child: Text("I am Too Big"),
onPressed: _onButtonClicked,
),
),
Expanded(
child: _showIndicator
? const Align(
alignment: Alignment.centerLeft,
child: CircularProgressIndicator(),
)
: const SizedBox(),
),
],
),
),
);
}
}
Here is my explanation:
The RaisedButton size is depends on its child. If you add it to Row it will automatically align to left(or start).
Expanded widget will fill the remaining space in Flex widget(Row & Column are child classes of Flex). If you add more than one Expanded widgets, it will split equally. So I added two Expanded to both the side of button to make it center.
Now We should give child for Expanded Widget.
For the first one(left) we don't have anything to display so I added SizedBox.
For the second one(right) we need CircularProgressIndicator. so I added it.
The Expanded widget will try to make its child to fill the space inside of it. So the CircularProgressIndicator will become Ellipse shaped. We can avoid this by using Align Widget.
Try this:
Updated:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyAppOne(),
);
}
}
class MyAppOne extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyAppOne>{
bool show = false;
#override
Widget build(BuildContext context){
return Scaffold(
body: Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
alignment: Alignment.center,
child: RaisedButton(
onPressed: () {
setState(() {
show =!show;
});
},
child: Text('Show'),
),
),
Positioned(
right: MediaQuery.of(context).size.width * .20,
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(10.0),
child: show ? CircularProgressIndicator() : Container(),
),
)
],
)
);
}
}
Flutter's Column and Row widgets have two convenient properties called mainAxisAlignment and crossAxisAlignment. I assume since you're using a Column and want the CircularProgressIndicator to the right of the button, you might be want to use crossAxisAlignment since the cross-axis of a Column is along the horizontal.
If possible, please share your code for better understanding and support of the issue.