Flutter. Widgets above listview.builder to scroll along with other content? - flutter

I'd like to add an info box on top of the listview and instead of having it fixed, I'd like it to scroll along with the content while the page is scrolled down. Instead what I'm experiencing is that it stays always in fixed position no matter what I try to do.
My code:
return new Column(children: <Widget>[
new Text('I want to be scrollable as well with other content as well'), // I want widget or widgets here to be part of the scroll instead of them being fixed.
new Expanded(
child: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return index >= state.posts.length
? BottomLoader()
: PostWidget(post: state.posts[index]);
},
itemCount: state.hasReachedMax
? state.posts.length
: state.posts.length + 1,
controller: _scrollController,
),
),
]);
Is this possible to achieve?

I think you could return your Text widget or any other widget at the 0 position ?
return new Column(children: <Widget>[
new Text('I want to be scrollable as well with other content as well'), // I want widget or widgets here to be part of the scroll instead of them being fixed.
new Expanded(
child: ListView.builder(
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
return Text('Your widget');
else {
return index >= state.posts.length
? BottomLoader()
: PostWidget(post: state.posts[index]);
}
},
itemCount: state.hasReachedMax
? state.posts.length
: state.posts.length + 1,
controller: _scrollController,
),
),
]);

Related

How to disable Listview Stretching on overscrolling

I don't know if stretching is the correct word to describe it.
But it looks like this.
"last message 👋👋" is the last message in this convo and the empty space below is the 'stretching'. Once I release the touch from my screen it goes back to normal. I want to disable that 'stretching' so that nothing happens when the user overscrolls.
ListView code:
ListView.builder(
controller: _controller1,
physics: BouncingScrollPhysics(),
itemCount: snapshot.data!.docs.length, //snapshot from parent StreamBuilder
itemBuilder: (context, index) {
final allowedOrNot = index == 0
? true
: compareDate( // Userdefined function to check if the dates above messages should be displayed or not
currentSnap: snapshot.data!.docs[index]
.data()['serverTimeStamp'],
previousSnap: snapshot.data!.docs[index - 1]
.data()['serverTimeStamp']);
return Column(
children: [
allowedOrNot
? chatDater( // the date widget (above some messages to indicate the date of conversation)
data: dataForChatDater( // Userdefined function which returns Strings like 'Yesterday', 'Today' for the widget to display
snap: snapshot.data!.docs[index]
.data()['serverTimeStamp']),
)
: const SizedBox(
width: 0,
height: 0,
),
Align(
alignment:
snapshot.data!.docs[index].data()['sender'] ==
widget.usernameOfFriend
? Alignment.centerLeft
: Alignment.centerRight,
child: MessageBubble( // The ChatBubble widget used to display the messages
timestamp: snapshot.data!.docs[index]
.data()['serverTimeStamp'],
message: snapshot.data!.docs[index].data()['message'],
IsYou: snapshot.data!.docs[index].data()['sender'] ==
widget.usernameOfFriend
? false
: true,
),
),
],
);
},
),
It is natural behavior of BouncingScrollPhysics.
If you don't want to overscroll, you can change it to NeverScrollableScrollPhysics.
Try shrinkWrap:true in your ListView.builder and mainAxisSize:MainAxisSize.min in Column as follows:
ListView.builder(
//other
shrinkWrap: true,
itemBuilder: (context, index) {
return Column(
mainAxisSize: MainAxisSize.min, //use this
children: [
//your children
],
);
})

Problem in listview.builder with multiple widgets

I have a list of items and based on a condition it should be displayed in Widget1() or Widget2(). It works fine up to there, the problem is when I get to the bottom of the list and want to scroll slowly up, the items rebuild and it jumps up and down the list.
Using the itemExent does solve the problem, but it's not the ideal solution. I need dynamic items.
Note: Widgets do not have a default size.
ListView.builder(
controller: scrollController,
cacheExtent: 10000,
addAutomaticKeepAlives: true,
dragStartBehavior: DragStartBehavior.start,
physics: AlwaysScrollableScrollPhysics(),
//itemExtent: 450.0,
itemBuilder: (context, index) {
if (index < list.length)
return MultiBlocProvider(
providers: [
/// some mandatory providers
],
child: some condition
? Widget1()
: Widget2());
return loading();
},
itemCount: list.length
);

When adding ListView builder , the entire screen is goes blank

when I try to add a list view builder, my entire screen goes black
here is the list view builder code
ListView.builder(
shrinkWrap = true,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
final data = courseCardList[index];
return Row(
children: [
CourseList(
color: data.color,
icon: data.icon,
caption: data.caption,
head: data.head,
timePeriod: data.timePeriod,
rating: data.rating,
),
],
);
},
),
when I build the UI, that shows a blank screen, what mistake am I making here?
Horizontal Listview.builder needs a fixed height. Horizontal list item shouldn't have a endless height. so solution came to my mind just wrap Listview.builder with a conatiner and set height of that container like below.
The Flutter framework can only know the height of a widget once it's been built.
If you're building ListView children dynamically, it can't calculate the required height of the ListView until all it's children have been built, which might never happen (infinite ListView).
You can either give the ListView a fixed height and build its children dynamically or have the ListView's height depend on it's children.
Container(
height: 200,
child: ListView.builder(
shrinkWrap = true,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
final data = courseCardList[index];
return Row(
children: [
CourseList(
color: data.color,
icon: data.icon,
caption: data.caption,
head: data.head,
timePeriod: data.timePeriod,
rating: data.rating,
),
],
);
},
),
);
Under ListView Widget
use shrinkWrap: true
add itemCount, itemCount: courseCardList.length
If it still doesn't solve the issue,
Remove the Row() widget as I can see you are only returning a single widget under it, so directly return that widget CourseList()
The code should look something like this.
ListView.builder(
shrinkWrap : true,
itemCount: courseCardList.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
final data = courseCardList[index];
return CourseList(
color: data.color,
icon: data.icon,
caption: data.caption,
head: data.head,
timePeriod: data.timePeriod,
rating: data.rating,
);
},
),
I think you forgot the itemCount
Expanded(
child:ListView.builder(
shrinkWrap = true,
itemCount = 5,// add this line or put courseCardList.length
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
final data = courseCardList[index];
return Row(
children: [
CourseList(
color: data.color,
icon: data.icon,
caption: data.caption,
head: data.head,
timePeriod: data.timePeriod,
rating: data.rating,
),
],
);
},
),
),
Refer ListView and Long List
provide itemCount to your ListviewBuilder and also wrap it with Expanded or some fixed size widget like container like:
Expanded(
child: ListView.builder(
itemCount: your counts(int)
.....
.....
)
)

