animated_infinite_scroll_pagination 0.1.1 copy "animated_infinite_scroll_pagination: ^0.1.1" to clipboard
animated_infinite_scroll_pagination: ^0.1.1 copied to clipboard

outdated

Flutter Animated infinite scroll pagination

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 extends PaginationViewModel<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();

	/// 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,
      onRefresh: () {
        // handle swipe refresh event
      }
    );
  }

AnimatedInfiniteScrollView Parameters: #

  • viewModel: The View-Model you declared above in this example (required).
  • topWidget: a widget you want to place at the top of the first itemBuilder widget (optional).
  • 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 is false.
  • itemBuilder: a widget function which build your Data Widget inside the scroll view on Each Data Item from list (required).
  • onRefresh: callback called when user swipe refresh to load new list (optional)
  • scrollDirection: Axis.vertical or Axis.horizontal (optional)
  • gridDelegate: A delegate that controls the layout of the children within the GridView (optional)