I have a simple infinite scroll which loads items from an API. My expectation after load more items, the list if updated with new items and my view port will not be resetted.
Here is my page
class ArticleListPage extends StatelessWidget {
final _scrollController = ScrollController();
final Color color = Colors.black;
ArticleListBloc _articleListBloc;
@override
Widget build(BuildContext context) {
_articleListBloc = BlocProvider.of<ArticleListBloc>(context);
return Scaffold(
backgroundColor: color,
body: BlocBuilder<ArticleListBloc, ArticleListState>(
builder: (context, ArticleListState state) {
if (state.searching) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.white,
),
);
} else {
return NotificationListener<ScrollNotification>(
onNotification: _handleScrollNotification,
child: ListView.builder(
shrinkWrap: true,
itemCount: calculateListItemCount(state),
controller: _scrollController,
physics: AlwaysScrollableScrollPhysics(),
itemBuilder: (context, index) {
return index >= state.listItems.length
? Center(
child: CircularProgressIndicator(),
)
: ArticleListViewItem(article: state.listItems[index]);
},
),
);
}
},
),
);
}
bool _handleScrollNotification(ScrollNotification notification) {
if (notification is ScrollEndNotification &&
_scrollController.position.extentAfter == 0) {
_articleListBloc.add(GetArticles());
}
return false;
}
int calculateListItemCount(ArticleListState state) {
if (state.hasReachedEndOfResults) {
return state.listItems.length;
} else {
return state.listItems.length + 1;
}
}
}
And my bloc
class ArticleListBloc extends Bloc<ArticleListEvent, ArticleListState> {
final ArticleDataSource dataSource;
ArticleListBloc(this.dataSource) : super(ArticleListState.initial());
@override
Stream<ArticleListState> mapEventToState(ArticleListEvent event) async* {
String searchKeyword = state.searchKeyword;
int pageNumber = state.pageNumber;
bool hasReachEndResult = state.hasReachedEndOfResults;
BuiltList<Article> currentArticles = state.listItems;
if (event is SetKeyword) {
searchKeyword = event.keyword;
pageNumber = 0;
currentArticles = new BuiltList<Article>();
hasReachEndResult = false;
}
if (!hasReachEndResult) {
yield state.rebuild((b) => b..searching = true);
final articles = await dataSource.getArticles(pageNumber, searchKeyword);
if (articles.isEmpty) {
hasReachEndResult = true;
} else {
hasReachEndResult = false;
}
yield ArticleListState.success(currentArticles + articles, pageNumber + 1,
searchKeyword, hasReachEndResult);
}
}
}
Somehow after searching (load more), I can see the first item is redrawn again and I am forced to scroll down again despite the items are correctly added into the list.
Do I need some kind of skip variable to go to the first newly added item ? What else am I missing here ?