_ _____ __ _ _____ _ _ __ __
| | | ____| | \ | | / ___/ | | / / \ \ / /
| | | |__ | | | | |___ | |/ / \ / /
| | | __| | | \ | ___ \ | |\ \ \ /
| |___ | |___ | | \ | ___| | | | \ \ / /
|_____| |_____| |_| _| /_____/ |_| _\ /_/
对于很多时候要用到滚动视图,渲染快速要用到 build index 方法 但是 每次都写很麻烦,于是就参照 iOS 方式简化代码步骤 整个分区来实现。
将需要添加的对象实现 TableViewDelegate 内的方法就可以实现简单的 scrollView 动态吸顶效果等 需要自己去拓展吧。
tableview 套 grid 的方法不是很好凑合着 慢慢去更新.
/*
* ***************************************************************
* 获取列表 tableView();
* 设置 sectionPadding() 设置的是区的边距 sliverPadding() 设置的是头部监听部分
* ***************************************************************
* 列表样式 对应方法
* ***************************************************************
* ----------- SliverHeader ------- loadSliverHeader() 实现此方法 实现根据滚动变化的头部 可为空
*
* -----------tableHeader --------- tableHeader() 表头
*
* -------sectionHeader 区 头------ sectionHeader(section)
* -------------------------------
* | list 区 | rowCount(section) listCell 数量
* | listCell | cellWithIndexPath(indexPath) listCell
* | listCell | getSectionType(section) SectionType.list 默认为list
* -------------------------------
* -------------- 区 尾 ------------ sectionFooter(section)
* -------------- 区 头 ------------
* -------------------------------
* | grid区 | getSectionType(section) SectionType.grid
* | gridItem | gridItem | getRowCount(section) gridItem 数量
* | gridItem | gridItem |
* ---------------------------------
* ------------- 区尾 --------------
* -------------- 区 头 ------------
* -------------------------------
* | grid区 | gridHorizontalCount(section) 横向排布数量
* | Item| Item | Item | gridVerticalSpacing(section) 纵向间距
* | Item| Item | Item | gridHorizontalSpacing(section) 横向间距
* | | gridAspectRatio(section) 宽高比例
* ---------------------------------
* ------------- 区尾 --------------
*
* --------------tableFooter-------- tableFooter() 表尾
* */
///使用方法
child:tableView(delegate:this),
//每个区有多少行
getRow(IndexPath indexPath)=> 10;
///类似iOS 的避免初学者觉得是套娃
Widget cellWithIndexPath(IndexPath indexPath){
return Container();
}
滚动视图摘抄 常用使用方式
import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
class TableView extends CustomScrollView {
TableView({
DragStartBehavior? dragStartBehavior,
required List<Widget> slivers,
ScrollController? scrollController,
}):super(
dragStartBehavior:dragStartBehavior?? DragStartBehavior.start,
slivers:slivers,
keyboardDismissBehavior:ScrollViewKeyboardDismissBehavior.manual,
physics: AlwaysScrollableScrollPhysics(parent: BouncingScrollPhysics()),
controller: scrollController,
);
static CustomScrollView tableView({required TableViewDelegate delegate,ScrollController? scrollController}){
var sliverList = <Widget>[];
var sliverHeader = delegate.loadSliverHeader();
///可伸缩的头部部分
if(sliverHeader != null){
var slh = SliverPadding(
padding: delegate.sliverPadding(),
sliver: sliverHeader,
);
sliverList.add(slh);
}
/// 伸缩头下面的唯一的一个头
var tbHeader = delegate._getTableHeader();
sliverList.add(tbHeader);
/// 加载所有的区间内容
for(var i = 0;i< delegate.getSection(); i++){
var sliverHeader =delegate.loadSliverSectionHeader(i); //在中间某个区添加悬浮区头
if(sliverHeader != null){
sliverList.add(sliverHeader); // 加载悬停式区头
}
var header = delegate._getSectionHeader(i);
/// 加载区头
sliverList.add(header);
///加载当前区的list 或 grid 控件 这时候只是加载到区,并没有加载任何 cell 默认加载的是 list 格式的
if(delegate.getSectionType(i) == SectionType.grid){
var grid =delegate. _getSliverGrid(i);// 中间嵌套grid 的情况
sliverList.add(grid);
}else{
var list =delegate._getSliverList(i);
sliverList.add(list);
}
/// 加载区尾内容
var footer = delegate._getSectionFooter(i);
sliverList.add(footer);
}
/// 加载表尾
var tbFooter = delegate._getTableFooter();
sliverList.add(tbFooter);
return TableView(
slivers: sliverList,
scrollController: scrollController,
);
}
}
mixin TableViewDelegate {
SectionType? getSectionType(section){
return null;
}
EdgeInsets sectionPadding(int section){
return EdgeInsets.zero;
}
_getSliverList(int section){
return SliverPadding(
padding: sectionPadding(section),
sliver:SliverList(
delegate: SliverChildBuilderDelegate((context, index){
return cellWithIndexPath(IndexPath(section: section, row: index));
},childCount: getRowCount(section)),
),
);
}
/// 当嵌套有 grid 情况设置
Widget gridCell(IndexPath indexPath){
return Container();
}
///当前区间横向排布数量
int gridHorizontalCount(int section){
return 2;
}
/// 纵向间距
double gridVerticalSpacing(section){
return 5;
}
/// 横向间距
double gridHorizontalSpacing(section){
return 5;
}
/// 宽高比例
double gridAspectRatio(section){
return 1;//宽高比例
}
_getSliverGrid(int section){
return SliverPadding(
padding: sectionPadding(section),
sliver: SliverGrid(
delegate: SliverChildBuilderDelegate((context, index){
return gridCell(IndexPath(section: section, row: index));
},childCount: getRowCount(section),),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: gridHorizontalCount(section), //横向数量
mainAxisSpacing: gridVerticalSpacing(section), //纵向间距
crossAxisSpacing: gridHorizontalSpacing(section), //纵向宽度
childAspectRatio: gridAspectRatio(section),
),
),
);
}
SliverToBoxAdapter _getSectionHeader(int section){
return SliverToBoxAdapter(
child: sectionHeader(section)??Container(),
);
}
SliverToBoxAdapter _getSectionFooter(int section){
return SliverToBoxAdapter(
child: sectionFooter(section)??Container(),
);
}
SliverToBoxAdapter _getTableFooter(){
return SliverToBoxAdapter(
child: tableFooter()??Container(),
);
}
SliverToBoxAdapter _getTableHeader(){
return SliverToBoxAdapter(
child: tableHeader()??Container(),
);
}
/// 这里需要一种类似 demo15 中 的能够监听到变化伸缩的头部控件
SliverPersistentHeader getSliverHeader(TableHeaderDelegate delegate,{bool pinned = false,bool floating = false}){
return SliverPersistentHeader(delegate: delegate,pinned: pinned,floating: floating,);
}
/// 加载可伸缩式的头部部分 默认是空的 需要使用 getSliverHeader(---) 获取该方法
SliverPersistentHeader? loadSliverHeader(){
return null;
}
SliverPersistentHeader? loadSliverSectionHeader(int section){
return null;
}
//头部的边距
EdgeInsets sliverPadding(){
return EdgeInsets.zero;
}
/// ListView 实现方法
Widget cellWithIndexPath(IndexPath indexPath){
return Container();
}
int getRowCount(section){
return 0;
}
/// 实现方法 默认为 1
int getSection(){
return 1;
}
/// 区头 子类可选实现
Widget? sectionHeader(section){
return null;
}
/// 区尾
Widget? sectionFooter(section){
return null;
}
/// 表头
Widget? tableHeader(){
return null;
}
/// 表尾
Widget? tableFooter(){
return null;
}
}
//TODO: ListView 索引 section + row
class IndexPath{
int section;
int row;
IndexPath({
required this.section,
required this.row
});
@override
String toString() {
List;
return "section : ${section} row : ${row}";
}
IndexPath operator +(IndexPath a){
return IndexPath(section: a.section + section, row: a.row + row);
}
IndexPath operator -(IndexPath a){
return IndexPath(section: section-a.section, row: row-a.row);
}
}
///列表区间类型
enum SectionType {
list,
grid,
}
/// shrinkOffset 变化的偏移量 overlapsContent 是否有遮盖部分
typedef Widget TableViewHeaderBuilder (BuildContext context, double shrinkOffset, bool overlapsContent);
/// 可伸缩式表头代理类
class TableHeaderDelegate extends SliverPersistentHeaderDelegate {
final double maxHeight;
final double minHeight;
final TableViewHeaderBuilder builder;
bool? rebuild; //是否允许重复 build 默认是yes
Widget? contentItem;
TableHeaderDelegate({
required this.maxHeight,
required this.minHeight,
required this.builder,
this.rebuild,
});
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
// overlapsContent 是否有重叠
// shrinkOffset 减小的大小
if(contentItem != null&&rebuild == false){ // 降低内容重绘频率 在需要时 使用 stateFullBuild 进行操作
return contentItem!;
}else{
contentItem = SizedBox.expand(child: builder(context,shrinkOffset,overlapsContent),);
return contentItem!;
}
}
@override
// TODO: implement maxExtent
double get maxExtent => (minHeight > maxHeight)?minHeight:maxHeight;
@override
// TODO: implement minExtent
double get minExtent => minHeight;
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
return true; //当前状态是否允许 ReBuilder
}
}