まずはdartレベルでページング処理を作成

f:id:ta_watanabe:20210928224321p:plain
ページング例

こんな感じのページネーションをflutter webで作ることになりました。

無限スクロールのパッケージはたくさんありますが、上記の様なオーソドックスなパターンのは見当たらなかったので、自作します。

ソース

基本はgoogleのページネーションを参考にしています。

import 'dart:math';

class Pagination {
  // 一度に画面に表示できる最大のページ数
  static const max = 10;

  static int maxHalf() => (max / 2).floor();
  static int centerLeftDiff() => maxHalf() - 1;

  static List<int> pagination(int selectedPage, int pageMax) {
    if (pageMax < selectedPage) {
      throw Exception('selectedPage must be less than or equal to pageMax');
    }

    // 右の最大ページが決まれば1に到達するか10個までカウントする
    List<int> pagination = [];
    for (var i = rightMaxNumber(selectedPage, pageMax); i > 0; i--) {
      if (pagination.length == max) {
        break;
      }
      pagination.add(i);
    }

    // for文の条件を反転させればreverseしなくてもいいと思うが
    // そうなると最小値を求めないといけなくなる
    return pagination.reversed.toList();
  }

  static int rightMaxNumber(int selectedPage, int pageMax) {
    int rightMaxNumber = 0;

    // 今回の例で言うと6ページを境に動作が変わる
    if (selectedPage <= maxHalf() + 1) {
      rightMaxNumber = min(pageMax, max);
    } else {
      rightMaxNumber = min(selectedPage + maxHalf() - 1, pageMax);
    }

    return rightMaxNumber;
  }
}

テストコード

import 'package:flutter_test/flutter_test.dart';

import 'package:flutter2_practice/src/pagination/pagination.dart';

void main() {
  group('Pagination', () {
    test('rightMaxNumber', () {
      expect(Pagination.rightMaxNumber(1, 1), 1);
      expect(Pagination.rightMaxNumber(1, 10), 10);
      expect(Pagination.rightMaxNumber(1, 11), 10);
      expect(Pagination.rightMaxNumber(6, 6), 6);
      expect(Pagination.rightMaxNumber(6, 7), 7);
      expect(Pagination.rightMaxNumber(6, 10), 10);
      expect(Pagination.rightMaxNumber(6, 11), 10);

      expect(Pagination.rightMaxNumber(7, 7), 7);
      expect(Pagination.rightMaxNumber(7, 8), 8);
      expect(Pagination.rightMaxNumber(7, 10), 10);
      expect(Pagination.rightMaxNumber(7, 11), 11);
      expect(Pagination.rightMaxNumber(7, 100), 11);
    });

    test('pagination', () {
      expect(Pagination.pagination(1, 1), [1]);
      expect(Pagination.pagination(1, 3), [1, 2, 3]);
      expect(Pagination.pagination(1, 10), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
      expect(Pagination.pagination(1, 11), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

      expect(Pagination.pagination(6, 6), [1, 2, 3, 4, 5, 6]);
      expect(Pagination.pagination(6, 7), [1, 2, 3, 4, 5, 6, 7]);
      expect(Pagination.pagination(6, 10), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
      expect(Pagination.pagination(6, 11), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

      expect(Pagination.pagination(7, 7), [1, 2, 3, 4, 5, 6, 7]);
      expect(Pagination.pagination(7, 8), [1, 2, 3, 4, 5, 6, 7, 8]);
      expect(Pagination.pagination(7, 10), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
      expect(Pagination.pagination(7, 11), [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
      expect(Pagination.pagination(7, 12), [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
      expect(Pagination.pagination(7, 100), [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);

      expect(Pagination.pagination(14, 20),
          [9, 10, 11, 12, 13, 14, 15, 16, 17, 18]);
    });
  });
}

上記のソースコードと、それをwidgetに組み込んだものも下記にあります。一番上の画像のようにはまだなってません。
https://github.com/na8esin/flutter2_practice/blob/main/lib/src/pagination