Flutter可扩展表格插件flutter_expandable_table的使用

发布于 1周前 作者 caililin 来自 Flutter

Flutter可扩展表格插件flutter_expandable_table的使用

Expandable Table

GitHub Pub Package Pub Points Pub Likes Package Issue Package License

ExpandableTable 是一个Flutter小部件,用于创建具有固定表头和第一列的表格。您可以创建嵌套的行/列,并将它们分组为可扩展的行/列。

ExpandableTable
Image

Features

  • 表头和第一列固定
  • 支持垂直和水平滚动
  • 可定制动画时长和曲线
  • 为每一行定义特定高度
  • 为每一列定义特定宽度
  • 构建单元格内容时访问单元格地址
  • 构建单元格内容时访问父行和列

Usage

Installation

pubspec.yaml 文件中添加以下依赖:

dependencies:
  flutter_expandable_table: <last-release>

Basic Setup

完整的示例代码可以在 GitHub 上找到。

return ExpandableTable(
  firstHeaderCell: ExpandableTableCell(
    child: Text('Simple\nTable'),
  ),
  headers: headers,
  rows: rows,
);

Use with the Controller

您可以创建一个外部控制器来动态管理表格,例如在其中添加或删除行。

//... Inside Widget State
late ExpandableTableController controller;

@override
void initState() {
  controller = ExpandableTableController(
    firstHeaderCell: ExpandableTableCell(child: Container()),
    headers: [],
    rows: [],
    headerHeight: 263,
    defaultsColumnWidth: 200,
    firstColumnWidth: 300,
    scrollShadowColor: AppColors.black,
  );
  super.initState();
}

void _onEvent(){    
  controller.rows.add(...your code...);
}

@Override
Widget build(BuildContext context) {
  return ExpandableTable(
    controller: controller,
  );
}

ExpandableTable Properties

  • firstHeaderCell: 左上角单元格,即第一个表头单元格。
  • headers: 包含所有列标题的列表,每个标题可以包含进一步的标题列表,允许创建嵌套和可扩展的列。
  • rows: 包含表格所有行的列表,每行可以包含进一步的行列表,允许创建嵌套和可扩展的行。
  • headerHeight: 每个列标题的高度,即第一行的高度。
  • firstColumnWidth: 第一列的宽度。
  • defaultsColumnWidth: 所有列的默认宽度,可以为每个单独的列重新定义。
  • defaultsRowHeight: 所有行的默认高度,可以为每个单独的行重新定义。
  • duration: 行/列扩展渲染动画的持续时间。
  • curve: 行/列扩展渲染动画的曲线。
  • scrollShadowDuration: 阴影动画的持续时间。
  • scrollShadowFadeInCurve: 阴影出现动画的曲线。
  • scrollShadowFadeOutCurve: 阴影消失动画的曲线。
  • scrollShadowColor: 阴影的颜色。
  • scrollShadowSize: 阴影的大小。
  • visibleScrollbar: 水平和垂直滚动条的可见性。
  • trackVisibilityScrollbar: 滚动条轨道是否可见。
  • thumbVisibilityScrollbar: 滚动条拇指是否可见,即使没有进行滚动操作。
  • expanded: 表格是否扩展,以填充水平和垂直轴上的可用空间。

示例代码

以下是使用 flutter_expandable_table 的完整示例代码:

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

// ignore: depend_on_referenced_packages
import 'package:flutter_expandable_table/flutter_expandable_table.dart';

const Color _primaryColor = Color(0xFF1e2f36); //corner
const Color _accentColor = Color(0xFF0d2026); //background
const TextStyle _textStyle = TextStyle(color: Colors.white);

void main() => runApp(const _MyApp());

class _MyApp extends StatelessWidget {
  const _MyApp();

  @override
  Widget build(BuildContext context) => MaterialApp(
        title: 'ExpandableTable Example',
        theme: ThemeData(primarySwatch: Colors.grey),
        home: const _MyHomePage(),
        scrollBehavior: _AppCustomScrollBehavior(),
      );
}

class _DefaultCellCard extends StatelessWidget {
  final Widget child;

  const _DefaultCellCard({
    required this.child,
  });

  @override
  Widget build(BuildContext context) => Container(
        color: _primaryColor,
        margin: const EdgeInsets.all(1),
        child: child,
      );
}

class _MyHomePage extends StatefulWidget {
  const _MyHomePage();

