//import 'dart:math' as math; import 'package:flutter/material.dart'; import './reorderable_flex.dart'; import './tabluar_flex.dart'; import './typedefs.dart'; import '../rendering/tabluar_flex.dart'; class ReorderableTableRow extends TabluarRow { ReorderableTableRow({ MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, MainAxisSize mainAxisSize = MainAxisSize.max, CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, TextDirection? textDirection, VerticalDirection verticalDirection = VerticalDirection.down, TextBaseline? textBaseline, List children = const [], Decoration? decoration, Key? key, }) : super( children: children, mainAxisAlignment: mainAxisAlignment, mainAxisSize: mainAxisSize, crossAxisAlignment: crossAxisAlignment, textDirection: textDirection, verticalDirection: verticalDirection, textBaseline: textBaseline, decoration: decoration, key: key, ); } typedef DecorateDraggableFeedback = Widget Function( BuildContext feedbackContext, Widget draggableFeedback); /// Reorderable (drag and drop) version of [Table], a widget that displays its /// children in a two-dimensional grid. /// /// The difference between table and list is that cells in a table are /// horizontally aligned, whereas in a list, each item can have children but /// they are not aligned with children in another item. /// Making a row draggable requires cells to be contained in a single widget. /// This isn't achievable with [Table] or [GridView] widget since their /// children are laid out as cells of widget instead of rows of widget. /// /// Cells of each row must be children of [ReorderableTableRow] and each /// [ReorderableTableRow] must have a key. /// /// See also: /// /// * [ReorderableTableRow], which is the container of a row of cells. /// * [Table], which uses the table layout algorithm for its children. class ReorderableTable extends StatelessWidget { /// Creates a reorderable table. /// /// The [children], [defaultColumnWidth], and [defaultVerticalAlignment] /// arguments must not be null. ReorderableTable({ required this.onReorder, this.children = const [], this.columnWidths, this.defaultColumnWidth = const FlexColumnWidth(1.0), this.textDirection, this.border, this.defaultVerticalAlignment = TableCellVerticalAlignment.top, this.textBaseline, this.header, this.footer, this.decorateDraggableFeedback, this.onNoReorder, this.reorderAnimationDuration, this.scrollAnimationDuration, this.ignorePrimaryScrollController = false, this.needsLongPressDraggable = true, Key? key, this.borderColor, }) : assert(() { if (children.any((ReorderableTableRow row1) => row1.key != null && children.any((ReorderableTableRow row2) => row1 != row2 && row1.key == row2.key))) { throw FlutterError( 'Two or more ReorderableTableRow children of this Table had the same key.\n' 'All the keyed ReorderableTableRow children of a Table must have different Keys.'); } return true; }()), // assert(() { // if (children.isNotEmpty) { // final int cellCount = children.first.children.length; // if (children.any((ReorderableTableRow row) => row.children.length != cellCount)) { // throw FlutterError( // 'Table contains irregular row lengths.\n' // 'Every ReorderableTableRow in a Table must have the same number of children, so that every cell is filled. ' // 'Otherwise, the table will contain holes.' // ); // } // } // return true; // }()), super(key: key) { assert(() { final List flatChildren = children .expand((ReorderableTableRow row) => row.children) .toList(growable: false); if (debugChildrenHaveDuplicateKeys(this, flatChildren)) { throw FlutterError( 'Two or more cells in this Table contain widgets with the same key.\n' 'Every widget child of every TableRow in a Table must have different keys. The cells of a Table are ' 'flattened out for processing, so separate cells cannot have duplicate keys even if they are in ' 'different rows.'); } return true; }()); } /// The rows of the table. /// /// Every row in a table must have the same number of children, and all the /// children must be non-null. final List children; /// How the horizontal extents of the columns of this table should be determined. /// /// If the [Map] has a null entry for a given column, the table uses the /// [defaultColumnWidth] instead. By default, that uses flex sizing to /// distribute free space equally among the columns. /// /// The [FixedColumnWidth] class can be used to specify a specific width in /// pixels. That is the cheapest way to size a table's columns. /// /// The layout performance of the table depends critically on which column /// sizing algorithms are used here. In particular, [IntrinsicColumnWidth] is /// quite expensive because it needs to measure each cell in the column to /// determine the intrinsic size of the column. final Map? columnWidths; /// How to determine with widths of columns that don't have an explicit sizing algorithm. /// /// Specifically, the [defaultColumnWidth] is used for column `i` if /// `columnWidths[i]` is null. final TableColumnWidth defaultColumnWidth; /// The direction in which the columns are ordered. /// /// Defaults to the ambient [Directionality]. final TextDirection? textDirection; /// The style to use when painting the boundary and interior divisions of the table. final TableBorder? border; final Color? borderColor; /// How cells that do not explicitly specify a vertical alignment are aligned vertically. final TableCellVerticalAlignment defaultVerticalAlignment; /// The text baseline to use when aligning rows using [TableCellVerticalAlignment.baseline]. final TextBaseline? textBaseline; /// Non-reorderable widget at top of the table. Cells in [header] also affects /// alignment of columns. final Widget? header; /// Non-reorderable widget at top of the table. Cells in [footer] also affects /// alignment of columns. final Widget? footer; /// Called when a child is dropped into a new position to shuffle the /// children. final ReorderCallback onReorder; final NoReorderCallback? onNoReorder; final DecorateDraggableFeedback? decorateDraggableFeedback; final Duration? reorderAnimationDuration; final Duration? scrollAnimationDuration; final bool ignorePrimaryScrollController; final bool needsLongPressDraggable; @override Widget build(BuildContext context) { // return TabluarColumn( // mainAxisSize: MainAxisSize.min, // children: itemRows + // [ // ReorderableTableRow(key: ValueKey(1), mainAxisSize:MainAxisSize.min, children: [Text('111111111111'), Text('222')]), // ReorderableTableRow(key: ValueKey(2), mainAxisSize:MainAxisSize.min, children: [Text('33'), Text('4444444444')]) // ], // ) final GlobalKey tableKey = GlobalKey(debugLabel: '$ReorderableTable table key'); return ReorderableFlex( header: header, footer: footer, children: children, onReorder: onReorder, onNoReorder: onNoReorder, needsLongPressDraggable: needsLongPressDraggable, direction: Axis.vertical, buildItemsContainer: (BuildContext containerContext, Axis direction, List children) { List mapped = borderColor == null ? children : children.map( (child) { final index = children.indexOf(child); if (index > 0) { return Container( decoration: BoxDecoration( border: Border.symmetric( horizontal: BorderSide( color: borderColor!, width: index != children.length - 1 ? 0.5 : 1), vertical: BorderSide(color: borderColor!), ), ), child: child); } return child; }, ).toList(); return TabluarFlex( key: tableKey, direction: direction, // mainAxisAlignment: mainAxisAlignment, // mainAxisSize: MainAxisSize.min, // crossAxisAlignment: crossAxisAlignment, textDirection: textDirection, // verticalDirection: verticalDirection, textBaseline: textBaseline, children: mapped); }, buildDraggableFeedback: (BuildContext feedbackContext, BoxConstraints constraints, Widget child) { // The child is a ReorderableTableRow because children is a List ReorderableTableRow tableRow = child as ReorderableTableRow; RenderTabluarFlex renderTabluarFlex = tableKey.currentContext!.findRenderObject() as RenderTabluarFlex; int grandChildIndex = 0; for (; grandChildIndex < tableRow.children.length; grandChildIndex++) { tableRow.children[grandChildIndex] = ConstrainedBox( constraints: BoxConstraints( minWidth: renderTabluarFlex .maxGrandchildrenCrossSize[grandChildIndex]!), child: tableRow.children[grandChildIndex]); } for (; grandChildIndex < renderTabluarFlex.maxGrandchildrenCrossSize.length; grandChildIndex++) { tableRow.children.add(ConstrainedBox( constraints: BoxConstraints( minWidth: renderTabluarFlex .maxGrandchildrenCrossSize[grandChildIndex]!), )); } ConstrainedBox constrainedTableRow = ConstrainedBox(constraints: constraints, child: tableRow); return Transform( transform: new Matrix4.rotationZ(0), alignment: FractionalOffset.topLeft, child: Material( // child: Card(child: ConstrainedBox(constraints: constraints, child: tableRow)), child: (decorateDraggableFeedback ?? defaultDecorateDraggableFeedback)( feedbackContext, constrainedTableRow), elevation: 6.0, color: Colors.transparent, borderRadius: BorderRadius.zero, ), ); }, reorderAnimationDuration: reorderAnimationDuration, scrollAnimationDuration: scrollAnimationDuration, ignorePrimaryScrollController: ignorePrimaryScrollController); } Widget defaultDecorateDraggableFeedback( BuildContext feedbackContext, Widget draggableFeedback) => Card(child: draggableFeedback); }