Horizontal scrolling ListView.builder inside SingleChildScrollView - wrap content height - flutter

My layout is a Form widget with some controls:
Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Form(
key: widget.addEventFormKey,
onChanged: () {},
child: Expanded(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(CustomResources.strings["add_event_category_label"], style: TextStyle(fontWeight: FontWeight.bold)),
/*some other widgets*/
Visibility(
child: Container(
height: 200.0,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: _attachments?.length,
itemBuilder: (context, index) {
return Stack(
children: [
Container(
margin: EdgeInsets.fromLTRB(0, 0, 5, 0),
child: Stack(
children: [
ClipRRect(
child: Image.file(File(_attachments[index]), cacheWidth: 200),
borderRadius: BorderRadius.circular(8.0),
),
],
),
),
IconButton(
onPressed: () {
File(_attachments[index]).delete();
setState(() => _attachments.remove(_attachments[index]));
},
icon: Icon(FontAwesomeIcons.trash, color: Colors.white, size: 20),
padding: EdgeInsets.only(top: 4, left: 4),
constraints: BoxConstraints(),
),
],
);
},
),
),
visible: _attachments.length > 0,
),
Visibility(child: Padding(padding: EdgeInsets.fromLTRB(0, 18, 0, 0)),visible: _attachments.length > 0),
SizedBox(
child: RaisedButton(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24.0)),
onPressed: _attachments.length < 3 ? () => _pickImage() : null,
padding: EdgeInsets.all(12.0),
color: Colors.blue,
child: Text(CustomResources.strings["add_event_add_photo_button_label"], style: TextStyle(color: Colors.white))),
width: double.infinity,
),
],
),
),
),
),
],
);
Problematic part is ListView.builder to display horizontally scrolled list of images. As you can see, picture will always get fixed width (200) and unknown height, because height depends from image aspect ratio. ListView.builder is wrapped with Container, so now it has constant height 200.
I want to force my ListView.builder to expand to child image height (it's single row ListView scrolled horizontally), image width always 200 and other widgets should be placed below it without any remaining space. With current approach, if image height is < 200, there will be remaining space left below image list. If image height is >200, image will be scaled (width/height).
I tried to wrap list view with Expanded widget instead Container, but it throws exception:
RenderFlex children have non-zero flex but incoming height constraints
are unbounded.
It says I still need to provide height, which I don't want to. How to solve this?

I think that's not possible with ListView.builder, because its height with dynamic items is not known while building widgets tree. Instead, my thumbnail widgets mus be built upfront, and added to Row after they have been built and wrap row with SingleChildScrollView to make it scrollable. So, my thumbnails list ready to place to my Form:
class AttachmentsListWidget extends StatelessWidget {
const AttachmentsListWidget({
Key key,
#required this.items,
#required this.onAttachmentDeleted,
}) : super(key: key);
final List<String> items;
final Function(String) onAttachmentDeleted;
#override
Widget build(BuildContext context) {
var _thumbnailsScrollController = ScrollController();
final thumbnails = <Widget>[];
Widget thumbnailsWidget;
if (items.length > 0) {
for (int i = 0; i < items.length; i++) {
thumbnails.add(
AttachmentWidget(
attachment: items[i],
onDeleteClicked: (String attachment) => onAttachmentDeleted(attachment),
),
);
}
thumbnailsWidget = Container(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Scrollbar(
isAlwaysShown: true,
controller: _thumbnailsScrollController,
child: SingleChildScrollView(
controller: _thumbnailsScrollController,
scrollDirection: Axis.horizontal,
child: Row(
children: thumbnails,
crossAxisAlignment: CrossAxisAlignment.start,
),
),
),
],
),
);
} else {
thumbnailsWidget = Container();
}
return thumbnailsWidget;
}
}
(also, always visible scrollbar added).

If you got RenderFlex children have non-zero flex but incoming height constraints are unbounded. try below code hope its help to you. add your Column inside Expanded() Widget and remove SingleChildScrollView() in your code
Expanded(
child: Column(
children:[
//Your Widgets
],
),
),

Related

How to fix Flutter web release showing grey screen but no debug error

