I'm developing a new Flutter app on Android Studio. One page of my app gets the information from a Future. With the future's result I'm creating a ListView.builder():
Here's the code:
Widget encabezados(BuildContext context, AsyncSnapshot snapshot)
{
ListView(
children: <Widget>[
new Card(
elevation: 3,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(width: 0.0,),
Category(
backgroundColor: Colors.deepOrange,
val: clientesacobrar.toString(),
title: "Clientes a cobrar",
),
SizedBox(width: 10.0,),
Category(
backgroundColor: Colors.blue,
title: "Clientes cobrados",
val: clientescobrados.toString(),
),
SizedBox(width: 10.0,),
Category(
val: formatter.format(montoacobrar),
//val: records.length.toString(),
backgroundColor: Colors.green,
title: "Monto a cobrar",
)
],
),
),
new ListView(
shrinkWrap: true,
scrollDirection: Axis.vertical,
//elevation: 3,
children: <Widget>[
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
//ShrinkWrappingViewport: true,
itemCount: records.length,
itemBuilder: (BuildContext context, int index) {
return records.isNotEmpty
? Card(
child: Padding (
padding: const EdgeInsets.all(16.00),
child: Text(records[index].nombre),
)
)
: CircularProgressIndicator();
}
)
],
)
],
);
}
As you can see in the listviews widgets I'm using shrinkWrap set to true, because if I don't set this I get:
I/flutter (22634): The following assertion was thrown during performResize():
I/flutter (22634): Vertical viewport was given unbounded height.
I/flutter (22634): Viewports expand in the scrolling direction to fill their container.In this case, a vertical
I/flutter (22634): viewport was given an unlimited amount of vertical space in which to expand. This situation
I/flutter (22634): typically happens when a scrollable widget is nested inside another scrollable widget.
I/flutter (22634): If this widget is always nested in a scrollable widget there is no need to use a viewport because
I/flutter (22634): there will always be enough vertical space for the children. In this case, consider using a Column
I/flutter (22634): instead. Otherwise, consider using the "shrinkWrap" property (or a ShrinkWrappingViewport) to size
I/flutter (22634): the height of the viewport to the sum of the heights of its children.
But if I set the shrinkwrap property I'm not able to scroll the page on my app
I have tried putting the ListView.builder() into:
container widget , column widget, ListView widget but in the ListView.builder() I'm always asked to set the shrinkwrap property.
You need to constrain your parent widget so it knows where the bounds are.
For that scenario, wrapping in a Container alone, won’t have any effect unless you explicit constrain it (eg. by setting constrains or just giving it a explicit height).
Container(
height: 200.0, // or whatever
child: ListView(
....
)
)
As an alternative, you can also set your inner ListView physics to NeverScrollableScrollPhysics() which will prevent any scroll from happening in the inner one and only the top scroll view will control scroll.
If all of that doesn't help, you may need to use a CustomScrollView which lets you nest multiple scroll views, basically a Sliver.
Try to add physics: NeverScrollableScrollPhysics() to inner ListView.
Miguel Ruivo's answer is correct but rather than using a Container with an explicit height, you can instead wrap the ListView with Expanded to give it the remaining height and allow it to scroll if needed.
Note: Make sure the Expanded widget is a descendant of Row, Column, or Flex.
Expanded(
child: ListView(
...
)
)
I had the same issue and found out that the main thing is using the physics: NeverScrollableScrollPhysics property inside of your ListView.Builder() like so:
body: Column(
children: [
_buildHeader(),
Container(
// height: 40,
child: ListView.builder(
physics: NeverScrollableScrollPhysics(), // <= This was the key for me
shrinkWrap: true,
itemCount: 1,
itemBuilder: (_, i) {
return _horizontalListView();
},
),
),
],
),
I tested and It works with Column, Container, Expanded widgets as a parent widget.
So something that seems odd is the fact that you have a whole heap of nested ListView's. I suggest try limiting it to just one.
It's a little difficult to see what the intended outcome should be... Is the "Category card" meant to be static with the ListView.builder part scrolling underneath? Or does the "Category card" scroll with the content?
Either way, I think I've got something that you should be able to use.
Included are ways of accomplishing either the static or scrolling head.
A note on the static header solution: I'm using a Column instead of a ListView This ensures we don't have top level scrolling going on. First child is your "header" with the second child the ListView responsible for the scrolling. This ListView is wrapped in Expanded to make sure the scrolling ListView takes up all of the remaining screen height.
Hope this helps
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SO Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sample Code'),
),
body: _buildViewWithStaticCategories());
// OR
// body: _buildViewWithScrollingCategories());
}
Widget _buildViewWithStaticCategories() {
return Column(
children: <Widget>[
_buildCategoriesCard(),
Expanded(
child: ListView.builder(
itemCount: 20, // records.length
itemBuilder: (BuildContext context, int index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.00),
child: Text(index.toString()),
),
);
}),
),
],
);
}
Widget _buildViewWithScrollingCategories() {
return ListView.builder(
itemCount: 21, // records.length + 1 for the Categories card
itemBuilder: (BuildContext context, int index) {
return index == 0
? _buildCategoriesCard()
: Card(
child: Padding(
padding: const EdgeInsets.all(16.00),
child: Text(index.toString()),
),
);
},
);
}
Widget _buildCategoriesCard() {
return Card(
elevation: 3,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('one'), // Category( ...
SizedBox(width: 10.0),
Text('two'), // Category( ...
SizedBox(width: 10.0),
Text('three'), // Category( ...
],
),
);
}
}
Related
I have already gone through this post
for nested scrolling but it is not the correct way according to me as explained in this video from the official flutter channel
I want to achieve the below layout
The list header like Claim requested credentials,Claim received credentials,Pending Requests etc are dynamic and will be coming from backend. Also each item in those list header like Module 1: Designing Financial Services are also dynamic
So I need list within a list
I am using a CustomScroll but I am not able to achieve the inner list view
I am looking for a lazy list option and not just mapping the inner list over a column or a list as the inner list might contain 100 items
Here is what I have achieved
Here is the sample code
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
HeaderGradient(),
Positioned(
top: 110,
left: 0,
right: 0,
bottom: 0,
child: Container(
padding: const EdgeInsets.all(8.0),
decoration: const BoxDecoration(
color: grayColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
child: CustomScrollView(
slivers: [
const SliverToBoxAdapter(
child: ManageCredentialHeader(),
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return ManageCredentialCard();
}, childCount: 10))
],
),
),
)
],
),
);
}
}
and
class ManageCredentialCard extends StatelessWidget {
const ManageCredentialCard({super.key});
#override
Widget build(BuildContext context) {
return Card(
color: Theme.of(context).colorScheme.background,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Column(
children: [
const ManageCredentialCardHeader(),
const ManageCredentialCardItem()
],
),
),
);
}
}
ManageCredentialCardItem is the inner list
As soon as I wrap ManageCredentialCardItem inside a ListView.builder I get error saying
RenderFlex children have non-zero flex but incoming height constraints are
unbounded.
When a column is in a parent that does not provide a finite height constraint,
for example if it is
in a vertical scrollable, it will try to shrink-wrap its children along the
vertical axis. Setting a
flex on a child (e.g. using Expanded) indicates that the child is to expand to
fill the remaining
space in the vertical direction.
Check the sample repo to check what I have tried and full source code
I was referring generating everything on sliver like
child: CustomScrollView(
slivers: [
const SliverToBoxAdapter(
child: ManageCredentialHeader(),
),
for (int i = 0; i < 10; i++)
SliverPadding(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
childCount: 10 + 1, //extend by 1 for header
(context, index) {
return index == 0
? const ManageCredentialCardHeader()
: const ManageCredentialCardItem();
}),
),
)
],
),
Also you can create custom-sliver for children.
To solve the ‘ListView.builder’ problem, I suggest to wrap it into an ’Expanded’ and set the ’shrinkWrap’ property of the ’ListView’ to true.
As for the list inside a list problem I’m not sure I understand correctly, but setting the ’primary’ property of the inner List to false should make it so it follows the scroll of the general list and avoids scrolling internally.
It might be needed to also set the ’shrinkWrap’ to true since it has no defined height, making it not lazy, but I’d try also wrapping it with ’Expanded’ as it might have the same result keeping it lazy
As #Yeasin Sheikh suggested, you dont need to use Listview.builder inside ManageCredentialCardItem because the parent of ManageCredentialCardItem is handling the scroll i.e
SliverList( // 👈 This will handle scroll
delegate: SliverChildBuilderDelegate((context, index) {
return ManageCredentialCard(); //👈 So you can use column instead
},childCount: 10))
If you have useCase where you have to use ListView.builder for fetching the results from remote source, you can use shrinkWrap: true prop inside ListView.builder
I'm not really sure about what effect you want to achieve, but :
If your goal is to display the full list of items in your ManageCredentialCard like this:
you just need to update the ManageCredentialCard widget with the following
class ManageCredentialCard extends StatelessWidget {
const ManageCredentialCard({super.key});
#override
Widget build(BuildContext context) {
return Card(
color: Theme.of(context).colorScheme.background,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Column(
children: [
const ManageCredentialCardHeader(),
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return const ManageCredentialCardItem();
},
itemCount: 20,
)
// const ManageCredentialCardItem()
],
),
),
);
}
}
BUT
If your goal is to limit the size of your Cards, and be able to scroll the Items list like this
you will have to define a height limit to your Cards with a SizeBox.
class ManageCredentialCard extends StatelessWidget {
const ManageCredentialCard({super.key});
#override
Widget build(BuildContext context) {
return Card(
color: Theme.of(context).colorScheme.background,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Column(
children: [
const ManageCredentialCardHeader(),
SizedBox(
height: 150,
child: ListView.builder(
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
itemBuilder: (context, index) {
return const ManageCredentialCardItem();
},
itemCount: 10,
),
)
// const ManageCredentialCardItem()
],
),
),
);
}
}
The only thing you'll have to do then would be to scroll the main Listview when reaching the end of one of the inner-lists.
To do so, you can use the NotificationListener widget as explained in this post :
Flutter : ListView : Scroll parent ListView when child ListView reach bottom - ClampingScrollPhysics not working in sized container
Use these below codes in ListView, SingleChildScrollView, ListView.builder() or any scroll view widgets.
shrinkWrap: true,
physics: NeverScrollablePhysics(),
For more, please go through this article.
I want to build a scrollable widget that consists of some Text widgets (and maybe some other widgets) that are produced by a function. However, I'm getting the following error:
A RenderFlex overflowed by 701 pixels on the bottom.
I'm not sure what the best layout is here. Should the scrollable Column be placed outside the container? Is a Column at the innermost nesting required? Is another Expanded widget required?
Also, the Text inside the widget list should be aligned left, not centereed.
If someone could help with a useful solution, I'd be very thankful!
Here's my current code:
List<Widget> getWidgetList(){
// do some stuff and return a list
return [Text("foo"), Text("bar")];
}
#override
Widget build(BuildContext context) => Scaffold(... body:
Container(
padding: EdgeInsets.all(20),
child:
SingleChildScrollView(
scrollDirection: Axis.vertical,
child:
SizedBox(
height: MediaQuery.of(context).size.height,
child:
Column(
children: getWidgetList()
)
)
)
)
);
}
I think the overflow happens from getWidgetList. Remove the fixed height from SizedBox,
You can use ListView, it provide padding params,
ListView(
padding: EdgeInsets.all(20),
children: [
...getWidgetList(),
],
),
Old
Container(
padding: EdgeInsets.all(20),
child:
SingleChildScrollView(
scrollDirection: Axis.vertical,
child:
SizedBox(
// height: MediaQuery.of(context).size.height,
child:
Column(
children: getWidgetList()
)
)
)
)
);
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: <Widget>[
_firstListView(),
_secondListView(),
],
),
);
}
Main class that calls other listView builders
Widget _firstListView() {
return Container(
color: Colors.green,
child: ListView.builder(
itemCount: 200,
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
itemBuilder: (context, index) => RecCell(
index: index,
),
),
);
}
first listView builder
Widget _secondListView() {
return Container(
color: Colors.red,
child: ListView.builder(
itemCount: 200,
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
itemBuilder: (context, index) => ListTile(title: Text("Second $index")),
),
);
}
the second listView builder
RecCell is another statefull widget that prints in init method when the widget is build and it build 200 times on restart and never recreated on scrolling
I want them to reuse each time parent listview scrolls any help will appreciated..
Well, wrapping them in a ListView removes the lazy loading benefit of ListView.builder, because the outer ListView wants to load all children at once. I think the best way is the combine the data of the two ListView.builders into a single one. Although, wrapping them in a colored container would not be possible then, but maybe you get the desired result by wrapping the items in it. So something like:
Widget builds(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: 400,
shrinkWrap: true,
itemBuilder: (context, index) => index < 200
? Container(
color: Colors.green,
child: RecCell(
index: index,
),
)
: Container(
color: Colors.red,
child: ListTile(title: Text("Second ${index - 200}")))));
}
You could use NestedScrollView if you have nested listview in the body of the parent widget.
try this
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: <Widget>[
Container(height: 300, child: _firstListView()),
Container(height: 300, child: _secondListView()),
],
),
);
}
-----------OR------------
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
children: <Widget>[
Container(height: 300, child: _firstListView()),
Container(height: 300, child: _secondListView()),
],
),
);
}
Use CustomScrollView instead ListViews for this case
CustomScrollView(
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => Container(),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => Container(),
),
),
],
);
because the ListView requires all available space and expands its own size as much as possible to understand how many child elements can be displayed
I did this and worked for me
Map<String, dynamic> d = {
0:..,listView widget,
1:[...list of widgets]
}
I am making a map containing all my widgets, and in the build method I am returning a listview with those widgets
return ListView(
children:[
d[0],
...d[1] // this will extract each widget from the list and insert it to list view
]
)
In this way my d[0] is horizontal listview builder and d1 are other widget that I am adding into parent list view directly now they all are rerendering when visible. Had to do some changing in my previous code
Final output:
output
I am trying to have a ListView with one single scroll. I want to be able to scroll down and see every widget. Currently, I have multiple widgets before a GridView. The problem with this is that the GridView has its own scroll. A good example of what I want is similar to the Instagram profile page. When you scroll down in the Instagram profile page, every element scrolls down. There are no nested scrolls.
This is my current code:
body: ListView(
children: <Widget>[
...
...
Container(
child: GridView.count(
crossAxisCount: 3,
shrinkWrap: true,
children: List.generate(100, (index) {
return Container(
alignment: Alignment.center,
margin: EdgeInsets.all(3.0),
height: MediaQuery.of(context).size.height,
child: FadeInImage.memoryNetwork(
placeholder: kTransparentImage,
image:'https://picsum.photos/${300}/${300}/',
),
);
}),
),
)
]
)
There's Nested Scrolled Widgets, the CustomScrollView which takes Slivers to achieve what you're looking for. SliverGrid and SliverList are the ones you need to achieve your goal:
Widget sliverScroll() {
return CustomScrollView(
slivers: <Widget>[
SliverGrid(
delegate: SliverChildBuilderDelegate((context, index) {
return Container();
}, childCount: 9),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return Container();
}, childCount: 6),
),
],
);
}
You can customize as you wish, changing the order or it, item count, etc
I want to add a transparent appBar over a PageView.
The Pageview is implemented using Stacks in Flutter, So that all the content is placed over an image. Since it is different than how Tabs work, I need to place an appBar such that even during Scrolling b/w multiple pageViews, the appBar remains Fixed at given position.
I am using following Code to achieve this, but it is showing various kind of errors.
import 'package:flutter/material.dart';
import 'package:page_transformer/data.dart';
import 'package:page_transformer/intro_page_item.dart';
import 'package:page_transformer/page_transformer.dart';
class IntroPageView extends StatelessWidget{
#override
Widget build(BuildContext context) {
List<Widget> children = new List();
return
Stack(
fit: StackFit.expand,
children: [
_buildBackground(),
_buildforeground()
],
);
}
Widget _buildforeground() => new AppBar(
title: Text('Antara',
textDirection: TextDirection.rtl,),
);
Widget _buildBackground() => PageTransformer(
pageViewBuilder: (context, visibilityResolver) {
return PageView.builder(
controller: PageController(viewportFraction: 1.0),
itemCount: sampleItems.length,
itemBuilder: (context, index) {
final item = sampleItems[index];
final pageVisibility =
visibilityResolver.resolvePageVisibility(index);
return IntroPageItem(
item: item,
pageVisibility: pageVisibility,
);
},
);
},
);
}
Please tell me where i am wrong, Also, i forgot to mention that the pageviews are stateful widgets, and the PageTransformer widget is used to give parallax effect while scrolling b/w pageviews.
Thank You
You should apply two changes to your code to force the height
Remove the StackFit.expand because it makes the bar to occupy the whole screen
Wrap the AppBar with a Container or SizedBox and provide a height to constrain it
And finally set the transparent color and elevation to 0.0
#override
Widget build(BuildContext context) {
List<Widget> children = new List();
return Stack(
//fit: StackFit.expand,
children: [
_buildBackground(),
_buildforeground(),
],
);
}
Widget _buildforeground() => SizedBox(
height: 80.0,
child: new AppBar(
backgroundColor: Colors.transparent,
elevation: 0.0,
title: Text(
'Antara',
textDirection: TextDirection.rtl,
style: TextStyle(color: Colors.grey),
),
),
);