Add infinte scroll loading data
toregua opened this issue · comments
Hi renefloor,
It is the first time i want to contributate on github and i don't find how to contribute.
I tried pull-request but it redirect me to comparing code.
So i have made my change in the code and i give you here the new feature :
side_header_list_view.dart
library side_header_list_view;
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
/**
* SideHeaderListView for Flutter
*
* Copyright (c) 2017 Rene Floor
*
* Released under BSD License.
*/
typedef bool HasSameHeader(int a, int b);
typedef void LoadMore();
class SideHeaderListView extends StatefulWidget {
final int itemCount;
final IndexedWidgetBuilder headerBuilder;
final IndexedWidgetBuilder itemBuilder;
final EdgeInsets padding;
final HasSameHeader hasSameHeader;
final LoadMore loadMore;
final itemExtend;
SideHeaderListView({
Key key,
this.itemCount,
@required this.itemExtend,
@required this.headerBuilder,
@required this.itemBuilder,
@required this.hasSameHeader,
this.padding,
this.loadMore,
})
: super(key: key);
@override
_SideHeaderListViewState createState() => new _SideHeaderListViewState();
}
class _SideHeaderListViewState extends State<SideHeaderListView> {
int currentPosition = 0;
@override
Widget build(BuildContext context) {
return new Stack(
children: <Widget>[
new Positioned(
child: new Opacity(
opacity: _shouldShowHeader(currentPosition) ? 0.0 : 1.0,
child: widget.headerBuilder(
context, currentPosition >= 0 ? currentPosition : 0),
),
top: 0.0,
left: 0.0,
),
new ListView.builder(
padding: widget.padding,
itemCount: widget.itemCount,
itemExtent: widget.itemExtend,
controller: _getScrollController(),
itemBuilder: (BuildContext context, int index) {
return new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new FittedBox(
child: new Opacity(
opacity: _shouldShowHeader(index) ? 1.0 : 0.0,
child: widget.headerBuilder(context, index),
),
),
new Expanded(child: widget.itemBuilder(context, index))
],
);
}),
],
);
}
bool _shouldShowHeader(int position) {
if (position < 0) {
return true;
}
if (position == 0 && currentPosition < 0) {
return true;
}
if (position != 0 &&
position != currentPosition &&
!widget.hasSameHeader(position, position - 1)) {
return true;
}
if (position != widget.itemCount - 1 &&
!widget.hasSameHeader(position, position + 1) &&
position == currentPosition) {
return true;
}
return false;
}
ScrollController _getScrollController() {
var controller = new ScrollController();
controller.addListener(() {
var pixels = controller.offset;
var newPosition = (pixels / widget.itemExtend).floor();
if (newPosition != currentPosition) {
setState(() {
currentPosition = newPosition;
});
}
});
if (widget.loadMore != null) {
controller.addListener(() {
var pixels = controller.offset;
var bottom = controller.position.maxScrollExtent;
if (pixels == bottom) {
widget.loadMore();
}
});
}
return controller;
}
}
CHANGELOG.md
[0.0.3] - 2017-12-27
Add LoadMore function in order to add inifinte scroll. When user arrives at the bottom of the screen, loadmore is call.
[0.0.2] - 2017-12-27
Bug fix release with two bug fixes:
- Overscroll on iOS resulted in an out of bounds exception
- Last item never showed a header, although it should have.
[0.0.1] - 2017-12-09
- First release of this awesome project :)
Hope you will add this feature 👍
Toregua
Hi @toregua
Thanks for the code. Can you also add some sample?
I assume you rebuild the widget (with setState) after you have loaded more items? Does the scroll position stays the same after you load? Do you have a progress indicator as last item?
I would add a check that it only calls 'LoadMore' once for a specific ScrollExtend. Otherwise it keeps calling loadMore when you scroll and I think it is better to catch that here than in 'LoadMore'.
Edit: I just thought it might be better to do it differently.
Infinite scroll is quite a specific feature and there are probably more features like that.
I am going to have a look at this, but I think it should be possible to catch a ScrollNotification in the Widget that uses the SideHeaderListView.
When you can get this in the outer widget all logic can be implemented on that level.
I just had a look and it works fine in the following way:
return new NotificationListener<UserScrollNotification>(
onNotification: onScrollNotification,
child: new SideHeaderListView(
itemCount: items.length,
itemExtend: 150.0,
headerBuilder: (BuildContext context, int index){
return new DayWidget(items[index].startDate);
},
itemBuilder: (BuildContext context, int index){
return new BattleListItem(items[index], key, index);
},
hasSameHeader: (int a, int b){
return items[a].startDate == items[b].startDate;
},
),
);
with
onScrollNotification(UserScrollNotification notification){
debugPrint("Scroll: ${notification.metrics.extentBefore}/${notification.metrics.maxScrollExtent}");
}
You can see all types of scrollnotifications here: https://docs.flutter.io/flutter/widgets/ScrollNotification-class.html
So for example you have ScrollStart, ScrollUpdate and ScrollEnd.
I can also imagine that you might want to load new items when the user still has some items left to scroll so it becomes more smooth.
I might add this feature later, but I don't think so. I suggest to use the method with the notifications.
These notifications are given by everything that scrolls, so you could also make a package that handles this with any type of scrollview.
Hi renefloor, you are right your solution is widely better because it totally decoupled from the component.
I juste tried your solution and it work perfectly.
Many thanks
Good luck with your project.
I would love to see the result. Do you plan to publish something in an app store or on github?