I have created a flutter webapp but I have a screen that shows a grey page not sure why, I think its something to do with the listview since all pages with the listview have the same problem. Can you take a look at the code below to figure out the problem?
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
endDrawer: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 300),
child: const SideMenu()),
body: SingleChildScrollView(
child: SafeArea(
child: Container(
width: size.width,
constraints: BoxConstraints(minHeight: size.height),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
const NavBar(),
This is where the grey screen start to appears
const BrowseScreenBody(),
const Footer(),
]),
),
),
),
);
}
}
class BrowseScreenBody extends StatelessWidget {
const BrowseScreenBody({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
final events = Event.events.toList();
return Container(
margin: const EdgeInsets.symmetric(vertical: 20, horizontal: 40),
child: Expanded(
child: Padding(
padding: ...,
child: Column(
children: <Widget>[
RichText(
text: TextSpan(
children: [
...
],
),
),
//Events Lists
SizedBox(
height: size.height * 0.65,width: size.width,
child: ListView.builder(
shrinkWrap: true,
itemCount: events.length,
itemBuilder: (context, index) {
final event = events[index];
return Card(
child: Container(
decoration: BoxDecoration(
color: Colors.white),
child: Padding(
padding: ...
child: Column(
children: [
Row(
children: [
Container(
height: size.height * 0.25,
width: size. Width * 0.4,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(...))),
),
Row(
children: [
Column(
children: [...
],
),
],
)
],
)
],
),
),
),
);
}),
),
],
),
),
));
}
}
Everything works well on debug mode, but when hosted to github pages I can't seem to get the widgets shown.
The problem is that you are using an Expanded as child of a Container. Expanded can only be a child of a Flex widget like Row or Column. But it's very likely that you actually do get errors in debug mode as well in your console. Because when I tried your code (slightly modified to make it runnable) it did give an error. Something like
======== Exception caught by widgets library =======================================================
The following assertion was thrown while applying parent data.:
Incorrect use of ParentDataWidget.
The ParentDataWidget Expanded(flex: 1) wants to apply ParentData of type FlexParentData to a RenderObject, which has been set up to accept ParentData of incompatible type BoxParentData.
Usually, this means that the Expanded widget has the wrong ancestor RenderObjectWidget. Typically, Expanded widgets are placed directly inside Flex widgets.
The offending Expanded is currently placed inside a Padding widget.

Column/SingleChildScrollView not scrolling

