flutterdartbloc

flutter bloc - infinite scrolling - Listview scroll to top when new data added to state


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]));  
      }

    });
  }
}

Solution

  • It is likely due to during FetchUserEvent the state was temporarily UserLoadingState and the scroll position is lost, maybe you can try make isLoading a property of UserState. And still show the list (instead of Text('UserLoadingState')) if the it is loading and has data.

    Edit: My previous answer would cause multiple fetch so I removed the code.