动画无限滚动视图
在用户向下滚动屏幕时加载并显示小块内容
概述
1- 声明扩展了 PaginationViewModel<T> 的 View-Model
- PaginationViewModel 是 **用户界面** 和由存储库处理的 **模型** 之间的层。
- T 是您的数据列表的类型。例如,如果您有
List<User>,您将创建一个扩展PaginationViewModel<User>的 **ViewModel**。
在创建 View-Model 之前,我们将创建我们的 **Repository**。Repository 是 View-Model 和后端之间的层。
Repository 将如下所示
import 'dart:async';
import 'dart:convert';
import 'package:example/config/env.dart';
import 'package:example/models/passenger.dart';
import 'package:animated_infinite_scroll_pagination/animated_infinite_scroll_pagination.dart';
import 'package:example/models/passengers_response.dart';
import 'package:http/http.dart' as http;
class PassengerRepository {
final _controller = StreamController<PaginationState<List<Passenger>>>();
Stream<PaginationState<List<Passenger>>> get result async* {
yield* _controller.stream;
}
Future<int> getPassengersList(int page) async {
/// emit loading
_controller.add(const PaginationLoading());
/// fetch data from server
final api = "${Env.paginationApi}?page=$page&size=${Env.perPage}";
try {
final http.Response response = await http.get(Uri.parse(api));
final responseData = PassengersListResponse.fromJson(jsonDecode(response.body));
final passengers = responseData.data ?? [];
/// emit fetched data
_controller.add(PaginationSuccess(passengers));
return responseData.totalPassengers ?? 0;
} catch (_) {
/// emit error
_controller.add(const PaginationError());
return 0;
}
}
}
现在我们将创建我们的 **View-Model**。View-Model 将如下所示
class PassengersViewModel extends PaginationViewModel<Passenger> {
final repository = PassengerRepository();
/// Sorts this list according to the order specified by the [compare] function.
@override
Function(Passenger a, Passenger b) compare = ((a, b) => a.id.toString().compareTo(b.id.toString()));
/// sort inserted items
@override
bool sortItems = false;
/// decide whether two object represent the same Item
@override
bool areItemsTheSame(Passenger a, Passenger b) {
return a.id == b.id;
}
/// fetch data from repository and emit by Stream to pagination-list
///
/// set total items count -> stop loading on last page
@override
Future<void> fetchData(int page) async {
final total = await repository.getPassengersList(page);
// tell the view-model the total of items.
// this will stop loading more data when last data-chunk is loaded
setTotal(total);
}
/// subscribe for list changes
@override
Stream<PaginationState<List<Passenger>>> streamSubscription() => repository.result;
/// remove an item from passengers list
void remove(Passenger passenger) {
// `params` is a variable declared in `PaginationViewModel`
// which contains the List<T>
final index = params.itemsList.value.items.indexWhere((element) => element.item.id == passenger.id);
// `deleteItem` is a method declared in `PaginationViewModel`
// which expected a integer value `index of item`
if (index != -1) deleteItem(index);
}
}
2- UI
- 在屏幕中声明您的 view-model
final viewModel = PassengersViewModel();
@override
void initState() {
super.initState();
viewModel
..listen() // observe data-list changes when repository update the list
..getPaginationList(); // fetch first chunk of data from server
}
@override
void dispose() {
viewModel.dispose();
super.dispose();
}
- 将动画滚动视图包装在您的屏幕中
deletePassenger(Passenger passenger) {
viewModel.remove(passenger);
}
@override
Widget build(BuildContext context) {
return AnimatedInfiniteScrollView<Passenger>(
viewModel: viewModel,
loadingWidget: const AppProgressBar(), // customize your loading widget
footerLoadingWidget: const AppProgressBar(), // customize your pagination loading widget
errorWidget: const Text("Pagination Error"), // customize your error widget
itemBuilder: (item) => PassengerCard(passenger: item, onDelete: deletePassenger),
refreshIndicator: true,
);
}
AnimatedInfiniteScrollView 参数
- viewModel:您在上面声明的 View-Model,例如 *(必需)*。
- loadingWidget:当第一页加载时您想显示的 widget *(可选)*。
- footerLoadingWidget:当分页数据加载时您想显示的 widget *(可选)*。
- errorWidget:当分页数据加载失败(抛出异常)时您想显示的 widget *(可选)*。
- refreshIndicator:将滚动视图包装在
RefreshIndicator中 *(可选)*,**默认值**为false。 - itemBuilder:一个 widget 函数,用于在滚动视图中构建您的 **数据 Widget**,针对列表中的每个 **数据项** *(必需)*。