I'm learning flutter to make an app. Here I'm trying to make a page to show the full post. The problem is that the page isn't scrolling as a single unit.
class SinglePostPage extends StatefulWidget {
final Post? post;
const SinglePostPage({Key? key, this.post}) : super(key: key);
#override
State<SinglePostPage> createState() => _SinglePostPageState();
}
class _SinglePostPageState extends State<SinglePostPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
margin: const EdgeInsets.only(top: 22.5),
padding: const EdgeInsets.fromLTRB(0, 5, 0, 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BackButton(),
Row(
children: [
InkWell(
onTap: () {
showDialog(...);
},
child: CircleAvatar(...),
Container(
padding: const EdgeInsets.only(left: 5.4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
onTap: () {
showDialog(...);
},
child: Text(
widget.post!.author[0].profileName,
),
),
const SizedBox(height: 4),
Text(
showTimeAgo(widget.post!.postTime).toString(),
),
],
),
),
],
),
PopupMenuButton(
icon: const Icon(Icons.more_vert),
itemBuilder: (context) => [...],
),
Container(
margin: const EdgeInsets.fromLTRB(12, 9, 12, 3),
// when the text is longer than the screen height it showed RenderFlex overflowed error so I put constraints. how to show it full and make this scroll
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.54,
minHeight: 50),
child: SingleChildScrollView(
child: Text(
widget.post!.postText,
textAlign: TextAlign.start,
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (widget.post!.postMedia.isNotEmpty)
CarouselSlider.builder(...),
const SizedBox(height: 4.5),
if (widget.post!.postMedia.length > 1)
buildDotIndicator(widget.post!.postMedia),
],
),
],
),
),
Container(
// post reactions row
),
CommentBodyWidget(
postId: widget.post!.postId,
),
],
),
);
}
}
I looked up for other answers and tried wrapping it with SingleChildScrollView, but it didn't work , and ListView with shrinkWrap: true also gave 'Incorrect use of ParentDataWidget' error.
CommentBodyWidget has a listview builder. It's scrolling on its own but the widget above isn't scrolling along with it.
How can I show this whole page and scroll together without having to limit the long post in a constrained container? Please help.
You can wrap body Column with SingleChildScrollView and use shrinkWrap: true, and use physics: NeverScrollableScrollPhysics(),. This will solve the issue.
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
//...
ListView.builder(
itemCount: itemlength,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return Text("item $index");
},
),
/// instead of listView
...List.generate(itemlength, (index) => Text("itemWidget"))
],
),
),
);
But I will encourage you to check CustomScrollView.
In order for the SingleChildScrollView to work, its parent's height should be defined.
You can achieve this by wrapping the SingleChildScrollView in a Container/SizedBox with defined height, or by wrapping it with the Expanded widget.
Pass SingleChildScrollView as body of your scaffold. Also shrinkwrap: true for the ListView is recommended so that it only takes up as much space as required, which will avoid unbounded height errors.
So instead of
...
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
...
Do this
...
Widget build(BuildContext context)
return Scaffold(
body: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(...
I recommend you to use CustomScrollView.
Try this:
CustomScrollView(
slivers: [
SliverFillRemaining(
hasScrollBody: false,
child: Column("""HERE PUT YOUR CODE""")
)]
),

ExpansionPanel with headers is not correctly aligned

I'm trying to build a Data Table variant so that the row can be expanded with extra information. I didn't found a way to do this with the DataTable class.
This is what I have:
class TestPanel extends StatefulWidget {
const TestPanel({Key? key}) : super(key: key);
#override
_TestPanelState createState() => _TestPanelState();
}
class _TestPanelState extends State<TestPanel> {
bool _customTileExpanded = false;
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(10),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: const <Widget>[
Expanded(
child: Text("Header 1"),
),
Expanded(
child: Text("Some Header 2"),
),
Expanded(
child: Text(""),
)
],
),
),
Expanded(
child: ListView.builder(
itemCount: 1,
itemBuilder: (BuildContext context, int index) {
return ExpansionPanelList(
animationDuration: Duration(milliseconds: 1000),
dividerColor: Colors.grey,
elevation: 1,
children: [
ExpansionPanel(
body: Container(
padding: EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Some text here",
style: TextStyle(
color: Colors.grey[700],
fontSize: 15,
letterSpacing: 0.3,
height: 1.3),
),
],
),
),
headerBuilder: (BuildContext context, bool isExpanded) {
return Container(
padding: EdgeInsets.all(10),
child: Row(
children: const <Widget>[
Expanded(
child: Text("Value 1"),
),
Expanded(
child: Text("Some Value 2"),
),
],
),
);
},
isExpanded: _customTileExpanded,
)
],
expansionCallback: (int item, bool expanded) {
setState(() => _customTileExpanded = !expanded);
},
);
},
),
),
],
),
);
}
}
Now this results in:
As you can see the alignment with the headers is not correct. I have no idea how I can make sure that those are always aligned.
There's a tricky solution here by adding SizedBox with the same width of the ExpansionPanelList icon "including it's padding" at the end of the header row like following:
Row(
children: [
Expanded(
child: Row(
children: const <Widget>[
Expanded(
child: Text("Header 1"),
),
Expanded(
child: Text("Some Header 2"),
),
],
),
),
// If the Header text "Some Header 2" is longer than the Expansion Header "Some Value 2" you'll need to increase the Sizedbox width.
const SizedBox(
64, //ExpansionPanelList IconContainer size: end margin 8 + padding 16*2 + size 24
),
],
),
Also, I dont' know if this would work with your idea but you can have dynamic rows height with DataTable2 Package but first you need to copy the package code and paste it in your root directory of your current project to make some edits on it "To make the row's height dynamic":
For having Dynamic row height remove height: effectiveDataRowHeight and height: effectiveHeadingRowHeight from the new widget file.
You can add constraints: BoxConstraints(minHeight: effectiveHeadingRowHeight,) and constraints: BoxConstraints(minHeight: effectiveDataRowHeight,) instead of above and use dataRowHeight parameter as min Height and same for headingRowHeight.
To center all data in cells add defaultVerticalAlignment: TableCellVerticalAlignment.middle to var dataRows in the widget code.
Result:
Image
You can also do the same with the DataTable class but at step3, You need to add defaultVerticalAlignment: TableCellVerticalAlignment.middle to the Table child in the image: Image
I prefer using DataTable2 as it has more customization, you can find it here: DataTable2
After That, You can add Icon at the end of the row. If it's clicked you add new row with extra information.

