How to make a SingleChildScrollView with nested ListView? - flutter

I have a list of days, and a list of opening hours for each day.
I need to make the list of days scrollable, and the list of opening hours expands as necessary (= I don't want the list of opening hours to be scrollable into a defined height container).
How to make the list scrollable ?
Here is what I have so far, a list of days that is not scrollable (and I can't see sunday) :
SingleChildScrollView(
child: ListView.builder(
itemCount: _days.length,
itemBuilder: (context, i) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(18.0),
child: Text(_days[i],
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.grey[700]))),
Container(
decoration: BoxDecoration(color: Colors.white),
child: ListView.builder(
shrinkWrap: true,
itemCount: _daysDispos[i].length,
itemBuilder: (context, j) {
final dispo = _daysDispos[i][j];
return ListTile(
title: Text(dispo.address.line));
}),
)
],
);
})),
EDIT
In the first listView builder, add :
physics: NeverScrollableScrollPhysics()
Solved my issue.

A good solution would be using Expandable package, and add multiple Expandables inside a single ListView.
Another Solution would be replacing the outer ScrollView with one single ListView, whose Childs will be 7 Columns, each column contains a group of opening hours.

You can try CustomScrollView with multiple SliverLists for your purposes instead of using SingleChildScrollView:
Widget build(BuildContext context) {
return CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate([
Column(
children: <Widget>[
//Widgets you want to use
],
),
]),
),
SliverList(
delegate: SliverChildListDelegate([
Column(
children: <Widget>[
//Widgets you want to use
],
),
]),
),
],
);
}

Related

How to get second ListView take all space in Column?

I have got parent Widget:
return Column(children: [
Windget1(), // I tried to make both of them Expanded
Windget2(),
]
Windget1 and Windget2 are ListView and look like:
ListView.builder(
shrinkWrap: true,
controller: ScrollController(),
itemBuilder: (ctx, idx) =>
SingleJobItem(controller.jobList[idx], key: UniqueKey()),
itemCount: controller.jobList.length)
);
At start Widgets1 display containers, that should be moved to Widget2 and Widget2 should take all available space, but it do not happens. It's take only half of size.
I tried different combinations with wrapping Column's widgets in Expand, Tried to remove/add flexible in Widget1/2, but nothing do not work.
What I am seeing on start:
What I am getting at result:
I need to make second take all available space.
I tried to make one of Widget Expanded, but it five me overflow:
return Column(
children: [
Widget1(),
Expanded(child: Widget2())
]
)
Bind your column with SingleChildScrollView
SingleChildScrollView(
child: Stack(
children:[
Column(
children: [
// your widgets
]
),
Align(
alignment: Alignment.bottomCenter,
child: //your button
)
]
)
)
Try below code hope its helpful to you. just try add padding: EdgeInsets.zero,
ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: 10,
physics: ScrollPhysics(),
itemBuilder: (context, index) {
return Container(
child:Text('Add Text! '),
);
},
),
If I got your question correctly then, wrap your Widget2() inside Expanded and do not use any Expanded in Widget1():
return Column(children: [
Windget1(),
Expanded(child: Windget2()),
]

Achieve multiple rows in gridview flutter

I am trying to get the layout as in the gif below but could not get any resource to continue with. There are two rows with multiple columns and is scrollable. How can I achieve such layout?
As your question says, you follow this approach, and it is not containing multiple columns, items are having different width. This snippet will work fine.
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column(
children: [
Row(
key: const ValueKey("row1"),
children: _items(),
),
Row(
key: const ValueKey("row2"),
children: _items(),
),
],
),
),
You can achieve this by SingleChildScrolView widget with axis : horizontal and that listitem will be created using FilterChip widget.
like this :
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Wrap(
spacing: 10.0,
runSpacing: 5.0,
children: [...generateTags()],
),
),
generateTags(){
return _keywords.map((e) => FilterChip(label : e)).toList();
}
var _keywords =["hello" , "hi" ,"words 1" , "word 2"];

Make Column begin in a fixed position on the screen

I'm trying to make a column with two children begin at a certain position on the screen. The 2nd child is a dynamic ListView where I do not want the number of children changing the position of the column.
How is this possible?
Just wrap your ListView with Expanded widget .
Example:
Column(
children: [
Expanded( //here
child: ListView.builder(
itemBuilder: (context, index) => Text('test $index'),
itemCount: 50, ),
),
Column(
children: [
Container(height: 100, color: Colors.yellow),
OutlinedButton(onPressed: (){}, child: Text('Test'),),
Text('Another Test')
]
)
],
),
I used a Listview.builder because I don't want to do it one by one. Just for the example
You can learn more about Expanded widget by watching this Official video or by visiting flutter.dev

