I'm developing a desktop app using Flutter, which have three widgets as above, a scrollable widget, an expended widget with a min-width, and a fixed-width widget. Notice that, the first widget is a horizontal scroll widget, so its width depends on its contents.
I know the whole width, but to set the max-width of the scrollabe widget, I need to know the min-size of the Expanded widget and the width of the fixed-width widget.
As a newbie of Flutter, I really have no idea how to layout these widgets.
Andbody help!
Few ways of providing space like providing flex on Flexiable/Expnaded widget, using LayoutBuilder/MediaQuery or just provide Expanded one of it to get available space.
You can test this widget
return Scaffold(
body: LayoutBuilder(
// in case of needed
builder: (context, constraints) {
return Row(
children: [
SizedBox(
width: constraints.maxWidth * .6,
child: ListView.builder(
itemBuilder: (context, index) => ListTile(
tileColor: index.isEven ? Colors.red : Colors.deepPurple,
),
)),
//or just use Expanded for available width
Expanded(
child: Container(
// width:constraints.maxWidth * .4-100,
// constraints: BoxConstraints(
// minWidth: 200,
// // constraints.maxWidth * .6 - 100,
// maxWidth: constraints.maxWidth * .4 - 100,
// ),
color: Colors.cyanAccent,
child: Column(
children: [
Text("an Expamed with min 2idth"),
],
),
),
),
Container(
width: 100,
height: 200,
color: Colors.red,
child: Text("Fixed-width widget"),
)
],
);
},
),
);
You can use a row and use containers with constrains to achieve this layout. For example.
Row(
children:[
Container(
constrains: BoxConstrain(
maxWidth: MediaQuery.of(context).size.width*0.5
),
child: SingleChildScrollView ()
),
Expanded(
child : Container(
constrain : BoxConstrain (
minWidth : MediaQuery.of(context).size.width*0.2
)
)
),
SizedBox(
width: MediaQuery.of(context).size.width*0.3,
)
]
)
Related
I'm new to flutter and I am working on a web app (not a phone app). I'm trying to create a layout similar to Twitter on the computer (not phone). It is even similar to stackoverflow itself. You have three columns:
Column 1: On the left with some basic menu items
Column 2: center and wider with a scrolling feed
Column 3: On the right with features/other helpful items in boxes.
With flutter, I have seen layouts that use the side menu but they are all the way on the left. They don't stick to the middle column.
I've attached an image of the general skeleton. I would love for any kind of help as to where I even begin. I've looked at SafeArea, Container etc. But since this is the base layout, I'm not sure what is the best practice for this and I want it to be a good start. Even if I can be pointed in the right direction, like start with this widget etc. I would appreciate it.
I will encourage to calculate the width and based on it change the viewport like I used to
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.deepOrange, // for screen background
body: Center(
child: LayoutBuilder(
builder: (context, constraints) {
final maxWidth = constraints.maxWidth;
return SizedBox(
width: maxWidth * .8, //max viewPort
child: Row(
children: [
Container(
width: maxWidth * .2, //left part
color: Colors.blue,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start, // for alignment
children: [
...List.generate(
8,
(index) => SizedBox(
height: 40,
child: Text("menu item $index "),
),
)
],
),
),
Expanded(
child: Container(
color: Colors.pinkAccent,
child: CustomScrollView(
slivers: [
SliverList(
// prefer builder
delegate: SliverChildListDelegate(
[
...List.generate(
44,
(index) => SizedBox(
height: 100,
child: Text("Body Element $index "),
),
)
],
),
)
],
)),
),
Container(
width: maxWidth * .2, //right part features
color: Colors.deepPurple,
child: Column(
children: [
...List.generate(
4,
(index) => SizedBox(
height: 40,
child: Text("features item $index "),
),
)
],
),
),
],
),
);
},
),
),
);
}
In my Flutter app, I have a ListView:
return Scaffold(
body: SafeArea(
child: Container(
color: Colors.blueAccent,
child: ListView(
children: [
Container(
color: Colors.yellow,
height: 100
),
const SizedBox(height: 10),
// How to make this container fill up the remaining height?
Container(
color: Colors.black,
),
],
),
),
),
);
This produces the following view:
Question: How can I make the second box (with black background color) fill up the remaining space? If the content of the box exceeds the remaining space, the ScrollView will enable scrolling.
Is this possible?
There is a better option here using SliverFillRemaining in combination with CustomScrollView instead of a ListView
in your case the code will look like this.
Container(
color: Colors.blueAccent,
child: CustomScrollView(
shrinkWrap: true, // or false
slivers: <Widget>[
SliverToBoxAdapter(
child:Container(
color: Colors.yellow,
height: 100
),),
SliverToBoxAdapter(
child: SizedBox(height: 10),),
// use SliverFillRemaining to make this container fill up the remaining height?
SliverFillRemaining(
hasScrollBody: false, // if using a column, so the widgets in it, doesn't scroll
child:Container( //this container will fill the remaining space in the ViewPort
color: Colors.black,
),
) ,
],
),
),
Fill the remaining space of a ListView means filling the infinite height. I will prefer using Stack as body in this case, hope you can simply archive this.
How can we do without stack?
In this case, we can get the height and use it on Column and using Expanded on inner child that will fill the remaining spaces even for dynamic height.
I prefer using LayoutBuilder to get height.
body: LayoutBuilder(builder: (context, constraints) {
return SafeArea(
child: Container(
color: Colors.blueAccent,
child: ListView(
children: [
SizedBox(
// 1st child of listView
height: constraints.maxHeight,
child: Column(
children: [
Container(color: Colors.yellow, height: 100),
const SizedBox(height: 10),
Expanded(
child: Container(
color: Colors.black,
),
),
],
),
)
],
),
),
);
}),
You can get size and then set a proportion for each...
And set NeverScrollableScrollPhysics() to ensure no slight scrolling, as per below this appears to get what you are seeking.
Widget build(BuildContext context) {
Size _size = MediaQuery.of(context).size;
return Container(
child: Scaffold(
body: SafeArea(
child: Container(
color: Colors.blueAccent,
child: ListView(
physics: NeverScrollableScrollPhysics(),
children: [
Container(color: Colors.yellow, height: _size.height * 0.20),
SizedBox(height: _size.height * 0.10), // How to make this container fill up the remaining height?
Container(
height: _size.height * 0.70,
color: Colors.black,
),
],
),
),
),
));
}
}
Try below code hope its helpful to you. use MediaQuery here. add MediaQuery height to your black container
Refer blog also here for relative to screen size
Container(
color: Colors.blueAccent,
child: ListView(
shrinkWrap: true,
physics: ScrollPhysics(),
children: [
Container(
color: Colors.yellow,
height: 100,
),
const SizedBox(height: 10),
// How to make this container fill up the remaining height?
Container(
color: Colors.black,
height: MediaQuery.of(context).size.height,
),
],
),
),
Your result screen->
I have a Container which has a height based on its parent which is an Expanded with a flex of 2. I need the height and the width to be the same. I don't want to create a fixed width such as width: 100.0. Is it possible to make the width match the height of this Container inside an Expanded?
For that you'll have to use LayoutBuilder, here is the minimal code:
Column(
children: <Widget>[
Expanded(
flex: 2,
child: LayoutBuilder(
builder: (_, constraints) {
return Container(
color: Colors.blue,
width: constraints.maxHeight, // width is made equal to height
);
},
),
),
Expanded(
flex: 9,
child: Container(color: Colors.orange),
),
],
)
Edit: As #pskink and other mentioned you can also use AspectRatio
AspectRatio(
aspectRatio: 1,
child: Container(color: Colors.blue),
)
Aspect Ratio will work
AspectRatio(
aspectRatio: 1,
child: Container(
color: Colors.red,
),
)
I have Column and inside it I am using 7 Expanded widgets and one of them is ClipRRect, but there is a problem: It is not circle.
I think there are 2 ways:
Wrap it with a container and give it's hight value to it's width but I don't know how?
Use another widget?
As I use CachedNetworkImage, I can't use CircleAvatar.
Expanded(),
Expanded(),
Expanded(),
Expanded(
flex: 5,
//padding: EdgeInsets.symmetric(horizontal: 10),
child: TabBarView(
controller: _tabController,
children: <Widget>[
Center(
child: Text(
'tracks',
style: TextStyle(color: Colors.white),
)),
Container(
height: 20,
width: 20,
color: Colors.red,
child: AnimatedBuilder(
animation: _animationController,
child: AspectRatio (
aspectRatio: 1,
child: ClipOval(
//borderRadius: BorderRadius.circular(10000000.0),
child: CachedNetworkImage(
fit: BoxFit.cover,
imageUrl: thisSongInfo.albumImageUrl,
),
),
),
builder: (BuildContext context, Widget child) {
return Transform.rotate(
child: child,
angle: _animationController.value * 2.0 * math.pi,
);
},
),
),
Center(
child: Text(
'information',
style: TextStyle(color: Colors.white),
),
),
],
),
),
Expanded(),
Expanded(),
Expanded(),
I edit and put all code inside that expanded
also I put debugPaintSizeEnabled = true; im main
link:
https://imge.to/i/fU8vi
Wrap your ClipRRect widget with an AspectRatio widget with an aspectRation value of 1. This forces the child widget to be square.
Or, if suitable, use a CircleAvatar widget which displays its child in a circle - this could replace both the AspectRatio and the ClipRRect widgets.
Edit: This is actually because of the TabBarView which forces its children to be full width for some reason. Wrap the child of the TabBarView within a Row so that it's allowed to resize itself horizontally.
I am using a CustomScrollView. I have added a few slivers in it but one of my children has to be a stack which has a list as on of its two children. I tried using SliverToBoxAdapter but that makes the list in the stack non scrollable which is the obvious behaviour. Is there a way to to write some kind of SliverToSliverAdapter? I tried reading the SliverPadding but its looks too complicated for me to understand. Below is my code:
CustomScrollView(
key: PageStorageKey<String>(name),
slivers: <Widget>[
SliverOverlapInjector(
handle:
NestedScrollView.sliverOverlapAbsorberHandleFor(context)),
SliverToBoxAdapter(
child: Stack(
children: <Widget>[
Container(
width: double.infinity,
height: 50,
decoration: BoxDecoration(color: pink),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: ListView.builder(
padding: const EdgeInsets.all(0),
shrinkWrap: true,
itemBuilder: (context, index) {
return buildSongRow(songs[index]);
},
itemCount: songs.length,
),
)
],
)),
],
);
Stack widget use parent's size by default. While parent widget is CustomScrollView that decide it's size mean, it will get infinite height. To solve this issue, we can wrap Stack with SizedBox(height:x).
Next issue comes on Stack about scrolling and widget visibility. Widget render prioritize bottom to top and same for the scroll event. While Container(height:50) is the first child of Stack, it will be paint behind ListView and in this case we can have full-scroll event on ListView.
We can move Container<height:50> at the bottom to paint it over ListView. In this case, scroll-event won't work on Container<h:50> portion.
We can wrap Container<h:50> and ListView with SizedBox by providing height and use Align or Positioned widget to place them on Stack. Also, we can increase itemCount+1 and use an empty SizedBox(height:50)(on index 0) that will take same amount of height as Container(height:50) and help to view only for 1st time on Stack.
I am using LayoutBuilder to get the size/constraints of screen.
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) => CustomScrollView(
slivers: [
const SliverAppBar(
title: Text("Stack On CustomScrollView"),
),
SliverToBoxAdapter(
child: SizedBox(
height: constraints.maxHeight * .3, // used by stack
child: Stack(
children: [
Align(
alignment: Alignment.topCenter,
child: Container(
// dont need to use sizedBox here, height is fixed by container's height:50
height: 50,
color: Colors.pink,
alignment: Alignment.center,
child: const Text("Stack Child: Container<height: 50>"),
),
),
Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
height: constraints.maxHeight * .3 - 50,
child: ListView.builder(
itemCount: 4,
itemBuilder: (context, index) => Container(
alignment: Alignment.center,
height: 50,
color: index.isEven
? Colors.deepOrange
: Colors.deepPurple,
child: Text("ListViewItem:$index"),
),
),
),
),
],
),
),
),
SliverList(
delegate: SliverChildListDelegate(
[
...List.generate(
44,
(index) => Container(
height: 100,
alignment: Alignment.center,
color: index.isEven ? Colors.cyanAccent : Colors.blueGrey,
child: Text("SliverList $index"),
),
),
],
),
),
],
),
),
);