  @override
  State<_MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<_MyHomePage> {
  ExpandableTableCell _buildCell(String content, {CellBuilder? builder}) =>
      ExpandableTableCell(
        child: builder != null
            ? null
            : _DefaultCellCard(
                child: Center(
                  child: Text(
                    content,
                    style: _textStyle,
                  ),
                ),
              ),
        builder: builder,
      );

  ExpandableTableCell _buildFirstRowCell() => ExpandableTableCell(
        builder: (context, details) => _DefaultCellCard(
          child: Padding(
            padding: const EdgeInsets.only(left: 16.0),
            child: Row(
              children: [
                SizedBox(
                  width: 24 * details.row!.address.length.toDouble(),
                  child: details.row?.children != null
                      ? Align(
                          alignment: Alignment.centerRight,
                          child: AnimatedRotation(
                            duration: const Duration(milliseconds: 500),
                            turns: details.row?.childrenExpanded == true
                                ? 0.25
                                : 0,
                            child: const Icon(
                              Icons.keyboard_arrow_right,
                              color: Colors.white,
                            ),
                          ),
                        )
                      : null,
                ),
                Text(
                  '${details.row!.address.length > 1 ? details.row!.address.skip(1).map((e) => 'Sub ').join() : ''}Row ${details.row!.address.last}',
                  style: _textStyle,
                ),
              ],
            ),
          ),
        ),
      );

  ExpandableTable _buildSimpleTable() {
    const int columnsCount = 20;
    const int rowsCount = 20;
    // 创建表头
    final List<ExpandableTableHeader> headers = List.generate(
      columnsCount - 1,
      (index) => ExpandableTableHeader(
        width: index % 2 == 0 ? 200 : 150,
        cell: _buildCell('Column $index'),
      ),
    );
    // 创建行
    final List<ExpandableTableRow> rows = List.generate(
      rowsCount,
      (rowIndex) => ExpandableTableRow(
        height: rowIndex % 2 == 0 ? 50 : 70,
        firstCell: _buildCell('Row $rowIndex'),
        cells: List<ExpandableTableCell>.generate(
          columnsCount - 1,
          (columnIndex) => _buildCell('Cell $rowIndex:$columnIndex'),
        ),
      ),
    );

    return ExpandableTable(
      firstHeaderCell: _buildCell('Simple\nTable'),
      headers: headers,
      scrollShadowColor: _accentColor,
      rows: rows,
      visibleScrollbar: true,
      trackVisibilityScrollbar: true,
      thumbVisibilityScrollbar: true,
    );
  }

  static const int columnsCount = 20;
  static const int subColumnsCount = 2;
  static const int rowsCount = 6;
  static const int subRowsCount = 3;
  static const int totalColumns = columnsCount + subColumnsCount;

  List<ExpandableTableRow> _generateRows(int quantity, {int depth = 0}) {
    final bool generateLegendRow = (depth == 0 || depth == 2);
    return List.generate(
      quantity,
      (rowIndex) => ExpandableTableRow(
        firstCell: _buildFirstRowCell(),
        children: ((rowIndex == 3 || rowIndex == 2) && depth < 3)
            ? _generateRows(subRowsCount, depth: depth + 1)
            : null,
        cells: !(generateLegendRow && (rowIndex == 3 || rowIndex == 2))
            ? List<ExpandableTableCell>.generate(
                totalColumns,
                (columnIndex) => _buildCell('Cell $rowIndex:$columnIndex'),
              )
            : null,
        legend: generateLegendRow && (rowIndex == 3 || rowIndex == 2)
            ? const _DefaultCellCard(
                child: Align(
                  alignment: FractionalOffset.centerLeft,
                  child: Padding(
                    padding: EdgeInsets.only(left: 24.0),
                    child: Text(
                      'This is row legend',
                      style: _textStyle,
                    ),
                  ),
                ),
              )
            : null,
      ),
    );
  }

  ExpandableTable _buildExpandableTable() {
    // 创建子表头
    final List<ExpandableTableHeader> subHeader = List.generate(
      subColumnsCount,
      (index) => ExpandableTableHeader(
        cell: _buildCell('Sub Column $index'),
      ),
    );

    // 创建表头
    final List<ExpandableTableHeader> headers = List.generate(
      columnsCount,
      (index) => ExpandableTableHeader(
          cell: _buildCell(
              '${index == 1 ? 'Expandable\nColumn' : 'Column'} $index'),
          children: index == 1 ? subHeader : null),
    );

    return ExpandableTable(
      firstHeaderCell: _buildCell('Expandable\nTable'),
      rows: _generateRows(rowsCount),
      headers: headers,
      defaultsRowHeight: 60,
      defaultsColumnWidth: 150,
      firstColumnWidth: 250,
      scrollShadowColor: _accentColor,
      visibleScrollbar: true,
      expanded: false,
    );
  }

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          title: const Text(
              '   Simple Table                    |                    Expandable Table'),
          centerTitle: true,
        ),
        body: Container(
          color: _accentColor,
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Expanded(
                child: Padding(
                  padding: const EdgeInsets.all(20.0),
                  child: _buildSimpleTable(),
                ),
              ),
              Expanded(
                child: Padding(
                  padding: const EdgeInsets.all(20.0),
                  child: _buildExpandableTable(),
                ),
              ),
            ],
          ),
        ),
      );
}

