flutter_pagewise 1.2.3

  • Readme
  • Changelog
  • Example
  • Installing
  • 97

A library for widgets that load their content one page (or batch) at a time (also known as lazy-loading and pagination).

flutter_pagewise

Features #

  • Load data one page at a time
  • Retry failed pages
  • Override the default loading, retry, and error widgets if desired
  • Manage loading of data more closely using a PagewiseLoadController
  • ListView and GridView implementations
  • SliverList and SliverGrid implementations
  • Extendability using inheritance

Breaking Change Starting V1.0.0: #

The library has been rewritten in version 1.0.0 to provide a more efficient implementation that does not require a totalCount parameter and shows only one loading sign when users scroll down. In addition, a new parameter has been added to itemBuilder callback to provide the index if needed by the user.

Installing the library: #

Like any other package, add the library to your pubspec.yaml dependencies:

dependencies:
    flutter_pagewise:

Then import it wherever you want to use it:

import 'package:flutter_pagewise/flutter_pagewise.dart';

Using the library #

Check out the example

The library provides the following widgets:

  • PagewiseGridView: A pagewise implementation of GridView. It could be used as follows:
PagewiseGridView.count(
  pageSize: 10,
  crossAxisCount: 2,
  mainAxisSpacing: 8.0,
  crossAxisSpacing: 8.0,
  childAspectRatio: 0.555,
  padding: EdgeInsets.all(15.0),
  itemBuilder: (context, entry, index) {
    // return a widget that displays the entry's data
  },
  pageFuture: (pageIndex) {
    // return a Future that resolves to a list containing the page's data
  },
);
  • PagewiseListView: A pagewise implementation of ListView. It could be used as follows:
PagewiseListView(
  pageSize: 10,
  padding: EdgeInsets.all(15.0),
  itemBuilder: (context, entry, index) {
    // return a widget that displays the entry's data
  },
  pageFuture: (pageIndex) {
    // return a Future that resolves to a list containing the page's data
  }
);
  • PagewiseSliverGrid: A pagewise implementation of SliverGrid. It could be used similar to PagewiseGridView for cases where a sliver is needed.
  • PagewiseSliverList: A pagewise implementation of SliverList. It could be used similar to PagewiseListView for cases where a sliver is needed.

The classes provide all the properties of ListViews and GridViews. In addition, you must provide the itemBuilder, which tells Pagewise how you want to render each element, and pageFuture, which Pagewise calls to fetch new pages. Please note that pageFuture must not return more values than mentioned in the pageSize parameter.

Customizing the widget: #

In addition to the required parameters, Pagewise provides you with optional parameters to customize the widget. You have loadingBuilder, errorBuilder, noItemsFoundBuilder, and retryBuilder to customize the widgets that show on loading, error, no found items and retry respectively.

The loadingBuilder can be used as follows:

loadingBuilder: (context) {
  return Text('Loading...');
}

The noItemsFoundBuilder can be used as follows:

noItemsFoundBuilder: (context) {
  return Text('No Items Found');
}

The retryBuilder can be used as follows:

retryBuilder: (context, callback) {
  return RaisedButton(
    child: Text('Retry'),
    onPressed: () => callback()
  );
}

Thus, the retryBuilder provides you with a callback that you can call when you want to retry.

The errorBuilder is only relevant when showRetry is set to false, because, otherwise, the retryBuilder is shown instead. The errorBuilder can be used as follows:

errorBuilder: (context, error) {
  return Text('Error: $error');
}

Check the classes' documentation for more details.

Providing your own PagewiseLoadController: #

Pagewise widgets manage the loading of pages using a PagewiseLoadController. This controller is responsible for fetching data, handling errors, etc.

You don't have to provide a controller yourself when creating a Pagewise widget. The widget will create one for you. However you might wish to create one yourself in order to achieve some effects.

Notice though that if you provide a controller yourself, you should provide the [pageFuture] and [pageSize] parameters to the controller instead of the widget.

A possible use case of the controller is to force a reset of the loaded pages using a RefreshIndicator. you could achieve that as follows (note that we added the Future.value({}) as a dummy return value, because onRefresh expects a Future, but reset does not return one):

final _pageLoadController = PagewiseLoadController(
  pageSize: 6,
  pageFuture: BackendService.getPage
);

@override
Widget build(BuildContext context) {
  return RefreshIndicator(
    onRefresh: () async {
      this._pageLoadController.reset();
      await Future.value({});
    },
    child: PagewiseListView(
        itemBuilder: this._itemBuilder,
        pageLoadController: this._pageLoadController,
    ),
  );
}

Another use case for creating the controller yourself is if you want to listen to the state of Pagewise and act accordingly. For example, you might want to show a snackbar when we reach the end of the list In that case, you could do:

final _pageLoadController = PagewiseLoadController(
  pageSize: 6,
  pageFuture: BackendService.getPage
);

@override
void initState() {
  super.initState();
  this._pageLoadController.addListener(() {
    if (!this._pageLoadController.hasMoreItems) {
      Scaffold.of(context).showSnackBar(
        SnackBar(
          content: Text('No More Items!')
        )
      );
    }
  });
}

