Flutter PaginatedDataTable 组件的一些样式设置 及 上移/下移数据行

882 阅读2分钟

Flutter PaginatedDataTable 的一些样式设置 及 上移/下移行

PaginatedDataTable 组件

Flutter 的 PaginatedDataTable 组件是在 DataTable 组件上又包装了一些分页的功能。

该示例实现了下面几个点:

  • 表头背景色
  • 表头文字样式
  • 行分隔线颜色
  • 鼠标悬停时数据行颜色(现在还有问题,当设置了数据行颜色时,悬停时颜色不起作用)
  • 数据行选中时的颜色
  • 选中数据行的上移/下移
  • 分页箭头颜色

datatable.gif

Don't talk! Show me the code!

数据行的类

data_item.dart

class DataItem {
  final String text;
  final bool selected;
  const DataItem({this.text = '', this.selected = false});
}

数据源

data_source.dart

import 'package:flutter/material.dart';

import 'data_item.dart';

class DataSource extends DataTableSource {
  final List<DataItem> list;
  final Function(int index, bool isSeleted) selectChanged;

  DataSource({required this.list, required this.selectChanged});

  @override
  DataRow getRow(int index) {
    return DataRow.byIndex(
      index: index,
      selected: list[index].selected,
      onSelectChanged: (isSelected) {
        // 处理
        selectChanged(index, isSelected ?? false);
      },
      cells: [
        DataCell(
          SizedBox(
            child: Text(index.toString()),
          ),
        ),
        DataCell(
          SizedBox(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Container(
                  height: 24.0,
                  alignment: Alignment.centerLeft,
                  child: Text('hello_${list[index].text}'),
                ),
                Container(
                  height: 20.0,
                  alignment: Alignment.centerLeft,
                  child: Text('world_${list[index].text}'),
                ),
              ],
            ),
          ),
        ),
        DataCell(
          SizedBox(
            child: OutlinedButton(
              onPressed: () {
                // 处理
                debugPrint('Hello, world');
              },
              child: const Text('go'),
            ),
          ),
        ),
      ],
    );
  }

  @override
  bool get isRowCountApproximate => false;

  @override
  int get rowCount => list.length;

  @override
  int get selectedRowCount => 0;
}

主程序

main.dart

import 'package:flutter/material.dart';
import 'package:untitled/data_item.dart';

import 'data_source.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

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

class _MyHomePageState extends State<HomePage> {
  // 分页DataTable的 Key,用于改变 分页DataTable的状态
  final keyDataTable = GlobalKey<PaginatedDataTableState>();
  // 数据
  late List<DataItem> list = [];
  // 选中行
  int selectedIndex = -1;

  final int totalRowCount = 21;

  @override
  void initState() {
    super.initState();
    list = dataItemList();
  }

  // 原始数据
  List<DataItem> dataItemList() {
    final List<DataItem> items = [];
    for (int i = 0; i < totalRowCount; i++) {
      items.add(DataItem(text: i.toString()));
    }
    return items;
  }

  // 选中/反选 某一行
  void selectChanged(int index, bool isSeleted) {
    final List<DataItem> items = [];
    for (int i = 0; i < totalRowCount; i++) {
      bool selected = false;
      if (i == index && isSeleted) {
        selected = true;
      }
      items.add(DataItem(text: list[i].text, selected: selected));
    }

    selectedIndex = index;

    setState(() {
      list = items;
    });
  }

  // 上移选中行
  void up(int index) {
    // 当前选中行
    DataItem selectedItem = list[index];

    // 上一行
    DataItem? item;
    if (index - 1 >= 0) {
      item = list[index - 1];
    }

    final List<DataItem> items = [];
    for (int i = 0; i < totalRowCount; i++) {
      if (i == index - 1) {
        // 当前选中行放到上一行
        items.add(selectedItem);
      } else if (i == index && item != null) {
        // 上一行放到当前选中行
        items.add(item);
      } else {
        // 其它行直接添加
        items.add(list[i]);
      }
    }

    if (index - 1 >= 0) {
      // 修改选中行
      selectedIndex = index - 1;
      // 跳转到选中行(主要用于翻页时自动跳转到对应页)
      keyDataTable.currentState!.pageTo(selectedIndex);
    }

    // 改变绑定的数据
    setState(() {
      list = items;
    });
  }

