I am trying to implement an infinite scroll list view, My data loads to liveView but everytime new data comes, listview scrolls to top, I want to avoid scroll to top when new data comes in
import 'package:bloc_infinite_scroll/bloc/user_bloc.dart';
import 'package:bloc_infinite_scroll/bloc/user_event.dart';
import 'package:bloc_infinite_scroll/bloc/user_state.dart';
import 'package:bloc_infinite_scroll/data/models/user_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final ScrollController _scrollController = ScrollController();
List<UserData> users = [];
@override
void initState() {
super.initState();
context.read<UserBloc>().add(FetchUserEvent());
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
context.read<UserBloc>().add(FetchUserEvent());
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Infinite Scroll'),
),
body: BlocBuilder<UserBloc, UserState>(
builder: (context, state) {
if (state is UserInitialState) {
return const Center(
child: Text('UserInitialState'),
);
}
if (state is UserLoadingState) {
return const Center(
child: Text('UserLoadingState'),
);
}
if (state is UserLoadedState) {
return Center(
child: ListView.builder(
controller: _scrollController,
itemCount: state.users.length + 1,
itemBuilder: (context, index) {
if (index >= state.users.length) {
return const Center(
child: CircularProgressIndicator(),
);
}
return ListTile(
title: Text(state.users[index].name.toString()),
);
},
),
);
}
return const Text('Users list');
},
),
);
}
}
here is my user_bloc.dart file
import 'package:bloc_infinite_scroll/bloc/user_event.dart';
import 'package:bloc_infinite_scroll/bloc/user_state.dart';
import 'package:bloc_infinite_scroll/data/models/user_model.dart';
import 'package:bloc_infinite_scroll/data/repositories/user_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class UserBloc extends Bloc<UserEvent, UserState> {
UserRepository userRepository;
UserBloc(this.userRepository) : super(UserInitialState()) {
on<FetchUserEvent>((event, emit) async {
emit(UserLoadingState());
final List<UserData> users = await userRepository.getUsers();
if(state is UserLoadingState){
emit(UserLoadedState(users: users));
}
if(state is UserLoadedState){
emit(UserLoadedState(users: [...(state as UserLoadedState).users,...users]));
}
});
}
}
2
Answers
It is likely due to during
FetchUserEvent
the state was temporarilyUserLoadingState
and the scroll position is lost, maybe you can trymake
isLoading
a property ofUserState
. And still show the list (instead ofText('UserLoadingState')
) if the it is loading and has data.Edit: My previous answer would cause multiple fetch so I removed the code.
The issue lies in the emission of the
UserLoadingState
state in bloc event handler, which causes theBlocBuilder
to rebuild theListView
, therefore losing the scroll position. To fix the issue, you need to either replace the loading state with something else, for example, a boolean flag or another state class that doesn’t cause the widget tree to rebuild.You also have small issues in the provided code sample:
state
is not checked to beUserLoadingState
. This can cause multiple calls toUserRepository.getUsers
in a single tree rebuild;ScrollController
and reliance onposition.maxScrollExtent
will work incorrectly if the first page of users ends up taking less than vertical viewport space. The scroll view will not have an overflow available for you to see inposition.pixels
and the next page will not be requested, i.e. no scrolling available = no ability to load new pages on scroll. Instead, you can check the item index position in the list builder callback and request data loads from there;CustomScrollView
andSliverList
are more performant in general.Here is a code sample with your original question and additional issues fixed. Note that I am using the freezed code generation package to generate the
UserState
class withcopyWith
method. Alternatively, you can use copy_with_extension_gen to generate thecopyWith
method.user_state.dart
:user_event.dart
:user_bloc.dart
:main.dart
:Here’s a gif of the code sample: demo.