When the scrollDirection is set to vertical, it works as expected. The problem is when I set the section.axis to Axis.horizontal so that the ListView displays the widgets horizontally.
This problem wont solve using Flexible or Expanded widget, because the the height of the ListView needs to be defined by the widgets in the list.
As you can see shrinkWrap is also enabled. So I dont know wth is going on here, thanks for helping.
Console says:
'constraints.hasBoundedHeight': is not true.
The relevant error-causing widget was: ListView
class SectionWidget extends StatelessWidget {
final Section section;
const SectionWidget({#required this.section});
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(section.title),
ListView.separated(
shrinkWrap: true,
scrollDirection: section.axis,
physics: section.axis == Axis.vertical
? NeverScrollableScrollPhysics()
: null,
itemCount: section.itemList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 100,
width: 100,
color: Colors.red,
); // Just to test if it works
},
separatorBuilder: (BuildContext context, int index) {
double paddingBetween = 10;
if (section.axis == Axis.horizontal) {
return SizedBox(width: paddingBetween);
} else {
return SizedBox(height: paddingBetween);
}
},
),
],
);
}
}
That's because Column or Row gives as much height/width that their children need and ListView takes as much height/width available from its parent.
To fix this, just wrap ListView in a Container. Like this:
import 'package:flutter/material.dart';
class SectionWidget extends StatelessWidget {
final Section section;
const SectionWidget({#required this.section});
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(section.title),
Container(
height: section.axis == Axis.horizontal ? 100 : 700, // I just set these values
width: section.axis == Axis.horizontal ? 350 : 100, // to fit with my device.
child: ListView.separated( // If you want to fit for
shrinkWrap: true, // all devices, use MediaQuery
scrollDirection: section.axis,
physics: section.axis == Axis.vertical
? NeverScrollableScrollPhysics()
: null,
itemCount: section.itemList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 100,
width: 100,
color: Colors.red,
); // Just to test if it works
},
separatorBuilder: (BuildContext context, int index) {
double paddingBetween = 10;
if (section.axis == Axis.horizontal) {
return SizedBox(width: paddingBetween);
} else {
return SizedBox(height: paddingBetween);
}
},
),
),
],
);
}
}
For more info about MediaQuery
Edit:
I think using a gridview would be more suited for this. I've remade the build code for ya to suit different children's sizes. The positioning of the children and other stuff I think you can manage.
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: Text(section.title),
),
SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 1),
shrinkWrap: true,
scrollDirection: section.axis,
physics: section.axis == Axis.vertical
? NeverScrollableScrollPhysics()
: null,
itemCount: section.itemList.length,
itemBuilder: (context, index) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
// Spacer(),
Container(
margin: EdgeInsets.all(10),
height: 100,
width: 100,
color: Colors.red,
),
// Spacer(),
],
);
},
),
),
],
);
}
Related
I'm getting the error stated that Incorrect use of ParentDataWidget while using ListView widget into Stack widget and using ListView.builder widget into ListView widget. (I need a scrollable page that contains ListView and GridView)
It works fine in the emulator (not working in the real device) but in a log, I'm getting error.
I'm posting both screenshots and code here.
Please help!
Screenshot of emulator
Screenshot of a real device
Check the below code
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: buildAppBar(),
body: Stack(
children: [
buildBody()
],
)
);
}
Widget buildBody() {
return Container(
padding: EdgeInsets.all(15.0),
child: Expanded(
child: ListView(
shrinkWrap: true,
children: [
buildTitleSection(),
buildSearchSection(),
buildFilterSortSection(),
buildListView(),
buildGridView()
],
),
)
);
}
Widget buildListView() {
return Visibility(
visible: isListVisible,
child: Expanded(
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(),
child: ListView.builder(
itemCount: items.length,
shrinkWrap: true,
physics: PageScrollPhysics(),
scrollDirection: Axis.vertical,
itemBuilder: (context, index) =>
buildRowItemsList(context, index),
)))),
);
}
Widget buildGridView() {
var screenWidth = MediaQuery
.of(context)
.size
.width;
var screenHeight = MediaQuery
.of(context)
.size
.height;
return Builder(builder: (BuildContext context) {
return Visibility(
visible: isGridVisible,
child: Expanded(
child: GridView.count(
crossAxisCount: 2,
childAspectRatio: screenWidth / (screenHeight * 0.7),
scrollDirection: Axis.vertical,
physics: PageScrollPhysics(),
shrinkWrap: true,
children: List.generate(items.length, (index) {
return buildRowItemsGrid(context, index);
})),
),
);
});
}
I used a ListView inside a Drawer Widget and Used ListView.Builder inside that ListView to print out menus. Now All Menus are Printed Perfectly But The Drawer is not Scrolling. How to make it scroll?
Widget build(BuildContext context) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Guide to Make Money'),
],
),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('images/header_photo.jpg'),
fit: BoxFit.cover),
),
),
Container(
height: double.maxFinite,
child: ListView.builder(
padding: EdgeInsets.only(top: 0.0),
itemBuilder: (context, index) {
final profession = professionList[index];
return Ink(
color: selectedLink == index ? Colors.blueGrey : null,
child: ListTile(
title: Text(profession.heading),
onTap: () {
setState(() {
selectedLink = index;
});
Navigator.pushNamed(context, profession.destinationRoute);
},
leading: index == 0
? Icon(
Icons.home,
)
: Icon(Icons.description),
),
);
},
itemCount: professionList.length,
),
),
],
),
);
}
I need to make it Scroll... Please Help
P.S: Hi, I'm new to Flutter and also Stack overflow.. I wanted to upload image as well but this website say's I need to have 10 reputation at least... So, I have just a Code for you.. I hope you can figure out and help me with this.
Try this, Column instead ListView, and Expanded instead Container(height: double.maxFinite
return Drawer(
child: Column(
children: <Widget>[
DrawerHeader(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('Guide to Make Money'),
],
),
decoration: BoxDecoration(
color: Colors.white,
),
),
Expanded(
child: ListView.builder(
padding: EdgeInsets.only(top: 0.0),
itemCount: 22,
itemBuilder: (context, index) {
return Ink(
color: true ? Colors.blueGrey : null,
child: ListTile(
title: Text("profession.heading"),
onTap: () {},
leading: index == 0
? Icon(
Icons.home,
)
: Icon(Icons.description),
),
);
},
),
),
],
),
);
Container(
height: double.maxFinite,
child: ListView.builder(
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, i) {
return new ListTile(
title: new Text(data[i]["title"]),
);
}))
We can just add
physics: ClampingScrollPhysics(),
to the ListView.builder and it scrolls perfectly
I would like to create a horizontal listView where the first item is always an + icon. After get image, it will move to second item and so on.
This is what I tried
Container(
color: const Color(0xFF00FF00),
padding: EdgeInsets.only(top: 15),
height: MediaQuery.of(context).size.height * 0.25,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 1,
itemBuilder: (context, index) {
return _buildRow();
}),
)
Widget _buildRow() {
return IconButton(
icon: Center(
child: Icon(
Icons.camera,
size: 30.0,
),
),
onPressed: () {
getImage();
},
);
}
You could start your list of widgets with just the plus widget. And you add your following widgets on the first position of the widget using dart's insert funcion in lists. Something like this:
listWidgets.insert(0, yourWidget);
Doing that you plus widget always will be the last item of the list and will be displayed on the last position.
Widget initialWidget = _buildRow();
List<Widget> listWidgets = [initialWidget];
Container(
color: const Color(0xFF00FF00),
padding: EdgeInsets.only(top: 15),
height: MediaQuery.of(context).size.height * 0.25,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 1,
itemBuilder: (context, index) {
return listWidgets[index]
),
)
Widget _buildRow() {
return IconButton(
icon: Center(
child: Icon(
Icons.camera,
size: 30.0,
),
),
onPressed: () {
Widget yourWidget = <your-logic-to-create-widget>;
setState(() {
listWidgets.insert(0, yourWidget);
})
},
);
}
I wanted to recreate my List UI like the one in the following Pic:
As you can see the Leading Icon in the List tile has a vertical divider to its right side and it is intersected with the bottom divider, but I'm not able to achieve that look and I'm only able to recreate this UI.
Here is the Code I'm Using:
contentBuilder: (BuildContext context) {
return Container(
child: FutureBuilder(
future: _getFoodDetails(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
print(snapshot.data);
if (snapshot.data == null) {
return Container(child: Center(child: Text("Loading...")));
} else {
return Expanded(
child: ListView.separated(
itemCount: snapshot.data.length,
separatorBuilder: (context, index) {
return Divider();
},
itemBuilder: (BuildContext context, int index) {
return ListTile(
dense: false,
leading: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(Icons.phone, color: Colors.blue),
VerticalDivider(),
],
),
title: Text(snapshot.data[index].foodTitle),
subtitle: Text(snapshot.data[index].foodQuantity),
onTap: () {},
trailing: Icon(Icons.keyboard_arrow_right,
color: Colors.yellow, size: 30.0),
);
},
),
);
}
},
),
);
}
Please suggest how can i Achieve the Desired UI Look with the Dividers proeprly intersecting with each other and remove the padding which im obtaining.
One of the solution that I can think of here is to wrap your row in IntrinsicHeight() if you are using VerticalDivider as separator in Row widget like the sample below:
IntrinsicHeight(
child: Row(
children: [
Text('Hello'),
VerticalDivider(
color: Colors.black,
thickness: 2,
),
Text('World'),
],
),
)
contentBuilder: (BuildContext context) {
return Container(
child: FutureBuilder(
future: _getFoodDetails(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
print(snapshot.data);
if (snapshot.data == null) {
return Container(child: Center(child: Text("Loading...")));
} else {
return Expanded(
child: ListView.separated(
itemCount: snapshot.data.length,
separatorBuilder: (context, index) {
return Divider();
},
itemBuilder: (BuildContext context, int index) {
return Container(
width: MediaQuery.of(context).size.width,
height: 80,
color: Colors.blue,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Icon(Icons.phone, color: Colors.blue),
Container(
width: 1,
color: Colors.white,
),
const SizedBox(
width: 10,
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Text(snapshot.data[index].foodTitle),
Text(snapshot.data[index].foodQuantity),
],
),
const Spacer(),
const Icon(Icons.keyboard_arrow_right,
color: Colors.yellow, size: 30.0),
const SizedBox(
width: 10,
),
],
),
),))}}}));
hope this will help
I'm trying to call an api when I expand the tile by using future builder. Which returns a list. However, when I click on it, it expands the whole screen and doesn't display anything.
Here's an image of what I'm describing:
Here's the code:
Widget build(BuildContext context) {
return FutureBuilder(
future: marketApiCall(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if(snapshot.hasData) {
return ListView.separated(
shrinkWrap: true,
separatorBuilder: (BuildContext context, int index) => Divider(),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int count) {
return ExpansionTile(
title: Text(snapshot.data[count].itemName),
children: <Widget>[
FutureBuilder(
future: getItemDetail(itemName: snapshot.data[count].urlName),
builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) {
if(snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData){
debug.output(fromFunction:"Widget build", message: snapshot.data[0]['en']['description']);
return Container(
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.8,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Item description: ", style: TextStyle(fontWeight: FontWeight.bold)),
Expanded(
child: Text(snapshot.data[0]['en']['description'].toString().replaceAll(reg, ''))
)
],
)
],
)
);
} else {
return Center(
child: Text("Error: No data")
);
}
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}
)
],
);
},
);
} else {
return ListTile(
title: Text("There was an error with the api call, please try again later")
);
}
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
I've tried adding shrinkwrap to see if that would work. That didn't.
Also, debug.output is just a custom function that displays print messages so there is no need to worry about that. As you can see, it's a future builder inside a futurebuilder. I've tried separating the inside future builder to a separate stateful class and returning a column to the expansion tile, however that didn't work either.
I think the proble is right after the "FutureBuilder"
return Container(
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.8,
You're giving its child Container an 80% of the screen height, that, with the ExpansionTile's height, will occupy the whole screen (or almost).
Try removing the container height and let its child to size the container.