Flutter | How to fill the remaining space of a ListView? - flutter

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->

Related

Efficient way to make container take remaining width in a Row widget in Flutter

I am new to Flutter and was practicing its UI where I came across a situation where I had a list where each list element have an image on the left and some text on right.
Below is my approach to that
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 20),
children: [
const SizedBox(height: 5),
Row(
children: [
Container(
height: 80,
width: 80,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
image: const DecorationImage(
image: NetworkImage('http://images.unsplash.com/photo-1555010133-d883506aedee?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max'),
fit: BoxFit.cover
)
),
),
const SizedBox(width: 10),
Container(
color: Colors.green,
height: 80,
width: 280,
)
],
),
],
),
Here I am specifying width individually for both containers which is not an efficient way to do this since phone sizes may vary.
Below is the result for above block of code
Screenshot of the app screen
I tried specifying crossAxisAlignment: CrossAxisAlignment.stretch to the Row() but it throws an error as below
The following assertion was thrown during performLayout():
BoxConstraints forces an infinite height.
How can I achieve this? Please assist
Thank you
Wrap the widget with an Expanded inside the row:
Row(
children: [
...otherChildren,
Expanded(
child: Container(
color: Colors.green,
height: 80,
),
),
],
),
Use ListView.builder with ListTile widgets. It has a leading widget (normally an icon or an avatar) to the left, text in the middle and trailing widget (normally an icon), each of which is optional.
ListView.builder(
itemCount: ...,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: const CircleAvatar(
radius: 20.0,
foregroundImage: NetworkImage(...),
),
title: Text(...),
subtitle: Text(...),
trailing: ...,
onTap: () => ...,
);
},
)

Is there a way to get fixed size for child inside GridView?

Is there a way to have a fixed size for the child inside GridView? I wanted to have the red color be same height. I tried using Expanded or specified the size for the Container, but it just won't work. GridView just makes the child same size.
Note: I tried specify the height for the red Container. It didn't work.
This is what I got.
This is what I did.
return GridView.count(
crossAxisCount: 2,
children: List.generate(drinksData.length, (index) {
return Container(
padding: EdgeInsets.all(20.0),
child: Column(
children: [
Expanded(
child: Container(
color: Colors.red,
),
),
Text(drinksData[index].label),
],
),
);
}),
);
GridView.count(
crossAxisCount: 2,
children: List.generate(drinksData.length, (index) {
return Container(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Expanded(
child: Container(
// height:90,//or whatever size you want!
color: Colors.red,
),
),
SizedBox(
height: 45,
child: Text(drinksData[index])
),
],
),
);
}),
)
```

how to only scroll the children inside scrollview that has another scrollview

I have ListView and the children that has pdf document wrap inside singleChildScrollView want to only scroll the singleChildScrollView and not the listView
after expansion
Below is my code
Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(right: 15, top: 10),
width: size.width,
child: Text("Title should be Pinned",
textAlign: TextAlign.end,
),
),
SizedBox(
height: 20,
),
Theme(
data: ThemeData(
accentColor: Colors.black,
primaryColor: Colors.white24,
),
child: Expanded(
child: ListView.builder(
itemCount: data.length,
itemBuilder: (context, i) => ExpansionTile(
title: Center(
child: Text(
"After Expansion this should be pinned and only children should be scrollable"
),
),
children: [
Container(
child: SingleChildScrollView( //should be scrollable on expanded and not the title of ExpansionTile
child: SfPdfViewer.network(
'$url',
),
),
),
],
),
),
),
)
],
),
warp your widget in a container and give it a height
use the following parameters inside the scroll widget
shrinkwrap: false,
primary: true
physics: NeverScrollableScrollPhysics():
Add This Line to Your Widget then it will work

Flutter web - setting height to 100% and not 100vh

In HTML creating a page that is divided into 2 columns that span 100% of the screen height is easy
<div id="row">
<div id="column1" style="width:75%; height:100%"></div>
<div id="column2" style="width:25%; height:100%"></div>
</div>
Using Flutter this seems more complicated than it should be, i tried to achieve the above in a couple of ways: (The page's outline is CustomScrollView -> SliverToBoxAdapter -> Row -> Columns)
Using MediaQuery to set the columns' height as deviceSize.height. This failed because as the left column got bigger than the right column, the right column did not expand in size to match.
Using Expanded or Flexible (tight or loose) to try and expand the height. This did not work.
My question is how do i achieve 100% height and not 100vh height in Flutter web?
Edit:
This illustrates the problem, the right column is not expanding in size to match the left column.
CustomScrollView(
slivers: <Widget>[
SliverFillRemaining(
child: Container(
color: Colors.purple,
child: SingleChildScrollView(
child: Row(
children: <Widget>[
Container(
color: Colors.blue,
child: Column(
children: <Widget>[
Text("test"),
SizedBox(height: 300),
Text("test"),
SizedBox(height: 300),
Text("test"),
SizedBox(height: 300),
Text("test"),
SizedBox(height: 300),
],
),
),
Container(
color: Colors.yellow,
child: Column(
children: <Widget>[Text("test")],
),
),
],
),
),
),
),
],
);
Not sure if this is what you want. But based on your statement I think you are looking for two column layout as in here and as shown below in the image.
Following is the code for the same. You can adjust the flex value in the Expanded widget to acquire the required screen width ratio. but if you want to adjust the width each columns like using a drag handle or so, then this alone wont suffice.
import 'package:flutter/material.dart';
class FullHeightDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: <Widget>[
Expanded(
flex: 4,
child: Container(
color: Colors.red,
child: Center(
child: Text(
'Column-1',
style:
TextStyle(fontWeight: FontWeight.bold, color: Colors.black),
),
),
),
),
Expanded(
flex: 1,
child: Container(
child: Container(
color: Colors.teal,
child: Center(
child: Text(
'Column-2',
style: TextStyle(
fontWeight: FontWeight.bold, color: Colors.black),
),
),
),
),
)
],
));
}
}
Specifying the height using MediaQuery inside a Scrollable widget could help
Following code places the columns inside a SingleChildScrollableView widget and specifies the height of the container based on MediaQuery to achieve the same. Will this help.? The LayoutBuilder is only there to show that the using the height from its constraints could also lead to error since in this case its height is also infinite.
import 'package:flutter/material.dart';
class FullHeightDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: LayoutBuilder(
builder: (context, constraints) {
var size = MediaQuery.of(context).size;
return Container(
child: Row(
children: <Widget>[
Container(
height: size.height,
// height: constraints.maxHeight,
width: constraints.maxWidth / 2,
color: Colors.red,
child: Center(
child: Text(
'Column 1',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
),
Container(
color: Colors.green,
height: size.height,
// height: constraints.maxHeight,
width: constraints.maxWidth / 2,
child: Center(
child: Text(
'Column 2',
style: TextStyle(
fontWeight: FontWeight.bold, color: Colors.black),
),
),
)
],
),
);
},
),
);
}
}

How to put a Stack as a child of CustomScrollView

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