背景
表格是在应用程序中高效组织数据的有用工具。 Flutter 的最新更新引入了 TwoDimensionalScrollView
小部件。
TwoDimensionalScrollView 是一个小部件,它结合了 TwoDimensionalScrollable
和 TwoDimensionalViewport
在垂直和水平维度上创建内容的交互式滚动窗格,但直接使用它有点具有挑战性。
下面是在 Flutter 中实现 2D ScrollView 的演示代码。
介绍
Flutter 的预构建 widget 具有出色的性能,仅当子视图位于视图中时才延迟渲染子视图,从而提高了性能,但 Flutter 发布了一个新包 two_dimensional_scrollables
来实现在垂直轴和水平轴上滚动的 TableView。
在本教程中,我们将探索这个包来实现一个非常简单的 Tableview,并了解如何自定义它。
您可以在这里找到完整的源代码。
执行
添加依赖项
要添加依赖项,请首先打开 pubspec.yaml 文件,然后添加依赖项。
yaml
dependencies:
two_dimensional_scrollables: <latest version>
将 TableView 添加到屏幕
添加 TableView
时,可以使用两个构造函数:
TableView.list
:这个与 Flutter 的 ListView 类似。它一次添加所有单元格,并且适用于较短的列表。Tableview.builder
:当视图进入视口时,它会延迟添加子项。非常适合您不想一次加载所有内容的较大列表!
让我们创建一个简单的 Tableview 来显示以下信息。
dart
class Employee {
final String id;
final String name;
final String role;
final String email;
Employee({
required this.id,
required this.name,
required this.role,
required this.email});
static get getEmployees{
return [
Employee(id: '1', name: 'John Doe', role: 'Manager', email: 'john@example.com'),
Employee(id: '2', name: 'Jane Smith', role: 'Developer', email: 'jane@example.com'),
Employee(id: '3', name: 'Mike Johnson', role: 'Designer', email: 'mike@example.com'),
Employee(id: '4', name: 'Emily Brown', role: 'HR Specialist',email: 'emily@example.com'),
Employee(id: '5', name: 'Alex Lee', role: 'Marketing Analyst', email: 'alex@example.com'),
Employee(id: '6', name: 'John Doe', role: 'Manager', email: 'john@example.com'),
Employee(id: '7', name: 'Jane Smith', role: 'Developer', email: 'jane@example.com'),
Employee(id: '8', name: 'Mike Johnson', role: 'Designer', email: 'mike@example.com'),
Employee(id: '9', name: 'Emily Brown', role: 'HR Specialist',email: 'emily@example.com'),
Employee(id: '10', name: 'Alex Lee', role: 'Marketing Analyst', email: 'alex@example.com'),
];
}
}
这是 TableView.builder
的基本示例代码。
dart
class TwoDimensionalScrollableDemo extends StatelessWidget {
TwoDimensionalScrollableDemo({super.key});
final List<Employee> employees = Employee.getEmployees;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Table view Demo"),
),
body: TableView.builder(
columnCount: 2,
rowCount: 11,
columnBuilder: buildTableSpan,
rowBuilder: buildTableSpan,
cellBuilder: (BuildContext context, TableVicinity vicinity) {
return Container(child: Center(child: addText(vicinity)));
}),
);
}
TableSpan buildTableSpan(int index){
return TableSpan(extent: FixedTableSpanExtent(50));
}
Widget addText(TableVicinity vicinity) {
if (vicinity.yIndex == 0 && vicinity.xIndex == 0) {
return const Text("Index");
} else if (vicinity.yIndex == 0 && vicinity.xIndex == 1) {
return const Text("name");
} else if (vicinity.xIndex == 0) {
return Text(employees[vicinity.yIndex-1].id);
} else if (vicinity.xIndex == 1) {
return Text(employees[vicinity.yIndex-1].name);
}
return Text("");
}
}
这对于几行代码来说已经相当不错了,让我们分解一下 TableView.builder
所需的参数。
columnCount
:该参数设置表中的列数。rowCount
:该参数设置表中的行数。columnBuilder
:它是一个帮助定义表中每列的布局和功能的函数。它接受一个整数作为参数,并返回一个TableSpan
,它构造和配置列。rowbuilder
:与columnBuilder
类似,此函数定义 TableView 中每行的布局和行为。cellBuilder
:它处理表中每个单元格的布局。它采用TableVicinity
参数,其中包含特定单元格的行索引和列索引。这有助于您自定义表格中各个单元格的外观和行为。
运行上面的代码,您将看到以下输出。
现在,让我们研究一下 TableView.builder
提供的功能!那么,让我们看看如何自定义和控制 TableView 并添加更多功能。
TableSpan 表跨度
TableSpan 表示 TableView 中的一行或一列。
添加边框
dart
TableSpan buildTableSpan(int index) {
TableSpanDecoration decoration = const TableSpanDecoration(
border: TableSpanBorder(
trailing: BorderSide(color: Colors.black),
leading: BorderSide(color: Colors.black)));
return TableSpan(
extent: const FixedTableSpanExtent(100),
foregroundDecoration: decoration);
}
TableSpanDecoration
类用于指定 TableSpan
的装饰。我们可以通过 TableSpanBorder
类中的 border
参数为 TableSpan
添加边框,该类具有代表行左右边框和列上下边框的拖尾和前导属性。
添加颜色
dart
TableSpan buildTableSpan(int index) {
TableSpanDecoration decoration = TableSpanDecoration(
color: index == 0 ? Colors.grey[300] : null,
border: const TableSpanBorder(
trailing: BorderSide(color: Colors.black),
leading: BorderSide(color: Colors.black)));
return TableSpan(
extent: const FixedTableSpanExtent(100),
backgroundDecoration: decoration);
}
TableSpanDecoration
中的 color
属性用于设置 TableSpan
的颜色。
运行代码,输出将如下所示
因为
- 在
TableView
中,装饰按特定顺序绘制。 - 首先绘制
mainAxis
的backgroundDecoration
(作为主轴的行或列)。 - 然后,绘制其他轴上的装饰。
- 接下来,绘制跨度内的各个单元格内容。
- 最后,任何指定的
foregroundDecoration
都会绘制在内容之上。
这里默认的轴是 Axis.vertical
,所以,先绘制列,然后绘制行,边框与行装饰重叠,所以,让我们为前景装饰添加边框。
dart
TableSpan buildTableSpan(int index) {
TableSpanDecoration foreGroundDecoration = const TableSpanDecoration(
border: TableSpanBorder(
trailing: BorderSide(color: Colors.black),
leading: BorderSide(color: Colors.black)));
TableSpanDecoration backGroundDecoration = TableSpanDecoration(
color: index == 0 ? Colors.grey[300] : null,
);
return TableSpan(
extent: const FixedTableSpanExtent(100),
backgroundDecoration: backGroundDecoration,
foregroundDecoration: foreGroundDecoration);
}
在前景装饰中添加边框可确保其呈现在 TableView
中单元格内容的顶部。
TableSpan.extent
它表示行的高度和列的宽度,TableSpanExtent
有四种类型。
1.FixedTableSpanExtent
具有固定[像素]的跨度。
dart
extent: const FixedTableSpanExtent(50),
2.FractionTableSpanExtent
它将跨度范围指定为视口范围的一部分。它与 Expanded
小部件相同,根据提供的分数占用空间。
dart
extent: const FractionalTableSpanExtent(0.5),
3.RemainingTableSpanExtent
它指定跨度应占据视口中的剩余空间。
dart
TableSpan buildColumnSpan(int index) {
TableSpanDecoration decoration = const TableSpanDecoration(
border: TableSpanBorder(
trailing: BorderSide(color: Colors.black),
leading: BorderSide(color: Colors.black)));
return TableSpan(
extent: index==0? FixedTableSpanExtent(100):RemainingTableSpanExtent(), backgroundDecoration: decoration);
}
4.CombiningTableSpanExtent
它以两个范围作为参数,并通过组合器函数运行这两个范围的结果。
dart
TableSpan buildRowSpan(int index) {
TableSpanExtent extent1 = FixedTableSpanExtent(100);
TableSpanExtent extent2 = FixedTableSpanExtent(100);
double combiner(double value1, double value2) {
return value1 + value2;
}
TableSpanDecoration foreGroundDecoration = const TableSpanDecoration(
border: TableSpanBorder(
trailing: BorderSide(color: Colors.black),
leading: BorderSide(color: Colors.black)));
TableSpanDecoration backGroundDecoration = TableSpanDecoration(
color: index == 0 ? Colors.grey[300] : null,
);
if (index == 1) {
return TableSpan(
extent: CombiningTableSpanExtent(extent1, extent2, combiner),
backgroundDecoration: backGroundDecoration,
foregroundDecoration: foreGroundDecoration);
}
return TableSpan(
extent: const FixedTableSpanExtent(100),
backgroundDecoration: backGroundDecoration,
foregroundDecoration: foreGroundDecoration);
}
当鼠标指针(按下或不按下按钮)进入此跨度描述的行或列时触发。
dart
void Function(PointerEnterEvent)? onEnter
void Function(PointerExitEvent)? onExit
当鼠标指针(按下或未按下按钮)退出此跨度描述的行或列时,它会触发。
dart
void Function(PointerExitEvent)? onExit
recognizerFactories
dart
recognizerFactories: <Type, GestureRecognizerFactory>{
TapGestureRecognizer:
GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
() => TapGestureRecognizer(),
(TapGestureRecognizer t) {
t.onTapDown=(TapDownDetails tapdownDetails){
print(tapdownDetails.localPosition);
};
t.onTapUp=(TapUpDetails tapupDetails){
print(tapupDetails.localPosition);
};
}
),
},
recognizerFactories
是一个映射,其中键是手势类型,值是 GestureRecognizerFactory 的实例。
GestureRecognizerFactoryWithHandlers
有两个参数。调用时返回 TapGestureRecognizer
实例的函数和初始化 TapGestureRecognizer
并设置事件处理程序以处理特定事件详细信息的回调函数。
添加填充
padding
属性用于为每行和每列添���填充。
dart
padding: TableSpanPadding(leading: 10),
向 UI 添加更多列和行
让我们在 TableView 中显示来自 Employee
类的更多数据,例如 email
和 role
。
dart
Widget addText(TableVicinity vicinity) {
if (vicinity.yIndex == 0 && vicinity.xIndex == 0) {
return const Text("Index");
} else if (vicinity.yIndex == 0 && vicinity.xIndex == 1) {
return const Text("name");
} else if (vicinity.yIndex == 0 && vicinity.xIndex == 2) {
return const Text("Email");
} else if (vicinity.yIndex == 0 && vicinity.xIndex == 3) {
return const Text("Role");
} else if (vicinity.xIndex == 0) {
return Text(employees[vicinity.yIndex - 1].id);
} else if (vicinity.xIndex == 1) {
return Text(employees[vicinity.yIndex - 1].name);
} else if (vicinity.xIndex == 2) {
return Text(employees[vicinity.yIndex - 1].email);
} else if (vicinity.xIndex == 3) {
return Text(employees[vicinity.yIndex - 1].role);
}
return Text("");
}
...
body: TableView.builder(
mainAxis: Axis.horizontal,
columnCount: 4,
rowCount: 21,
columnBuilder: buildColumnSpan,
rowBuilder: buildTableSpan,
cellBuilder: (BuildContext context, TableVicinity vicinity) {
return Center(child: addText(vicinity));
}),
...
TableSpan buildColumnSpan(int index) {
TableSpanDecoration decoration = const TableSpanDecoration(
border: TableSpanBorder(
trailing: BorderSide(color: Colors.black),
leading: BorderSide(color: Colors.black)));
if (index == 2) {
return TableSpan(
extent: const RemainingTableSpanExtent(),
backgroundDecoration: decoration,
);
} else if (index == 3) {
return TableSpan(
extent: const FractionalTableSpanExtent(0.5),
backgroundDecoration: decoration,
);
}
return TableSpan(
extent: FixedTableSpanExtent(100), backgroundDecoration: decoration);
}
输出是
固定行和列
固定持续出现在 TableView 视口边缘的特定数量的行和列。
dart
TableView.builder(
...
pinnedRowCount: 1,
pinnedColumnCount: 1,
),
滚动细节
ScrollableDetail
允许对小部件的垂直和水平滚动行为进行特定配置。
dart
verticalDetails: ScrollableDetails.vertical(
reverse: true,
controller: verticalController,
physics: const AlwaysScrollableScrollPhysics(),
decorationClipBehavior: Clip.hardEdge
),
ScrollableDetails
中的 verticalDetails
允许对小部件的垂直滚动行为进行特定配置,它封装了各种属性。
dart
verticalDetails: ScrollableDetails.vertical(
reverse: true,
controller: verticalController,
physics: const AlwaysScrollableScrollPhysics(),
decorationClipBehavior: Clip.hardEdge,
)
以下是属性的详细说明:
reverse
:当设置为true
时,小部件内的内容向相反方向滚动。controller
:指定用于管理和控制滚动行为的ScrollController
。它允许您滚动到特定位置或收听滚动偏移量的变化。physics
:确定滚动物理的行为。decorationClipBehavior
:指定可滚动区域装饰的剪切行为
cacheExtent 缓存范围
dart
cacheExtent: 200,
与ListView类似, cacheExtent
是在进入屏幕可见部分之前绘制的区域的大小。
DiagonalDragBehavior 对角线拖动行为
该枚举允许开发人员指定如何使用 kTouchSlop
处理对角滚动。
结论
在本教程中,我们学习了如何使用 Flutter 中的 two_dimensional_scrollable
实现 Flutter 中的 tableView,我们可以用它做更多的事情并使用它添加更多功能。这是我对 Flutter 中 TableView 的一个小介绍。