FloatingActionButton over last item of list

I have a scrollable list with a FloatingActionButton. I would like to make the list to finish before the FloatingActionButton because last item of list isn't visible anymore(FloatingActionButton it's over list item)
return Scaffold(
body: ListView(
scrollDirection: Axis.vertical,
controller: _scrollController,
shrinkWrap: true,
children: <Widget>[
_buildUpcomingExpansionTileEvents(myUpcomingEvents),
_buildPastExpansionTileEvents(myPastEvents),
],
),
floatingActionButton: UnicornDialer(
parentButtonBackground: Colors.blue,
orientation: UnicornOrientation.VERTICAL,
parentButton: Icon(OMIcons.add),
childButtons: childButtons,
),
);
How could I change my list to finish with one empty item? Or what's the best solution to this problem?
FloatingActionButton has size of 56 for non-mini and 40 for mini, so what you can do is use padding in ListView like:
ListView(
padding: EdgeInsets.only(bottom: 56), // if you have non-mini FAB else use 40
// ...
),
To solve this I just add a sized box as the last element in the list. That way you still get the end of list highlight in the correct place when the user scrolls to the bottom of the list.
ListView(
scrollDirection: Axis.vertical,
controller: _scrollController,
shrinkWrap: true,
children: <Widget>[
_buildUpcomingExpansionTileEvents(myUpcomingEvents),
_buildPastExpansionTileEvents(myPastEvents),
SizedBox(
height: 100, // or whatever height works for your design
),
],
),

Flutter ListView.Builder() in scrollable Column with other widgets