Creating your own Pagewise Widgets: #

You need to inherit from the Pagewise class. Check the code of PagewiseListView and PagewiseGridView for examples

1.2.3 - 27/03/2019 #

  • Handle a null Future return when loading a page. We assume a null Future is the same as an empty one.

1.2.2 - 06/02/2019 #

  • Fix environment in pubspec.yaml to remove health complaint on dart website

1.2.1 - 06/02/2019 #

  • Fixes the GridView exception in case the number of items in the last page is less than page size (issues #35, #33)
  • Fixes the race condition that might cause the same page to be fetched multiple times (Issues #6, #30)
  • Improves types, generic types, and default values on parameters (Issues #24, #25, #32)
  • Implements fixes and improvements to the README and the example

1.2.0 - 19/12/2018 #

  • Add scenario of moving from one widget.controller to another in didUpdateWidget
  • Implement noItemsFoundBuilder

1.1.1 - 18/12/2018 #

  • Add didUpdateWidget to PagewiseState class for cases of switching controller

1.1.0 - 18/12/2018 #

  • Implement controller pattern for more control and visibility over page loading
  • Provide support for slivers (PagewiseSliverList and PagewiseSliverGrid)

1.0.0 - 16/12/2018 #

  • Re-architect the library for more efficiency and ease of use.

0.5.0 - 22/08/2018 #

  • Provide ability to retry
  • Fix case of page futures refiring when rebuilt

0.4.1 - 17/08/2018 #

  • Decrease size of GIF in README to make it load faster

0.4.0 - 11/08/2018 #

  • Make future final in _FutureBuilderWrapper
  • Remove unneeded _pages data structure and operations
  • Provide ItemListBuilder for cases where we want to build a list of widgets for each data entry
  • Provide controller property to allow custom ScrollController

0.3.0 - 04/08/2018. #

0.2.0 - 31/07/2018. #

  • Replace loadingWidget with a loadingBuilder that accepts a BuildContext and returns a widget.
  • Fix environment constraints in pubspec.yaml
  • Make the example better looking, and the demo as well
  • Mention lazy-loading in the README.
  • Reformat the code using flutter format

0.1.2 - 29/07/2018. #

  • Add gif to README.

0.1.1 - 29/07/2018. #

  • Small fix to README.

0.1.0 - 29/07/2018. #

  • Provided basic functionality for Pagewise class, PagewiseGridView class and PagewiseListView class.

example/lib/main.dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_pagewise/flutter_pagewise.dart';
import 'package:http/http.dart' as http;

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Pagewise Demo',
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 4,
      child: Scaffold(
          appBar: AppBar(
            title: Text('Pagewise'),
            bottom: TabBar(tabs: [
              Tab(
                text: 'List',
              ),
              Tab(text: 'Grid'),
              Tab(text: 'SliverList'),
              Tab(text: 'SliverGrid')
            ]),
          ),
          body: TabBarView(
            children: [
              PagewiseListViewExample(),
              PagewiseGridViewExample(),
              PagewiseSliverListExample(),
              PagewiseSliverGridExample()
            ],
          )),
    );
  }
}

class PagewiseGridViewExample extends StatelessWidget {
  static const int PAGE_SIZE = 6;

  @override
  Widget build(BuildContext context) {
    return PagewiseGridView.count(
      pageSize: PAGE_SIZE,
      crossAxisCount: 3,
      mainAxisSpacing: 8.0,
      crossAxisSpacing: 8.0,
      childAspectRatio: 0.555,
      padding: EdgeInsets.all(15.0),
      itemBuilder: this._itemBuilder,
      pageFuture: (pageIndex) =>
          BackendService.getImages(pageIndex * PAGE_SIZE, PAGE_SIZE),
    );
  }

  Widget _itemBuilder(context, ImageModel entry, _) {
    return Container(
        decoration: BoxDecoration(
          border: Border.all(color: Colors.grey[600]),
        ),
        child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Expanded(
                child: Container(
                  decoration: BoxDecoration(
                      color: Colors.grey[200],
                      image: DecorationImage(
                          image: NetworkImage(entry.thumbnailUrl),
                          fit: BoxFit.fill)),
                ),
              ),
              SizedBox(height: 8.0),
              Expanded(
                child: Padding(
                    padding: EdgeInsets.symmetric(horizontal: 8.0),
                    child: SizedBox(
                        height: 30.0,
                        child: SingleChildScrollView(
                            child: Text(entry.title,
                                style: TextStyle(fontSize: 12.0))))),
              ),
              SizedBox(height: 8.0),
              Padding(
                padding: EdgeInsets.symmetric(horizontal: 8.0),
                child: Text(
                  entry.id,
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ),
              SizedBox(height: 8.0)
            ]));
  }
}

class PagewiseListViewExample extends StatelessWidget {
  static const int PAGE_SIZE = 10;

  @override
  Widget build(BuildContext context) {
    return PagewiseListView(
        pageSize: PAGE_SIZE,
        itemBuilder: this._itemBuilder,
        pageFuture: (pageIndex) =>
            BackendService.getPosts(pageIndex * PAGE_SIZE, PAGE_SIZE));
  }