class _AppCustomScrollBehavior extends MaterialScrollBehavior {
  @override
  Set<PointerDeviceKind> get dragDevices => {
        PointerDeviceKind.touch,
        PointerDeviceKind.mouse,
        PointerDeviceKind.trackpad,
      };
}

通过以上代码,您可以创建一个简单的可扩展表格,并根据需要自定义其外观和行为。希望这些信息对您有所帮助!


更多关于Flutter可扩展表格插件flutter_expandable_table的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter可扩展表格插件flutter_expandable_table的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter中使用flutter_expandable_table插件来实现可扩展表格的示例代码。

首先,你需要在你的pubspec.yaml文件中添加flutter_expandable_table依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_expandable_table: ^latest_version  # 请替换为实际的最新版本号

然后运行flutter pub get来安装依赖。

接下来是一个完整的示例代码,展示如何使用flutter_expandable_table来创建一个可扩展的表格:

import 'package:flutter/material.dart';
import 'package:flutter_expandable_table/flutter_expandable_table.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Expandable Table Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ExpandableTableScreen(),
    );
  }
}

class ExpandableTableScreen extends StatefulWidget {
  @override
  _ExpandableTableScreenState createState() => _ExpandableTableScreenState();
}

class _ExpandableTableScreenState extends State<ExpandableTableScreen> {
  final List<Map<String, dynamic>> tableData = [
    {
      'header': 'Header 1',
      'rows': [
        {'cell1': 'Row 1, Cell 1', 'cell2': 'Row 1, Cell 2'},
        {'cell1': 'Row 2, Cell 1', 'cell2': 'Row 2, Cell 2'},
      ],
    },
    {
      'header': 'Header 2',
      'rows': [
        {'cell1': 'Row 1, Cell 1', 'cell2': 'Row 1, Cell 2'},
        {'cell1': 'Row 2, Cell 1', 'cell2': 'Row 2, Cell 2'},
        {'cell1': 'Row 3, Cell 1', 'cell2': 'Row 3, Cell 2'},
      ],
    },
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Expandable Table Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: ExpandableTable(
          tableData: tableData,
          headerStyle: TextStyle(
            fontSize: 20,
            fontWeight: FontWeight.bold,
          ),
          cellStyle: TextStyle(
            fontSize: 16,
          ),
          onRowTap: (index, rowIndex) {
            print("Header index: $index, Row index: $rowIndex");
          },
        ),
      ),
    );
  }
}

在这个示例中:

  1. 依赖安装:我们在pubspec.yaml文件中添加了flutter_expandable_table依赖。
  2. 数据结构tableData是一个包含表头和对应行的列表。每个表头映射到一个包含多个行的列表,每个行又是一个包含单元格数据的映射。
  3. UI构建
    • 使用Scaffold构建应用的主界面。
    • 使用AppBar添加一个应用栏。
    • 使用Padding为内容添加一些内边距。
    • 使用ExpandableTable构建可扩展表格,并传入tableData作为数据源。
    • headerStylecellStyle用于定义表头和单元格的文本样式。
    • onRowTap是一个回调函数,当用户点击某一行时会触发,这里简单地打印了表头和行的索引。

这个示例展示了如何使用flutter_expandable_table插件来创建一个简单的可扩展表格,并处理行的点击事件。你可以根据需要进一步自定义和扩展这个示例。

回到顶部