  // 下移选中行
  void down(int index) {
    // 当前选中行
    DataItem selectedItem = list[index];

    // 下一行
    DataItem? item;
    if (index + 1 < list.length) {
      item = list[index + 1];
    }

    final List<DataItem> items = [];
    for (int i = 0; i < 21; i++) {
      if (i == index + 1) {
        // 当前选中行放到下一行
        items.add(selectedItem);
      } else if (i == index && item != null) {
        // 下一行放到当前选中行
        items.add(item);
      } else {
        // 其它行直接添加
        items.add(list[i]);
      }
    }

    if (index + 1 < list.length) {
      // 修改选中行
      selectedIndex = index + 1;
      // 跳转到选中行(主要用于翻页时自动跳转到对应页)
      keyDataTable.currentState!.pageTo(selectedIndex);
    }

    // 改变绑定的数据
    setState(() {
      list = items;
    });
  }

  @override
  Widget build(BuildContext context) {
    final double width = MediaQuery.of(context).size.width;

    return Scaffold(
      appBar: AppBar(
        title: const Text('可分页的 DataTable'),
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            Row(
              children: [
                // 下移选中行
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: OutlinedButton(
                    child: const Icon(Icons.arrow_downward),
                    onPressed: () {
                      down(selectedIndex);
                    },
                  ),
                ),
                // 上移选中行
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: OutlinedButton(
                    child: const Icon(Icons.arrow_upward),
                    onPressed: () {
                      up(selectedIndex);
                    },
                  ),
                ),
              ],
            ),
            Theme(
              data: Theme.of(context).copyWith(
                // DataTable 行分隔线的颜色
                dividerColor: Colors.red,
              ),
              child: DataTableTheme(
                data: DataTableTheme.of(context).copyWith(
                  // 表头背景色
                  headingRowColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
                    return Theme.of(context).primaryColorDark; //
                  }),
                  // 表头文字样式(颜色等)
                  headingTextStyle: const TextStyle(color: Colors.orangeAccent),
                  dataRowColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
                    // 点击、获得焦点、选中时的状态
                    const Set<MaterialState> stateA = <MaterialState>{
                      MaterialState.pressed,
                      MaterialState.focused,
                      MaterialState.selected,
                    };
                    // 点击、获得焦点、选中时的颜色
                    if (states.any(stateA.contains)) {
                      return Colors.lightBlue;
                    }

                    // 鼠标经过时的状态
                    const Set<MaterialState> stateB = <MaterialState>{
                      MaterialState.hovered,
                    };
                    // 鼠标经过时的颜色
                    if (states.any(stateB.contains)) {
                      return Colors.cyan;
                    }

                    // 数据行的颜色,这里如果设置颜色的话,上面的鼠标经过则不起作用。
                    return Colors.transparent;
                  }),
                ),
                child: PaginatedDataTable(
                  // 用于代码控制分页DataTable的状态
                  key: keyDataTable,
                  horizontalMargin: 8,
                  columnSpacing: 8,
                  dataRowHeight: 48,
                  showCheckboxColumn: false,
                  // 分页箭头颜色(第一页的向左箭头和最后一页的向右箭头无法设置灰度)
                  arrowHeadColor: Colors.green,
                  rowsPerPage: 10,
                  source: DataSource(list: list, selectChanged: selectChanged),
                  columns: [
                    DataColumn(
                      label: Container(
                        constraints: BoxConstraints(minWidth: width * 0.1),
                        child: const Text('行号'),
                      ),
                    ),
                    DataColumn(
                      label: Container(
                        // alignment: Alignment.center,
                        constraints: BoxConstraints(minWidth: width * 0.75),
                        child: const Text('内容'),
                      ),
                    ),
                    DataColumn(
                      label: Container(
                        alignment: Alignment.center,
                        constraints: BoxConstraints(minWidth: width * 0.05),
                        child: const Text('处理'),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}