Flutter仿web端pagination

vw2ch-fskhr.gif

当前页码保持居中:


gmteo-wlbnb.gif

选中页码保持居中的模式,需要找出当前页左右临近的几个数字,单侧个数不足由另一侧补齐。

完整代码:

import 'package:flutter/material.dart';

class WebPagination extends StatefulWidget {
  final int initPage;
  final int pageSize; // 每页item个数
  final int total; // 总页数
  final double itemSize; // 页码item大小
  final double textFont; // 页码item文字大小
  final Color selectTextColor; // 页码item文字颜色-选中
  final Color unselectTextColor; // 页码item文字颜色-未选中
  final Color selectItemColor; // 页码item背景颜色-选中
  final Color unselectItemColor; // 页码item背景颜色-未选中
  final Color arrowItemColor; // 翻页item背景颜色
  final Color arrowItemTextColor; // 翻页item文字颜色
  final bool curPageAlwaysCenter; // 当前页码是否保持居中

  const WebPagination({
    Key? key,
    this.initPage = 1,
    this.pageSize = 10,
    required this.total,
    this.itemSize = 30,
    this.textFont = 14,
    this.selectTextColor = Colors.white,
    this.unselectTextColor = Colors.black,
    this.selectItemColor = Colors.redAccent,
    this.unselectItemColor = Colors.white,
    this.arrowItemColor = Colors.redAccent,
    this.arrowItemTextColor = Colors.white,
    this.curPageAlwaysCenter = true,
  }) : super(key: key);

  @override
  State<WebPagination> createState() => _WebPaginationState();
}

class _WebPaginationState extends State<WebPagination> {
  int curPage = 1;
  List<int> paginationList = []; // item列表

  @override
  void initState() {
    super.initState();
    curPage = widget.initPage;
    resetPagination(curPage);
  }

  // 刷新页码item列表
  void resetPagination(int curIndex) {
    if (curIndex <= 0) curIndex = 1;
    if (curIndex > widget.total) curIndex = widget.total;
    curPage = curIndex; // 当前页

    // 回调
    if (widget.pageChanged != null) {
      widget.pageChanged!(curPage);
    }

    paginationList.clear();

    if (widget.curPageAlwaysCenter) {
      // 选中页码保持居中
      int leftLackNum = 0; // curPage左边缺少的item个数
      int rightLackNum = 0; // curPage右边缺少的item个数

      // 左半边需要的item个数,把curPage也算在左半边,个数+1
      int leftCount = (widget.pageSize / 2).floor() + 1;

      if (curPage < leftCount) {
        // curPage小于leftCount,左半边的按钮不够了,记录缺少的个数
        leftLackNum = leftCount - curPage;
      }

      // 右半边需要的item个数,pageSize-leftCount
      int rightCount = widget.pageSize - leftCount;
      if ((widget.total - curPage) < rightCount) {
        // 右半边剩余的按钮不够了,记录缺少的个数
        rightLackNum = rightCount - (widget.total - curPage);
      }

      // 添加左半边的item进列表,个数为 右边缺少的个数+左边原有的个数
      for (int i = rightLackNum + leftCount - 1; i >= 0; i--) {
        if (curPage - i > 0) {
          paginationList.add(curPage - i);
        }
      }

      // 添加右半边的item进列表,个数为 左边缺少的个数+右边原有的个数
      for (int i = 1; i <= leftLackNum + rightCount; i++) {
        if (curPage + i <= widget.total) {
          paginationList.add(curPage + i);
        }
      }

      debugPrint('左边缺少的个数:$leftLackNum 右边缺少的个数:$rightLackNum');
    } else {
      int startIndex = curPage % widget.pageSize == 0
          ? curPage - widget.pageSize
          : (curPage / widget.pageSize).floor() * widget.pageSize;
      for (int i = 1; i <= widget.pageSize; i++) {
        if (startIndex + i <= widget.total) {
          paginationList.add(startIndex + i);
        }
      }
    }

    debugPrint(
        'curPage:$curPage\npageList:${paginationList.toString()}\npageSize:${paginationList.length}');

    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        // 第一页
        GestureDetector(
          child: Container(
            margin: const EdgeInsets.symmetric(horizontal: 2),
            width: widget.itemSize,
            height: widget.itemSize,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(4),
              color: widget.arrowItemColor,
            ),
            alignment: Alignment.center,
            child: Icon(
              Icons.first_page,
              color: widget.arrowItemTextColor,
            ),
          ),
          onTap: () {
            resetPagination(1);
          },
        ),
        // 上一页
        GestureDetector(
          child: Container(
            margin: const EdgeInsets.symmetric(horizontal: 2),
            width: widget.itemSize,
            height: widget.itemSize,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(4),
              color: widget.arrowItemColor,
            ),
            alignment: Alignment.center,
            child: Icon(
              Icons.chevron_left,
              color: widget.arrowItemTextColor,
            ),
          ),
          onTap: () {
            resetPagination(curPage - 1);
          },
        ),
        // 页码列表
        ...List.generate(paginationList.length, (index) {
          return GestureDetector(
            child: Container(
              margin: const EdgeInsets.symmetric(horizontal: 2),
              width: widget.itemSize,
              height: widget.itemSize,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(4),
                color: curPage == paginationList[index]
                    ? widget.selectItemColor
                    : widget.unselectItemColor,
              ),
              alignment: Alignment.center,
              child: Text(
                paginationList[index].toString(),
                style: TextStyle(
                  fontSize: widget.textFont,
                  color: curPage == paginationList[index]
                      ? widget.selectTextColor
                      : widget.unselectTextColor,
                ),
              ),
            ),
            onTap: () {
              resetPagination(paginationList[index]);
            },
          );
        }),
        // 下一页
        GestureDetector(
          child: Container(
            margin: const EdgeInsets.symmetric(horizontal: 2),
            width: widget.itemSize,
            height: widget.itemSize,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(4),
              color: widget.arrowItemColor,
            ),
            alignment: Alignment.center,
            child: Icon(
              Icons.chevron_right,
              color: widget.arrowItemTextColor,
            ),
          ),
          onTap: () {
            resetPagination(curPage + 1);
          },
        ),
        // 最后一页
        GestureDetector(
          child: Container(
            margin: const EdgeInsets.symmetric(horizontal: 2),
            width: widget.itemSize,
            height: widget.itemSize,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(4),
              color: widget.arrowItemColor,
            ),
            alignment: Alignment.center,
            child: Icon(
              Icons.last_page,
              color: widget.arrowItemTextColor,
            ),
          ),
          onTap: () {
            resetPagination(widget.total);
          },
        ),
      ],
    );
  }
}

使用:

WebPagination(
  pageSize: 6,
  total: 20,
  curPageAlwaysCenter: true,
  pageChanged: (int page) {
    debugPrint('$page');
  },
)

入参:

final int initPage; // 初始页码
final int pageSize; // 每页item个数
final int total; // 总页数
final double itemSize; // 页码item大小
final double textFont; // 页码item文字大小
final Color selectTextColor; // 页码item文字颜色-选中
final Color unselectTextColor; // 页码item文字颜色-未选中
final Color selectItemColor; // 页码item背景颜色-选中
final Color unselectItemColor; // 页码item背景颜色-未选中
final Color arrowItemColor; // 翻页item背景颜色
final Color arrowItemTextColor; // 翻页item文字颜色
final bool curPageAlwaysCenter; // 当前页码是否保持居中
final Function(int)? pageChanged; // 页码变化回调
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容