Flutter : Different width container depend of value

I want to implement a design like the one above, the problem is i don't know how to make the container size different depending on the number.
Expectaion
Source code
final userPlaylist = {
'Kaguya Love Dramatic': 10,
'Saenai Heroine': 8,
'Jujutsu Kaisen Kaikai Kitan': 3,
'Jujutsu Kaisen LOST IN PARADISE': 2,
'Kaguya Sama DADDY DADDY DO': 2,
};
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: userPlaylist.length,
itemBuilder: (context, index) {
final song = userPlaylist.keys.elementAt(index);
final totalPlay = userPlaylist.values.elementAt(index);
final margin = margins[index];
return Padding(
padding: const EdgeInsets.only(top: 12),
child: Row(
children: [
Expanded(child: CircleAvatar()),
SizedBox(width: 10),
Expanded(
flex: 4,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(song),
SizedBox(height: 10),
Container(
margin:EdgeInsets.only(right:margin),
height: 20,
decoration: BoxDecoration(
color: Colors.green,
),
),
],
),
),
SizedBox(width: 10),
Expanded(child: Text(totalPlay.toString(),style: TextStyle(color:Colors.white),))
],
),
);
},
),
],
);
}
}
and this size container applies if there are numbers that have the same value
Add a variable max right after the margin definition. This reduces the list of values to the largest value in the list:
final max = userPlaylist.values.reduce((curr, next) => curr > next ? curr : next);
Replace the green Container with:
Row(
children: [
Expanded(
flex: totalPlay,
child: Container(
height: 20,
decoration: BoxDecoration(
color: Colors.green,
),
),
),
Expanded(
flex: max - totalPlay,
child: Container(),
)
],
)
The green bar is a Row widget containing two Expanded widgets. The first Expanded widget has flex: totalPlay, and the second flex: max - totalPlay. This would show a green bar, the length is the song's value proportional to the max value.
(margin is not used)
Make each bar like this:
Row(
children: [
Expanded(
flex: totalPlay,
child: Container(color: Colors.green),
),
Expanded(
flex: maxTotalPlay - totalPlay,
child: Container(),
),
],
),
flexes of two Expanded widgets should divide the parent width proportionally among them.

A RenderFlex overflowed by 19 pixels on the bottom, while scrolling

