I am trying to create a nested list view each wrapped by a column. The parent widget (widget 1) has a column with a vertical list view and each list view item (widget 2) is a column with a horizontal list view. So far I am able to get it to render with the following code where in widget 2 I wrap the horizontal list view with a Container and a specified height. I am trying to use not use a fixed height, however, so I have tried using Flexible and Expanded instead of Container but both of these result in the unbounded height constraints error.
class Widget1State extends State<Widget1> {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
child: Scrollbar(
child: ListView.builder(
padding: const EdgeInsets.all(8.0),
itemCount: getWidgets().length,
itemBuilder: (BuildContext context, int index) {
return Widget2();
},
),
),
),
],
),
);
}
}
class Widget2State extends State<Widget2> {
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
height: 30,
child: Scrollbar(
child: ListView.builder(
padding: const EdgeInsets.all(8.0),
scrollDirection: Axis.horizontal,
itemCount: getWidgets2().length,
itemBuilder: (BuildContext context, int index) {
return Text('widget');
},
),
),
),
],
);
}
}
As you can see below this is how it currently works where the exercises is the parent list view and the sets are the child list view. Currently because the sets list is in a Container it takes up space when it's empty and also doesn't size to whatever makes up the list item. I want to change the sets list view so that it only takes up the space is needed by the list item.
Given that Listview takes all avaible height if you dont provide one it will result in failure.
In order to proovide alternative solutions I need you especify how is the design that you want. Coudl you give more details?
---- Edited:
This solution was found here: Flutter: Minimum height on horizontal list view
You can change the widget 2 from Listview to SingleChildScrollView:
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [
const Padding(
padding: EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(label: Text('Workout Name')),
),
),
...List.generate(
exercises, // number of exercises
(index) => Column(
mainAxisSize: MainAxisSize.min,
children: [
DropdownButton<String>(items: const [
DropdownMenuItem(child: Text('Select Exercise'))
], onChanged: (value) {}),
SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Row(
children: List.generate(
sets, //number of sets
(index) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Set $index'),
Text('reps'),
Text('rest')
],
)),
)),
TextButton.icon(
onPressed: () {
addSet();
},
icon: const Icon(Icons.add),
label: Text('Set'))
],
))
],
),
bottomNavigationBar: IconButton(
onPressed: () {
addExercise();
},
icon: const Icon(Icons.add)),
);
}
I've tried this code and works as you need, without setting the height of the 'Sets' scrollview.
I want to point the use of the bottomNavigationBar to the AddExercise button, instead of a Column>Listview structure. Separating the button in the bottomNavBar you can use the body atribute freely.
You want to use Flutter default widgets when posible.
I am trying to build a Nested listview but getting "RenderFlex children have non-zero flex but incoming height constraints are unbounded" error with below code.
Layers are like this...
Each item of a horizontal ListView has a Text widget and a ListView widget.
At the second level, each item of vertical ListView contains again a Text widget and a ListView.
At the third level, each item of the ListView contains a Text widget.
-Horizontal ListView
- Person's Name
- ListView
- Relation Name
- ListView
- Person's Name
Thanks in advance.
person.relations is a Map<String, List<Person>>
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Relationship Explorer"),
),
body: SafeArea(
child: BlocBuilder<RelationCubit, CubitState>(
bloc: _cubit,
builder: (_, state) {
if (state is RelationSuccessState) {
return ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (_, outerIndex) =>
_relationTreeView(context, outerIndex),
itemCount: _cubit.people.length,
);
} else {
return WaitWidget();
}
},
),
),
);
}
Widget _relationTreeView(BuildContext context, int outerIndex) {
var person = _cubit.people[outerIndex];
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(person.displayName ?? ''),
Expanded(
child: Container(
width: MediaQuery.of(context).size.width,
child: ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: person.relations?.length,
itemBuilder: (_, index) {
var persons = person.relations?[index];
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(person.relations!.keys.elementAt(index)),
Expanded(
child: Container(
width: MediaQuery.of(context).size.width,
child: ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: persons.length,
itemBuilder: (_, index) {
var innerPerson = persons[index];
return Text(innerPerson.displayName ?? '');
},
),
),
)
],
);
},
),
),
),
],
);
}
Wrap the list view with a container and give a height.
Widget build(BuildContext context) {
return Container(
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: data[1].store.length,
itemBuilder: (BuildContext context, int index) {
return Wrap(
children: [
Text(data[1].store[index].number.toString()),
],
);
},
),
);
}
I used ListView.builder, I want to wrap Horizontal & scroll Vertical,.
I spend a lot of time on this stack...
My data from local JSON, with Future Builder and return to ListView.builder...
please see attach..
Thanks All...
Replace your Container with the below code.
SingleChildScrollView(
child: Column(
children: [
Wrap(
children: List<Widget>.generate(
1000,
(int index) {
return Text(index.toString() + ' ');
},
),
)
],
)),
I have widget which return CircularProgressIndicator()
it shows on the circular mark upper left of screen.
However I want to put this as overlay and put at the center of screen.
I am checking widget list but I cant find what Widget should I use as overlay.
On which layer should I put this on??
For now my code is like this ,when loading it shows CircularProgressIndicator instead of ListView
However I want to put CircularProgressIndicator() on ListView
Widget build(BuildContext context) {
if(loading) {
return CircularProgressIndicator();
}
return ListView.builder(
controller: _controller,
itemCount: articles.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(articles[index]),
);
},
);
}
Thank you very much for answers.
I solve with stack Widget like this below.
At first I try to use overlay, but I bumped into some errors.
So, I use simply stack.
#override
Widget build(BuildContext context) {
Widget tempWidget = new CircularProgressIndicator();
if(loading) {
tempWidget = new CircularProgressIndicator();
}
else {
tempWidget = new Center();//EmptyWidget
}
return Stack(
children: <Widget>[
ListView.builder(
controller: _controller,
itemCount: articles.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(articles[index].title),
onTap: () => onTapped(context,articles[index].url),
);
},
),
Center(
child: tempWidget
),
]
);
}
Stack(
children: <Widget>[
ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Container(
height: 100,
color: Colors.red,
);
},
itemCount: 10,
),
Center(
child: CircularProgressIndicator(),
),
],
)
Stack(
children: <Widget>[
ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Container(
height: 100,
color: Colors.red,
);
},
itemCount: 10,
),
isLoading? Container(child: Center(
child: CircularProgressIndicator(),
)): Container(), //if isLoading flag is true it'll display the progress indicator
],
)
or you can use futureBuilder or streamBuilder when loading data from somewhere and you want to change the ui depending on the state
To overlay or position items on top of each other you would usually use a Stack widget or Overlay as described here. For your usecase I would recommend checking out the modal progress hud package.
I am trying to achieve a very common behavior nowadays which is to have a horizontal List within another widget that is at the same time scrollable. Think something like the home screen of the IMDb app:
So I want to have a widget that scrolls vertically with few items on them. At the top of it, there should be a horizontal ListView, followed up with some items called motivationCard. There are some headers in between the list and the cards as well.
I got something like this on my Widget:
#override
Widget build(BuildContext context) => BlocBuilder<HomeEvent, HomeState>(
bloc: _homeBloc,
builder: (BuildContext context, HomeState state) => Scaffold(
appBar: AppBar(),
body: Column(
children: <Widget>[
Text(
Strings.dailyTasks,
),
ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: tasks.length,
itemBuilder: (BuildContext context, int index) =>
taskCard(
taskNumber: index + 1,
taskTotal: tasks.length,
task: tasks[index],
),
),
Text(
Strings.motivations,
),
motivationCard(
motivation: Motivation(
title: 'Motivation 1',
description:
'this is a description of the motivation'),
),
motivationCard(
motivation: Motivation(
title: 'Motivation 2',
description:
'this is a description of the motivation'),
),
motivationCard(
motivation: Motivation(
title: 'Motivation 3',
description:
'this is a description of the motivation'),
),
],
),
),
);
this is the error I get:
I/flutter (23780): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter (23780): The following assertion was thrown during performResize():
I/flutter (23780): Horizontal viewport was given unbounded height.
I/flutter (23780): Viewports expand in the cross axis to fill their container and constrain their children to match
I/flutter (23780): their extent in the cross axis. In this case, a horizontal viewport was given an unlimited amount of
I/flutter (23780): vertical space in which to expand.
I have tried:
Wrapping the ListView with an Expanded widget
Wrapping the Column with SingleChildScrollView > ConstrainedBox > IntrinsicHeight
Having CustomScrollView as a parent, with a SliverList and the List within a SliverChildListDelegate
None of these work and I continue getting the same kind of error. This is a very common thing and shouldn't be any hard, somehow I just cannot get it to work :(
Any help would be much appreciated, thanks!
Edit:
I thought this could help me but it didn't.
Well, Your Code Work Fine with wrapping your- ListView.builder with Expanded Widget &
setting mainAxisSize: MainAxisSize.min, of Column Widget.
E.x Code of what you Have.
body: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'Headline',
style: TextStyle(fontSize: 18),
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: 15,
itemBuilder: (BuildContext context, int index) => Card(
child: Center(child: Text('Dummy Card Text')),
),
),
),
Text(
'Demo Headline 2',
style: TextStyle(fontSize: 18),
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (ctx,int){
return Card(
child: ListTile(
title: Text('Motivation $int'),
subtitle: Text('this is a description of the motivation')),
);
},
),
),
],
),
Update:
Whole page Is Scroll-able with - SingleChildScrollView.
body: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'Headline',
style: TextStyle(fontSize: 18),
),
SizedBox(
height: 200.0,
child: ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: 15,
itemBuilder: (BuildContext context, int index) => Card(
child: Center(child: Text('Dummy Card Text')),
),
),
),
Text(
'Demo Headline 2',
style: TextStyle(fontSize: 18),
),
Card(
child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
),
Card(
child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
),
Card(
child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
),
Card(
child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
),
Card(
child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
),
],
),
),
Screenshot:
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: 7,
itemBuilder: (_, i) {
if (i < 2)
return _buildBox(color: Colors.blue);
else if (i == 3)
return _horizontalListView();
else
return _buildBox(color: Colors.blue);
},
),
);
}
Widget _horizontalListView() {
return SizedBox(
height: 120,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (_, __) => _buildBox(color: Colors.orange),
),
);
}
Widget _buildBox({Color color}) => Container(margin: EdgeInsets.all(12), height: 100, width: 200, color: color);
}
We have to use SingleScrollView inside another SingleScrollView, using ListView will require fixed height
SingleChildScrollView(
child: Column(
children: <Widget>[
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [Text('H1'), Text('H2'), Text('H3')])),
Text('V1'),
Text('V2'),
Text('V3')]))
If someone gets the renderview port was exceeded error. warp your ListView in a Container widget and give it the height and width property to fix the issue
Column(
children: <Widget>[
Text(
Strings.dailyTasks,
),
Container(
height: 60,
width: double.infinity,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: tasks.length,
itemBuilder: (BuildContext context, int index) =>
taskCard(
taskNumber: index + 1,
taskTotal: tasks.length,
task: tasks[index],
),
),
)
]
)
I tried in this code and I fixed my problem I hope solved your want it.
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
item(),
item(),
item(),
item(),
],
),
),
Horizontal ListView inside Vertical ListView using Builder
None of the answers proved to solve my issue, which was to have a horizontal ListView inside a Vertical ListView while still using ListBuilder (which is more performant than simply rendering all child elements at once).
Turned out it was rather simple. Simply wrap your vertical list child inside a Column, and check if index is 0 (or index % 3 == 0) then render the horizontal list.
Seems to work fine:
final verticalListItems = [];
final horizontalListItems = [];
ListView.builder(
shrinkWrap: true,
itemCount: verticalListItems.length,
itemBuilder: (context, vIndex) {
final Chat chat = verticalListItems[vIndex];
return Column( // Wrap your child inside this column
children: [
// And then conditionally render your Horizontal list
if (vIndex == 0) ListView.builder(itemCount: horizontalListItems.length itemBuilder: (context, hIndex) => Text('Horizontal List $hIndex')),
// Vertical list
Text('Item No. $vIndex')
],
);
},
),
for Web Chome you have to add MaterialScrollBehavior for horizontal scrolling to work. see(Horizontal listview not scrolling on web but scrolling on mobile) I demonstrate how to use the scrollcontroller to animate the list both left and right.
import 'package:flutter/gestures.dart';
class MyCustomScrollBehavior extends MaterialScrollBehavior {
// Override behavior methods and getters like dragDevices
#override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
};
}
return MaterialApp(
title: 'Flutter Demo',
scrollBehavior: MyCustomScrollBehavior(),
)
class TestHorizontalListView extends StatefulWidget {
TestHorizontalListView({Key? key}) : super(key: key);
#override
State<TestHorizontalListView> createState() => _TestHorizontalListViewState();
}
class _TestHorizontalListViewState extends State<TestHorizontalListView> {
List<String> lstData=['A','B','C','D','E','F','G'];
final ScrollController _scrollcontroller = ScrollController();
_buildCard(String value)
{
return Expanded(child:Container(
margin: const EdgeInsets.symmetric(vertical: 20.0),
width:300,height:400,child:Card(child: Expanded(child:Text(value,textAlign: TextAlign.center, style:TextStyle(fontSize:30))),)));
}
void _scrollRight() {
_scrollcontroller.animateTo(
_scrollcontroller.position.maxScrollExtent,
duration: Duration(seconds: 1),
curve: Curves.fastOutSlowIn,
);
}
void _scrollLeft() {
_scrollcontroller.animateTo(
0,
duration: Duration(seconds: 1),
curve: Curves.fastOutSlowIn,
);
}
_segment1()
{
return SingleChildScrollView(child:
Expanded(child:
Container(height:300,
width:MediaQuery.of(context).size.width,
child:Row(children: [
FloatingActionButton.small(onPressed: _scrollRight, child: const Icon(Icons.arrow_right),),
Expanded(child:Scrollbar(child:ListView.builder(
itemCount: lstData.length,
controller: _scrollcontroller,
scrollDirection: Axis.horizontal,
itemBuilder:(context,index)
{
return _buildCard(lstData[index]);
})
,),
),
FloatingActionButton.small(onPressed: _scrollLeft, child: const Icon(Icons.arrow_left),),
]))
,
)
);
}
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text("horizontal listview",)),body:
segment1(),
);
}
}
You just have to fix your height of your Listview (by wrapping it in a SizedBox for example).
This is because the content of your listview can't be known before the frame is drawn. Just imagine a list of hundreds of items.. There is no way to directly know the maximum height among all of them.