I have a TabBarView() with an amount of different views. I want of them to be a Column with a TextField at top and a ListView.Builder() below, but both widgets should be in the same scrollable area (scrollview). The way I implemented it threw some errors:
#override
Widget build(BuildContext context) {
return new Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: new TextField(
decoration: new InputDecoration(
hintText: "Type in here!"
),
)
),
new ListView.builder(
itemCount: _posts.length, itemBuilder: _postBuilder)
],
);
}
Error:
I/flutter (23520): The following assertion was thrown during performResize():
I/flutter (23520): Vertical viewport was given unbounded height.
I/flutter (23520): Viewports expand in the scrolling direction to fill their container.In this case, a vertical
I/flutter (23520): viewport was given an unlimited amount of vertical space in which to expand. This situation
I/flutter (23520): typically happens when a scrollable widget is nested inside another scrollable widget.
I/flutter (23520): If this widget is always nested in a scrollable widget there is no need to use a viewport because
I/flutter (23520): there will always be enough vertical space for the children. In this case, consider using a Column
I/flutter (23520): instead. Otherwise, consider using the "shrinkWrap" property (or a ShrinkWrappingViewport) to size
I/flutter (23520): the height of the viewport to the sum of the heights of its children.
I read about stacking the ListView.builder() in an Expanded-Area but it made the textfield kind of "sticky" which is not what I want. :-)
I also came across CustomScrollView but didn't fully understand how to implement it.
Here is the solution:
SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
children: <Widget>[
Text('Hey'),
ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount:18,
itemBuilder: (context,index){
return Text('Some text');
})
],
),
),
Placing the ListView inside an Expanded widget should solve your problem:
#override
Widget build(BuildContext context) {
return new Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: new TextField(
decoration: new InputDecoration(
hintText: "Type in here!"
),
)
),
new Expanded(child: ListView.builder(
itemCount: _posts.length, itemBuilder: _postBuilder))
],
);
}
Use SingleChildScrollView which allows the child widget to scroll
Solution
SingleChildScrollView(
child: Column(
children: <Widget>[
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
Two properties used here
shrinkWrap: true
only occupies the space it needs (it will still scroll when there more items).
physics: NeverScrollableScrollPhysics()
Scroll physics that does not allow the user to scroll. Means only Column+SingleChildScrollView Scrolling work.
Reason for the error:
Column expands to the maximum size in main axis direction (vertical axis), and so does the ListView
Solution
You need to constrain the height of the ListView, so that it does expand to match Column, there are several ways of solving this issue, I'm listing a few here:
If you want to allow ListView to take up all remaining space inside Column use Flexible.
Column(
children: <Widget>[
Flexible(
child: ListView(...),
)
],
)
If you want to limit your ListView to certain height, you can use SizedBox.
Column(
children: <Widget>[
SizedBox(
height: 200, // constrain height
child: ListView(),
)
],
)
If your ListView is small, you may try shrinkWrap property on it.
Column(
children: <Widget>[
ListView(
shrinkWrap: true, // use it
)
],
)
Use physics: NeverScrollableScrollPhysics() and shrinkWrap: true inside ListView.Builder() and enjoy
Here is an efficient solution:
class NestedListExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
const SliverToBoxAdapter(
child: Text('Header'),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(ctx, index) {
return ListTile(title:Text('Item $index'));
},
),
),
],
);
}
}
Here is a preview on dartpad.
You can use a SliverToBoxAdapter for the other children as only Slivers can be a direct child of a CustomScrollView.
If all the list items are the same height, then you could use SliverFixedExtentList, which is more efficient because the height of each child isn't calculated on the fly, but you will have to know the exact pixel height. You could also use a SliverPrototypeExtentList, where you provide the first item in the list(the prototype), and all the other children will use the height of the prototype so you don't need to know the exact height in pixels.
Use Expanded widget to constrain without overflowing those pixels, :)
Column(
children: <Widget>[
Expanded(
child: ListView(),
),
Expanded(
child: ListView(),
),
],
)
just add
Column(
mainAxisSize: MainAxisSize.max, //Add this line onyour column
children:[
SomeWidget(),
Expanded(child:ListView.builder())
]
)
In my case with a future i did it like this:
SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text("Hey ho let's go!"),
Flexible(
child: FutureBuilder(
future: getData(),
builder: (BuildContext context,
AsyncSnapshot<List<Sale>> snapshot) {
if (snapshot.connectionState != ConnectionState.done ||
snapshot.hasData == null) {
return CircularProgressIndicator();
} else {
data = snapshot.data;
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return dataItemWidget(size, data[index], context);
},
itemCount: data.length,
);
}
},
),
),
],
),
),
//If you want Listview.builder inside ListView and want to scroll the parent ListView// //whenever the Items in ListView.builder ends or start you can do it like this
body: ListView(
physics: ScrollPhysics(),
children: [
SizedBox(height: 20),
Container( height: 110.0 *5, // *5 to give size to the container //according to items in the ListView.builder. Otherwise will give hasSize Error
child:ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
itemCount: 5,
itemBuilder: (BuildContext context, int indexChild) {
return InkWell(child:Container(height:100));}))
),]),
The best way will be to make the column scrollable by making the column child of SingleChildScrollView and then assigning the same ScrollController to both the SingleChildScrollView and the ListView.builder. This will make the text field and the below ListView as scrollable.
Just add physics: NeverScrollableScrollPhysics() in ListView.builder() so you can scroll
Add physics: NeverScrollableScrollPhysics() inside Listview.builder() method and the nested Listview will scroll
Column is not scrollable, which is why the TextField on top wouldn't scroll but the ListView on the bottom would.
The best way to solve this in my opinion is to make your TextField the first item in your ListView.
So you won't need a column, your parent widget is the ListView, and its children are the TextField followed by the remaining items you build with _postBuilder.
return Column(
children: [
Text("Popular Category"),
Expanded(
child: ListView.builder(`enter code here`
shrinkWrap: false,
scrollDirection: Axis.horizontal,
itemCount: 3,
itemBuilder: (context, index) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("hello"),
],
);
}),
),
],
);
body: SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
children: [
getFiltersOnHomePage(),
SizedBox(
child: StreamBuilder(
stream: FirebaseFirestore.instance.collection('posts').snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (ctx, index) => Container(
margin: EdgeInsets.symmetric(
horizontal: width > webScreenSize ? width * 0.3 : 0,
vertical: width > webScreenSize ? 15 : 0,
),
child: PostCard(
snap: snapshot.data!.docs[index].data(),
),
));
},
),
),
],
),
),[enter image description here][1]***You will be able to scroll through the page by using Expanded Widget
Blockquote
Using this you can scroll over the entire page. This page includes a row and a listview builder inside a scrollable column.
In my case, I added a Container with transparent color and height up to 270 to solve this one.
Column(
children: <Widget>[
ListView(
shrinkWrap: true, // use it
),
Container(
color: Colors.transparent,
height: 270.0,
),
],
)