In TabBarView -> Column, Iam getting this exception A RenderFlex overflowed by 120 pixels on the bottom.
while scrolling, It happens only on the particular part/container: TabBarView -> Column -> Container.
here is an image for better understanding sample image
here is the code for tabView.dart:
class TabView extends StatelessWidget {
List<Category> categories = [
];
final TabController tabController;
TabView({Key key, this.tabController}) : super(key: key);
#override
Widget build(BuildContext context) {
print(MediaQuery.of(context).size.height / 9);
return TabBarView(
physics: NeverScrollableScrollPhysics(),
controller: tabController,
children: <Widget>[
Column( **//Exception here**
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
margin: EdgeInsets.all(8.0),
height: MediaQuery.of(context).size.height/9,
width: MediaQuery.of(context).size.width,
// padding: EdgeInsets.only(top: 4.0),
child: ListView.builder(
//shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: categories.length,
itemBuilder: (_, index) => CategoryCard(
category: categories[index],
)),),
SizedBox(
height: 16.0,
),
Flexible(child: RecommendedList()),
],
),
Column(children: <Widget>[
SizedBox(
height: 16.0,
),
Flexible(child: RecommendedList())
]),
Column(children: <Widget>[
SizedBox(
height: 16.0,
),
Flexible(child: RecommendedList())
]),
Column(children: <Widget>[
SizedBox(
height: 16.0,
),
Flexible(child: RecommendedList())
]),
Column(children: <Widget>[
SizedBox(
height: 16.0,
),
Flexible(child: RecommendedList())
]),
]);
}
}
code for recommendedList.dart:
class RecommendedList extends StatelessWidget {
List<Product> products = [....];
#override
Widget build(BuildContext context) {
return Column( **//Exception here**
children: <Widget>[
Container(
height: 20,
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
IntrinsicHeight(
child: Container(
margin: const EdgeInsets.only(left: 16.0, right: 8.0),
width: 4,
color: Colors.lightBlue,
),
),
Center(
child: Text(
'Recommended',
style: TextStyle(
color: darkGrey,
fontSize: 16.0,
fontWeight: FontWeight.bold),
)),
],
),
),
Flexible(
child: Container(),
),//
],
);
}
}
These 2 classes are used in main page, here is the code:
return Scaffold(
resizeToAvoidBottomPadding: false,
bottomNavigationBar: CustomBottomBar(controller: bottomTabController),
body: CustomPaint(
painter: MainBackground(),
child: TabBarView(
controller: bottomTabController,
physics: NeverScrollableScrollPhysics(),
children: <Widget>[
SafeArea(
child: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
// These are the slivers that show up in the "outer" scroll view.
return <Widget>[
SliverToBoxAdapter(
child: appBar,
),
SliverToBoxAdapter(
child: topHeader, //child: ParallaxMain(),
),
SliverToBoxAdapter(
child: ProductList(
products: products,
),
),
SliverToBoxAdapter(
child: ProductList2(),
),
SliverToBoxAdapter(
child: tabBar,
),
];
},
body: Container(
child: TabView(
tabController: tabController,
),
//: MediaQuery.of(context).size.height/10,
),
),
),
CategoryListPage(),
CheckOutPage(),
ProfilePage()
],
),
),
);
and here is the exception i got:
A RenderFlex overflowed by 104 pixels on the bottom.
The relevant error-causing widget was:
Column file:///E:/arm%20dataset/flutter_ecommerce_template-m/lib/screens/main/components/tab_view.dart:59:11
The overflowing RenderFlex has an orientation of Axis.vertical.
The edge of the RenderFlex that is overflowing has been marked in the rendering with a yellow and black striped pattern. This is usually caused by the contents being too big for the RenderFlex.
Consider applying a flex factor (e.g. using an Expanded widget) to force the children of the RenderFlex to fit within the available space instead of being sized to their natural size.
This is considered an error condition because it indicates that there is content that cannot be seen. If the content is legitimately bigger than the available space, consider clipping it with a ClipRect widget before putting it in the flex, or using a scrollable container rather than a Flex, like a ListView.
The specific RenderFlex in question is: RenderFlex#7b505 OVERFLOWING
... needs compositing
... parentData: <none> (can use size)
... constraints: BoxConstraints(w=411.4, h=13.1)
... size: Size(411.4, 13.1)
... direction: vertical
... mainAxisAlignment: start
... mainAxisSize: min
... crossAxisAlignment: center
... verticalDirection: down
◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤
════════════════════════════════════════════════════════════════════════════════════════════════════
════════ (2) Exception caught by rendering library ═════════════════════════════════════════════════
A RenderFlex overflowed by 19 pixels on the bottom.
The relevant error-causing widget was:
Column file:///E:/arm%20dataset/flutter_ecommerce_template-m/lib/screens/main/components/recommended_list.dart:37:12
════════════════════════════════════════════════════════════════════════════════════════════════════
Please help me out.
Use ListView instead of Column should help.
Did you try using wrapping your Column with SingleChildScrollView widget like this?
SingleChildScrollView(
child: Column(
children: <Widget>[
Wrapping the Column widget with SingleChildScrollview should work.. Let me know if it worked for you..