  Widget _itemBuilder(context, PostModel entry, _) {
    return Column(
      children: <Widget>[
        ListTile(
          leading: Icon(
            Icons.person,
            color: Colors.brown[200],
          ),
          title: Text(entry.title),
          subtitle: Text(entry.body),
        ),
        Divider()
      ],
    );
  }
}

class PagewiseSliverListExample extends StatelessWidget {
  static const int PAGE_SIZE = 6;

  @override
  Widget build(BuildContext context) {
    return CustomScrollView(slivers: [
      SliverAppBar(
        title: Text('This is a sliver app bar'),
        snap: true,
        floating: true,
      ),
      PagewiseSliverList(
          pageSize: PAGE_SIZE,
          itemBuilder: this._itemBuilder,
          pageFuture: (pageIndex) =>
              BackendService.getPosts(pageIndex * PAGE_SIZE, PAGE_SIZE))
    ]);
  }

  Widget _itemBuilder(context, PostModel entry, _) {
    return Column(
      children: <Widget>[
        ListTile(
          leading: Icon(
            Icons.person,
            color: Colors.brown[200],
          ),
          title: Text(entry.title),
          subtitle: Text(entry.body),
        ),
        Divider()
      ],
    );
  }
}

class PagewiseSliverGridExample extends StatelessWidget {
  static const int PAGE_SIZE = 6;

  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: <Widget>[
        SliverAppBar(
          title: Text('This is a sliver app bar'),
          floating: true,
          snap: true,
        ),
        SliverPadding(
          padding: const EdgeInsets.all(8.0),
          sliver: PagewiseSliverGrid.count(
            pageSize: 6,
            crossAxisCount: 3,
            mainAxisSpacing: 8.0,
            crossAxisSpacing: 8.0,
            childAspectRatio: 0.555,
            itemBuilder: this._itemBuilder,
            pageFuture: (pageIndex) =>
                BackendService.getImages(pageIndex * PAGE_SIZE, PAGE_SIZE),
          ),
        )
      ],
    );
  }

  Widget _itemBuilder(context, ImageModel entry, _) {
    return Container(
        decoration: BoxDecoration(
          border: Border.all(color: Colors.grey[600]),
        ),
        child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Expanded(
                child: Container(
                  decoration: BoxDecoration(
                      color: Colors.grey[200],
                      image: DecorationImage(
                          image: NetworkImage(entry.thumbnailUrl),
                          fit: BoxFit.fill)),
                ),
              ),
              SizedBox(height: 8.0),
              Expanded(
                child: Padding(
                    padding: EdgeInsets.symmetric(horizontal: 8.0),
                    child: SizedBox(
                        height: 30.0,
                        child: SingleChildScrollView(
                            child: Text(entry.title,
                                style: TextStyle(fontSize: 12.0))))),
              ),
              SizedBox(height: 8.0),
              Padding(
                padding: EdgeInsets.symmetric(horizontal: 8.0),
                child: Text(
                  entry.id,
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ),
              SizedBox(height: 8.0)
            ]));
  }
}

class BackendService {
  static Future<List<PostModel>> getPosts(offset, limit) async {
    final responseBody = (await http.get(
            'http://jsonplaceholder.typicode.com/posts?_start=$offset&_limit=$limit'))
        .body;

    // The response body is an array of items
    return PostModel.fromJsonList(json.decode(responseBody));
  }

  static Future<List<ImageModel>> getImages(offset, limit) async {
    final responseBody = (await http.get(
            'http://jsonplaceholder.typicode.com/photos?_start=$offset&_limit=$limit'))
        .body;

    // The response body is an array of items.
    return ImageModel.fromJsonList(json.decode(responseBody));
  }
}

class PostModel {
  String title;
  String body;

  PostModel.fromJson(obj) {
    this.title = obj['title'];
    this.body = obj['body'];
  }

  static List<PostModel> fromJsonList(jsonList) {
    return jsonList.map<PostModel>((obj) => PostModel.fromJson(obj)).toList();
  }
}

class ImageModel {
  String title;
  String id;
  String thumbnailUrl;

  ImageModel.fromJson(obj) {
    this.title = obj['title'];
    this.id = obj['id'].toString();
    this.thumbnailUrl = obj['thumbnailUrl'];
  }

  static List<ImageModel> fromJsonList(jsonList) {
    return jsonList.map<ImageModel>((obj) => ImageModel.fromJson(obj)).toList();
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  flutter_pagewise: ^1.2.3

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:flutter_pagewise/flutter_pagewise.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
93
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
99
Overall:
Weighted score of the above. [more]
97
Learn more about scoring.

We analyzed this package on Mar 31, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.7.1
  • pana: 0.13.6
  • Flutter: 1.12.13+hotfix.8

Maintenance suggestions

Package is getting outdated. (-0.82 points)

The package was last published 52 weeks ago.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
async ^2.0.8 2.4.1
flutter 0.0.0
Transitive dependencies
collection 1.14.11 1.14.12
meta 1.1.8
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test