Animated Infinite Scroll view
load and display small chunks of items as the user scrolls down the screen
Overview:
1- Declare View-Model extends PaginationViewModel<T>
- PaginationViewModel the layer between user interface and model which is handled by a repository.
- T is Type of your data list. for Example if you have
List<User>
, you will create a viewModel extendsPaginationViewModel<User>
.
Before create the View-Model we’re going to create our Repository. The Repository is the layer between the View-Model and back-end.
The Repository will be something like this:
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;
}
}
}
Now we’re going to create our View-Model. The View-Model will be something like this:
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:
- Declare your view-model in your screen:
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();
}
- Wrap the animated scrollView in your screen:
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 Params:
- viewModel: The View-Model you declared above in this example (required).
- loadingWidget: a widget you want to display when first page is loading (optional).
- footerLoadingWidget: a widget you want to display when pagination data is loading (optional).
- errorWidget: a widget you want to display when pagination data is field loading (throw exception) (optional).
- refreshIndicator: wrap the scroll view inside a
RefreshIndicator
(optional), default value isfalse
. - itemBuilder: a widget function which build your Data Widget inside the scroll view on Each Data Item from list (required).