使用flutter自定义一个分页器
最近在写flutter项目,项目刚好需要一个分页器,对数据进行分页处理,一开始想要在网上找有没有已经写好的插件,搜寻一会后还是想着自己写一个。
先看看效果图
思路
- 首先要准备一个盒子,装载分页数字。
- 其次要考虑数据总数量在一定范围内该如何进行分页比较合理,比如:展示的要有多少的页数,隐藏起来要有多少的页数,从而对盒子进行规划
- 并且当页数改变的时候,对盒子内的页码参数进行合适的变化
以上是我的简单思路,具体还是看看源码
- 第一,因为分页器的内容需要时常改变,所以,要使用 StatefulWidget
class Pager extends StatefulWidget {const Pager({Key? key}) : super(key: key);@overrideState<Pager> createState() => _PagerState();}class _PagerState extends State<Pager> {@overrideWidget build(BuildContext context) {return Container();}}
- 第二,准备展示具体的页码参数,我的思路是根据页码生成对应的widget,所以存放页码的容器使用 list ,但是这个数组需要根据你需要在页面上展示多少的可见页码和隐藏页码来决定
class _PagerState extends State<Pager> {List _list = []; // 存放页码的数组int _total = 10; // 页码数量(总数据量/一页需要展示多少条数据)int _pageIndex = 1; // 当前页码/*假如:在一行上展示七个页码item,那么要对total进行分配,并且需要根据当前的页码来决定list里面页码的规划*/void managePage(){_list = []; // 将页码数组制空if(_total <= 1) { // 当页码数量小于等于1时就不需要页码了,直接跳过,此时list为空数组return;}if(_total <= 7) { // 7:是我自己规定一行内展示的页码item数量,需要展示多少可以自己定义,当小于等于7时,直接进行展示这7个页码itemfor(var i = 0; i < _total; i++) {_list.add(i+1);}} else if(_pageIndex <= 4) { // 当页码大于7后,则需要根据当前页码对有些页码进行隐藏_list = [1,2,3,4,5, "...", _total];} else if(_pageIndex > _total - 3) { // 以此类推_list = [1,2, "...", _total - 3, _total - 2, _total - 1, _total];} else { _list = [1, "...", _pageIndex - 1, _pageIndex, _pageIndex + 1, "...", _total];}// 页码制定好后,根据自己需求,加上前一页按钮,后一页按钮_list.insert(0, "上一页");_list.add("下一页");}@overrideWidget build(BuildContext context) {return Container();}}
- 第三,将页码展示在页面,在这里行内布局我使用 Row,里面的每个页码我用的容器 Container套入Text组件,样式可以自己根据项目需要定义
@overrideWidget build(BuildContext context) {return Container(width: double.infinity,child: Row(mainAxisSize: MainAxisSize.min,mainAxisAlignment: MainAxisAlignment.spaceAround,children: _list.mapIndexed((index, ele) {if(index == 0) {return GestureDetector(onTap: () {if(_pageIndex > 1) {setState(() {_pageIndex--;// 改变页码在这里发送事件通知});}},child: buildPagerItem(child: ele),);}if(index == _list.length - 1) {return GestureDetector(onTap: () {if(_pageIndex < _total) {setState(() {_pageIndex++;// 改变页码在这里发送事件通知});}},child: buildPagerItem(child: ele),);}return GestureDetector(onTap: () {if(ele != "...") {setState(() {if(_pageIndex != ele) {_pageIndex = ele;// 改变页码在这里发送事件通知}});}},child: buildPagerItem(child: Text("$ele",// 当前页码对应的组件的样式style: ele == _pageIndex ? TextStyle() : TextStyle())),);}).toList(),),);}// 在这里我对 build 里面的内容进行抽取Widget buildPagerItem({required Widget child}) {return Container(width: 100,height: 50,alignment: Alignment.center,decoration: BoxDecoration(),margin: EdgeInsets.symmetric(),child: child,);}
最后上全部源码
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';import 'package:dianying/core/extension/index.dart';class Pager extends StatefulWidget {late TextStyle activeTextStyle; // 当前页码的文本样式late Widget preWidget; // 上一页的widgetlate Widget nextWidget; // 下一页的widgetint total; // 总页码数量late double containerWidth; // 容器的宽度late MainAxisAlignment mainAxisAlignment; // 页码横主轴对齐方式late TextStyle normalTextStyle; // 其他页码的文本样式late double pagerItemWidth; // 页码item的宽度late double pagerItemHeight; // 页码item的高度late BoxDecoration pagerItemDecoration; // 页码item的样式ValueChanged pageChange; // 页码改变的回调late EdgeInsetsGeometry pagerItemMargin; // 页码item的边距Pager({Key? key,TextStyle? activeTextStyle,Widget? preWidget,Widget? nextWidget,required this.total,double? containerWidth,MainAxisAlignment? mainAxisAlignment,TextStyle? normalTextStyle,double? pagerItemWidth,double? pagerItemHeight,BoxDecoration? pagerItemDecoration,required this.pageChange,EdgeInsetsGeometry? pagerItemMargin}) : activeTextStyle = activeTextStyle ?? TextStyle(color: Colors.white,fontSize: 14.px),preWidget = preWidget ?? Icon(Icons.arrow_left, color: Colors.red, size: 22.px),nextWidget = nextWidget ?? Icon(Icons.arrow_right, color: Colors.red, size: 22.px),containerWidth = containerWidth ?? double.infinity,mainAxisAlignment = mainAxisAlignment ?? MainAxisAlignment.spaceAround,normalTextStyle = normalTextStyle ?? TextStyle(color: Colors.red, fontSize: 14.px),pagerItemWidth = pagerItemWidth ?? 36.px,pagerItemHeight = pagerItemHeight ?? 30.px,pagerItemDecoration = pagerItemDecoration ?? BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(3.px)),pagerItemMargin = pagerItemMargin ?? const EdgeInsets.all(0),super(key: key);@overrideState<Pager> createState() => _PagerState();
}class _PagerState extends State<Pager> {int _pageIndex = 1;List _list = [];void manageList() {_list = [];if(widget.total <= 1) {return;}if(widget.total <= 7) {for(var i = 0; i < widget.total; i++) {_list.add(i+1);}} else if(_pageIndex <= 4) {_list = [1,2,3,4,5, "...", widget.total];} else if(_pageIndex > widget.total - 3) {_list = [1,2, "...", widget.total - 3, widget.total - 2, widget.total - 1, widget.total];} else {_list = [1, "...", _pageIndex - 1, _pageIndex, _pageIndex + 1, "...", widget.total];}_list.insert(0, widget.preWidget);_list.add(widget.nextWidget);}@overrideWidget build(BuildContext context) {return buildContent();}Widget buildContent() {manageList();return Container(width: widget.containerWidth,child: Row(mainAxisSize: MainAxisSize.min,mainAxisAlignment: widget.mainAxisAlignment,children: _list.mapIndexed((index, ele) {if(index == 0) {return GestureDetector(onTap: () {if(_pageIndex > 1) {setState(() {_pageIndex--;widget.pageChange(_pageIndex);});}},child: buildPagerItem(child: ele),);}if(index == _list.length - 1) {return GestureDetector(onTap: () {if(_pageIndex < widget.total) {setState(() {_pageIndex++;widget.pageChange(_pageIndex);});}},child: buildPagerItem(child: ele),);}return GestureDetector(onTap: () {if(ele != "...") {setState(() {if(_pageIndex != ele) {_pageIndex = ele;widget.pageChange(_pageIndex);}});}},child: buildPagerItem(child: Text("$ele",style: ele == _pageIndex ? widget.activeTextStyle : widget.normalTextStyle,)),);}).toList(),),);}Widget buildPagerItem({required Widget child}) {return Container(width: widget.pagerItemWidth,height: widget.pagerItemHeight,alignment: Alignment.center,decoration: widget.pagerItemDecoration,margin: widget.pagerItemMargin,child: child,);}
}