Как реализовать загрузку большего количества данных с помощью flutter_bloc без перезагрузки каждый раз: у меня есть это:
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");
}
});
}
}
А дома.дарт:
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();
}
},
)
);
}
}
Он работает без каких-либо проблем, но каждый раз, когда мне нужно загрузить больше, просто повторно излучайте состояние и отображайте данные с самого начала, мне нужно просто продолжить с дополнительными данными, а не перезагружать и добавлять все данные
Измените состояние BLoC, чтобы родительское состояние имело свойство posts
. Вы можете установить его в пустой список для состояний, которые его не используют.
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);
}
Затем измените свой BLoC соответствующим образом:
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");
}
});
В вашем виджете удалите переменную posts
и используйте posts
из состояния блока. Поскольку у вашего родительского состояния есть свойство posts
, вы можете получить доступ к posts
для каждого состояния, включая LoadMoreState
. Поэтому измените условие if, чтобы оно возвращало ListView
для всех состояний, кроме 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(),);
}
},
);
}
},
)
);
}
}
@OsamaMohammed Я отредактировал ответ, включив в него код виджета, а также немного отредактировал код PostBloc
. Я не могу запустить код и протестировать его, поэтому могут быть ошибки. Просто поймите логику и внесите соответствующие изменения.
теперь для параметра (на страницу) в конечной точке я добавил 20, но когда я прокручиваю много раз, это не похоже на добавление 20 каждый раз и повторную загрузку, но добавить больше? почему?
В on<LoadMore>
добавьте state is! LoadMoreState
к условию if.
та же проблема, дайте мне двойное количество предметов, а не просто добавляйте 20 при каждой загрузке
Попробуйте отладить, кажется, что on<LoadMore>
вызывается больше, чем нужно. context.read<PostsBloc>().controller..addListener(() => context.read<PostsBloc>().add(LoadMore()))
наверное каждый раз добавляет нового слушателя. Преобразуйте свой виджет в Stateful
и добавьте прослушиватель только в initState
.
Давайте продолжим обсуждение в чате.
я думаю, что это почти сработало, но я не знаю, как добавить индикатор загрузки для загрузки большего количества, и, пожалуйста, объясните, почему? Я ничего не менял, кроме как сделать так, чтобы в родительском состоянии были сообщения о свойствах.