programmatically prevent scroll listview flutter

I have nested ListView in a ListView.
So I want to disable scroll for child ListView programmatically.
How to do it.
Thanks,
You can prevent it by using physics: NeverScrollableScrollPhysics() for example
Expanded(
child: ListView.builder(
physics: NeverScrollableScrollPhysics(), <-----
scrollDirection: Axis.horizontal,
itemCount: status == false ? list.length : 5,
itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
value: list[i], child: ListItem()),
),
If you want to set it programatically, use a setState() to update the physics type like this:
ScrollPhysics physics = AlwaysScrollableScrollPhysics();
return Stack(
children: [
ListView(
physics: physics,
),
RaisedButton(
child: Text("Disable scrolling"),
onPressed: () {
setState((){
if(physics is AlwaysScrollableScrollPhysics){
physics = NeverScrollableScrollPhysics();
} else {
physics = AlwaysScrollableScrollPhysics();
}
});
},
),
]
);
You need to set this "physics" property value "NeverScrollableScrollPhysics" inside the nested listview.
example:
ListView.builder(
physics: NeverScrollableScrollPhysics(),
itemBuilder: () {}
),

Refresh indicator doesn't work when list doesn't fill the whole page

I have a page the has a list with refresh indicator. When I have many elements in the list (filling the whole view and more, i.e. I can scroll), the refresh indicator works.
However, when I have only one or two elements in the list (nothing to scroll), then the refresh indicator doesn't work. Is this the expected behaviour? Is there a way to get the indicator to work also for short lists?
I tried wrapping the list view (child of refresh indicator) with a scroll bar but the problem still persists.
Future<void> _onRefresh() async {
_getPeople();
}
#override
Widget build(BuildContext context) {
super.build(context);
return new Container(
child: _items.length == 0
? Center(child: CircularProgressIndicator())
: new RefreshIndicator(
onRefresh: _onRefresh,
child: Scrollbar(
child: new ListView.builder(
controller: controller,
itemBuilder: (context, index) {
return PeopleCard(
People: _items[index],
source: 2,
);
},
itemCount: _items.length,
),
)),
);
}
If the length of the items list is 1 or 2 or any amount that doesn't need scrolling, refresh indicator should also work.
Set the physics to AlwaysScrollableScrollPhysics just like the below code.
new RefreshIndicator(
key: _refreshIndicatorKey,
color: Colors.blue,
onRefresh: (){
setState(() {
_future = fetchPosts(http.Client());
_getAvalailableSlots();
});
},
child: ListView.builder(
physics: AlwaysScrollableScrollPhysics(),
itemCount: data.length,
itemBuilder: (context, i) {
//do something
}
)
)
Case 1:
Use AlwaysScrollableScrollPhysics() in your Listview or SingleChildScrollView.
physics: const AlwaysScrollableScrollPhysics(),
Case 2:
If you want to use other scroll physics, use this way...
physics: BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics(),
),
Case 3:
Make sure you didn't make shrinkWrap, true. ShrinkWrap has to be false like default.
shrinkWrap: true. ❌
shrinkWrap: false. ✅
shrinkWrap: false,
Case 4:
If you have any custom widgets other than a listview, use stack and listview this way...
RefreshIndicator(
onRefresh: () async {
print('refreshing');
},
Stack(
children: [
YourWidget(),
ListView(
physics: const AlwaysScrollableScrollPhysics(),
),
],
),
),
To use with BouncingScrollPhysics:
RefreshIndicator(
onRefresh: () async {
},
child: ListView.builder(
physics: BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
itemBuilder: (context, index) {
return Text("${index}");
}),
To make RefreshIndicator always appear, set physics: const AlwaysScrollableScrollPhysics() for the scrollable child like below.
ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: <Widget>[widget.bodyWidget]
)
If you still have some errors, the below might help.
ListView widget needs to have height value to be "always" scrollable vertically. So wrap it with SizedBox or some other proper widget and set the height.
How to fix the height? Get the height of viewport using MediaQuery.of(context).size.height and, if needed, subtract the value of non-scrollable part.
If you use SingleChildScrollView and it's not working with AlwaysScrollableScrollPhysics(), it is because the child doesn't have height. fix the child's height or just use ListView.
To sum up, the following worked for me.
sizes.bottomNavigationBar and sizes.appBar are the constant variables I made to customize the appbar and navbar.
RefreshIndicator(
onRefresh: widget.onRefresh,
child: SizedBox(
height: MediaQuery.of(context).size.height -
sizes.bottomNavigationBar -
sizes.appBar,
child: ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: <Widget>[widget.bodyWidget]
)),
)
If you're finding any reliability, Refresh indicator does not show up part of the official doc would help.
If you're using ListView inside of Column with Expanded.
Try Removing
shrinkWrap:true