Home > database >  emit another state when load more data with flutter_bloc
emit another state when load more data with flutter_bloc

Time:01-19

How to implement load more data with flutter_bloc without reload every time: I have this:

post_bloc.dart:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scroool/post_events.dart';
import 'package:scroool/post_repo.dart';
import 'package:scroool/post_states.dart';

class PostsBloc extends Bloc<PostEvents, PostsStates> {
  PostRepo repo;

  int page = 1;

  ScrollController controller = ScrollController();

  PostsBloc(this.repo) : super(PostInitState()){
    on<FetchPosts>((event, emit) async{
        emit(PostLoadingState());
        final posts = await repo.fetchPosts(page);
        emit(PostLoadedState(posts: posts));
    });

    on<LoadMore>((event, emit) async{
      if (controller.position.pixels ==
          controller.position.maxScrollExtent) {
       emit(LoadMoreState());
        page  ;
        final posts = await repo.fetchPosts(page);
        emit(PostLoadedState(posts: posts));

      //  isLoadingMore = false;
      } else {
        print("not called");
      }
    });
  }


}

And at home.dart:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:scroool/post_bloc.dart';
import 'package:scroool/post_events.dart';
import 'package:scroool/post_states.dart';

class Scroool extends StatelessWidget {
  List posts = [];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocConsumer<PostsBloc, PostsStates>(
        listener: (context, state){},
        builder: (context, state) {
          if(state is PostLoadingState) {
            return const Center(child: CircularProgressIndicator(),);
          } else if(state is PostLoadedState) {
            posts = posts   state.posts;
            return ListView.builder(
              controller: context.read<PostsBloc>().controller
                ..addListener(() => context.read<PostsBloc>().add(LoadMore())),
              itemCount: state is LoadMoreState
                  ? posts.length   1 : posts.length ,
              itemBuilder: (context, index) {
                if(index < posts.length) {
                  final post = posts[index];
                  return Card(
                      child: ListTile(
                        leading: CircleAvatar(child: Text("${index   1}"),),
                        title: Text(post['author'].toString()),
                        subtitle: Text(post['title']['rendered']),
                      )
                  );
                } else {
                  return Center(child: CircularProgressIndicator(),);
                }
              },
            );

          } else {
            return Container();
          }

        },
      )
    );
  }
}

It is working without any problem, but every time I need to load more is just re-emit the state and display data from beginning, I need just continue with more data not reload and add all data

CodePudding user response:

Change your BLoC state so the parent state will have property posts. You can set it to empty list for states that do not use it.

abstract class PostState {
  final List<Post> posts;

  PostState(this.posts);
}

class PostLoadingState extends PostState {
  LoadMoreState() : super ([]);
}

class LoadMoreState extends PostState {
  LoadMoreState({required List<Post> posts}) : super (posts);
}

class PostLoadedState extends PostState {
  PostLoadedState({required List<Post> posts}) : super (posts);
}

Then change your BLoC accordingly:

on<LoadMore>((event, emit) async{
  if (controller.position.pixels ==
      controller.position.maxScrollExtent) {
    emit(LoadMoreState(posts: state.posts));
    page  ;
    final posts = await repo.fetchPosts(page);
    emit(PostLoadedState(posts: [...state.posts, ...posts]));
  } else {
    print("not called");
  }
});

In your widget remove the posts variable and use posts from bloc's state. Since your parent state has property posts, you can access posts for every state, including LoadMoreState. So change the if condition to return ListView for all states except PostLoadingState.

class Scroool extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: BlocConsumer<PostsBloc, PostsStates>(
          listener: (context, state){},
          builder: (context, state) {
            if(state is PostLoadingState) {
              return const Center(child: CircularProgressIndicator(),);
            } else {
              final posts = state.posts;

              return ListView.builder(
                controller: context.read<PostsBloc>().controller
                  ..addListener(() => context.read<PostsBloc>().add(LoadMore())),
                itemCount: state is LoadMoreState
                    ? posts.length   1 : posts.length ,
                itemBuilder: (context, index) {
                  if(index < posts.length) {
                    final post = posts[index];
                    return Card(
                        child: ListTile(
                          leading: CircleAvatar(child: Text("${index   1}"),),
                          title: Text(post['author'].toString()),
                          subtitle: Text(post['title']['rendered']),
                        )
                    );
                  } else {
                    return Center(child: CircularProgressIndicator(),);
                  }
                },
              );
            }
          },
        )
    );
  }
}
  • Related