mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: update board layout, support footer and header
This commit is contained in:
parent
a4b4b20cfc
commit
dbc5de2968
@ -46,9 +46,22 @@ class _MultiBoardListExampleState extends State<MultiBoardListExample> {
|
|||||||
return Board(
|
return Board(
|
||||||
dataController: boardData,
|
dataController: boardData,
|
||||||
background: Container(color: Colors.red),
|
background: Container(color: Colors.red),
|
||||||
builder: (context, item) {
|
footBuilder: (context, columnData) {
|
||||||
|
return Container(
|
||||||
|
color: Colors.purple,
|
||||||
|
height: 30,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
headerBuilder: (context, columnData) {
|
||||||
|
return Container(
|
||||||
|
color: Colors.yellow,
|
||||||
|
height: 30,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cardBuilder: (context, item) {
|
||||||
return _RowWidget(item: item as TextItem, key: ObjectKey(item));
|
return _RowWidget(item: item as TextItem, key: ObjectKey(item));
|
||||||
},
|
},
|
||||||
|
columnConstraints: const BoxConstraints.tightFor(width: 240),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ class _SingleBoardListExampleState extends State<SingleBoardListExample> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Board(
|
return Board(
|
||||||
dataController: boardData,
|
dataController: boardData,
|
||||||
builder: (context, item) {
|
cardBuilder: (context, item) {
|
||||||
return _RowWidget(item: item as TextItem, key: ObjectKey(item));
|
return _RowWidget(item: item as TextItem, key: ObjectKey(item));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -13,8 +13,7 @@ class BoardOverlayEntry {
|
|||||||
/// Whether this entry occludes the entire overlay.
|
/// Whether this entry occludes the entire overlay.
|
||||||
///
|
///
|
||||||
/// If an entry claims to be opaque, then, for efficiency, the overlay will
|
/// If an entry claims to be opaque, then, for efficiency, the overlay will
|
||||||
/// skip building entries below that entry unless they have [maintainState]
|
/// skip building entries below that entry.
|
||||||
/// set.
|
|
||||||
bool get opaque => _opaque;
|
bool get opaque => _opaque;
|
||||||
bool _opaque;
|
bool _opaque;
|
||||||
|
|
||||||
|
@ -20,25 +20,37 @@ class Board extends StatelessWidget {
|
|||||||
/// Defaults to 0.0.
|
/// Defaults to 0.0.
|
||||||
final double runSpacing;
|
final double runSpacing;
|
||||||
|
|
||||||
|
///
|
||||||
final Widget? background;
|
final Widget? background;
|
||||||
|
|
||||||
final BoardColumnItemWidgetBuilder builder;
|
///
|
||||||
|
final BoardColumnCardBuilder cardBuilder;
|
||||||
|
|
||||||
|
///
|
||||||
|
final BoardColumnHeaderBuilder? headerBuilder;
|
||||||
|
|
||||||
|
///
|
||||||
|
final BoardColumnFooterBuilder? footBuilder;
|
||||||
|
|
||||||
///
|
///
|
||||||
final BoardDataController dataController;
|
final BoardDataController dataController;
|
||||||
|
|
||||||
|
final BoxConstraints columnConstraints;
|
||||||
|
|
||||||
///
|
///
|
||||||
final BoardPhantomController passthroughPhantomContorller;
|
final BoardPhantomController phantomController;
|
||||||
|
|
||||||
Board({
|
Board({
|
||||||
required this.dataController,
|
required this.dataController,
|
||||||
required this.builder,
|
required this.cardBuilder,
|
||||||
this.spacing = 10.0,
|
this.spacing = 10.0,
|
||||||
this.runSpacing = 0.0,
|
this.runSpacing = 0.0,
|
||||||
this.background,
|
this.background,
|
||||||
|
this.footBuilder,
|
||||||
|
this.headerBuilder,
|
||||||
|
this.columnConstraints = const BoxConstraints(maxWidth: 200),
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : passthroughPhantomContorller =
|
}) : phantomController = BoardPhantomController(delegate: dataController),
|
||||||
BoardPhantomController(delegate: dataController),
|
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -51,41 +63,25 @@ class Board extends StatelessWidget {
|
|||||||
List<String> acceptColumns =
|
List<String> acceptColumns =
|
||||||
dataController.columnControllers.keys.toList();
|
dataController.columnControllers.keys.toList();
|
||||||
|
|
||||||
dataController.columnControllers.forEach((columnId, dataController) {
|
dataController.columnControllers.forEach((columnId, controller) {
|
||||||
Widget child =
|
Widget child = _buildColumn(columnId, acceptColumns, controller);
|
||||||
buildBoardColumn(columnId, acceptColumns, dataController);
|
|
||||||
if (children.isEmpty) {
|
|
||||||
// children.add(SizedBox(width: spacing));
|
|
||||||
}
|
|
||||||
// if (background != null) {
|
|
||||||
// child = Stack(children: [
|
|
||||||
// background!,
|
|
||||||
// child,
|
|
||||||
// ]);
|
|
||||||
// }
|
|
||||||
// children.add(Expanded(key: ValueKey(columnId), child: child));
|
|
||||||
children.add(child);
|
children.add(child);
|
||||||
// children.add(SizedBox(width: spacing));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return BoardColumnContainer(
|
return BoardColumnContainer(
|
||||||
onReorder: (fromIndex, toIndex) {},
|
onReorder: (fromIndex, toIndex) {},
|
||||||
boardDataController: dataController,
|
boardDataController: dataController,
|
||||||
|
background: background,
|
||||||
|
spacing: spacing,
|
||||||
children: children,
|
children: children,
|
||||||
);
|
);
|
||||||
|
|
||||||
// return Row(
|
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
// children: children,
|
|
||||||
// );
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
Widget buildBoardColumn(
|
Widget _buildColumn(
|
||||||
String columnId,
|
String columnId,
|
||||||
List<String> acceptColumns,
|
List<String> acceptColumns,
|
||||||
BoardColumnDataController dataController,
|
BoardColumnDataController dataController,
|
||||||
@ -95,18 +91,19 @@ class Board extends StatelessWidget {
|
|||||||
value: dataController,
|
value: dataController,
|
||||||
child: Consumer<BoardColumnDataController>(
|
child: Consumer<BoardColumnDataController>(
|
||||||
builder: (context, value, child) {
|
builder: (context, value, child) {
|
||||||
return SizedBox(
|
return ConstrainedBox(
|
||||||
width: 200,
|
constraints: columnConstraints,
|
||||||
child: BoardColumnWidget(
|
child: BoardColumnWidget(
|
||||||
header: Container(color: Colors.yellow, height: 30),
|
headerBuilder: headerBuilder,
|
||||||
builder: builder,
|
footBuilder: footBuilder,
|
||||||
|
cardBuilder: cardBuilder,
|
||||||
acceptColumns: acceptColumns,
|
acceptColumns: acceptColumns,
|
||||||
dataController: dataController,
|
dataController: dataController,
|
||||||
scrollController: ScrollController(),
|
scrollController: ScrollController(),
|
||||||
onReorder: (_, int fromIndex, int toIndex) {
|
onReorder: (_, int fromIndex, int toIndex) {
|
||||||
dataController.move(fromIndex, toIndex);
|
dataController.move(fromIndex, toIndex);
|
||||||
},
|
},
|
||||||
phantomController: passthroughPhantomContorller,
|
phantomController: phantomController,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -4,34 +4,33 @@ import '../../rendering/board_overlay.dart';
|
|||||||
import '../../utils/log.dart';
|
import '../../utils/log.dart';
|
||||||
import '../phantom/phantom_controller.dart';
|
import '../phantom/phantom_controller.dart';
|
||||||
import '../flex/reorder_flex.dart';
|
import '../flex/reorder_flex.dart';
|
||||||
import '../flex/drag_state.dart';
|
import '../flex/drag_target_inteceptor.dart';
|
||||||
import '../flex/reorder_flex_ext.dart';
|
|
||||||
import 'data_controller.dart';
|
import 'data_controller.dart';
|
||||||
|
|
||||||
typedef OnDragStarted = void Function(int index);
|
typedef OnColumnDragStarted = void Function(int index);
|
||||||
typedef OnDragEnded = void Function(String listId);
|
typedef OnColumnDragEnded = void Function(String listId);
|
||||||
typedef OnReorder = void Function(String listId, int fromIndex, int toIndex);
|
typedef OnColumnReorder = void Function(
|
||||||
typedef OnDeleted = void Function(String listId, int deletedIndex);
|
String listId, int fromIndex, int toIndex);
|
||||||
typedef OnInserted = void Function(String listId, int insertedIndex);
|
typedef OnColumnDeleted = void Function(String listId, int deletedIndex);
|
||||||
typedef OnPassedInPhantom = void Function(
|
typedef OnColumnInserted = void Function(String listId, int insertedIndex);
|
||||||
String listId,
|
|
||||||
FlexDragTargetData dragTargetData,
|
|
||||||
int phantomIndex,
|
|
||||||
);
|
|
||||||
|
|
||||||
typedef BoardColumnItemWidgetBuilder = Widget Function(
|
typedef BoardColumnCardBuilder = Widget Function(
|
||||||
BuildContext context, ColumnItem item);
|
BuildContext context, ColumnItem item);
|
||||||
|
|
||||||
|
typedef BoardColumnHeaderBuilder = Widget Function(
|
||||||
|
BuildContext context, BoardColumnData columnData);
|
||||||
|
|
||||||
|
typedef BoardColumnFooterBuilder = Widget Function(
|
||||||
|
BuildContext context, BoardColumnData columnData);
|
||||||
|
|
||||||
class BoardColumnWidget extends StatefulWidget {
|
class BoardColumnWidget extends StatefulWidget {
|
||||||
final Widget? header;
|
|
||||||
final Widget? footer;
|
|
||||||
final BoardColumnDataController dataController;
|
final BoardColumnDataController dataController;
|
||||||
final ScrollController? scrollController;
|
final ScrollController? scrollController;
|
||||||
final ReorderFlexConfig config;
|
final ReorderFlexConfig config;
|
||||||
|
|
||||||
final OnDragStarted? onDragStarted;
|
final OnColumnDragStarted? onDragStarted;
|
||||||
final OnReorder onReorder;
|
final OnColumnReorder onReorder;
|
||||||
final OnDragEnded? onDragEnded;
|
final OnColumnDragEnded? onDragEnded;
|
||||||
|
|
||||||
final BoardPhantomController phantomController;
|
final BoardPhantomController phantomController;
|
||||||
|
|
||||||
@ -39,18 +38,25 @@ class BoardColumnWidget extends StatefulWidget {
|
|||||||
|
|
||||||
final List<String> acceptColumns;
|
final List<String> acceptColumns;
|
||||||
|
|
||||||
final BoardColumnItemWidgetBuilder builder;
|
final BoardColumnCardBuilder cardBuilder;
|
||||||
|
|
||||||
|
final BoardColumnHeaderBuilder? headerBuilder;
|
||||||
|
|
||||||
|
final BoardColumnFooterBuilder? footBuilder;
|
||||||
|
|
||||||
|
final double? spacing;
|
||||||
|
|
||||||
const BoardColumnWidget({
|
const BoardColumnWidget({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.header,
|
this.headerBuilder,
|
||||||
this.footer,
|
this.footBuilder,
|
||||||
required this.builder,
|
required this.cardBuilder,
|
||||||
required this.onReorder,
|
required this.onReorder,
|
||||||
required this.dataController,
|
required this.dataController,
|
||||||
required this.phantomController,
|
required this.phantomController,
|
||||||
required this.acceptColumns,
|
required this.acceptColumns,
|
||||||
this.config = const ReorderFlexConfig(),
|
this.config = const ReorderFlexConfig(),
|
||||||
|
this.spacing,
|
||||||
this.onDragStarted,
|
this.onDragStarted,
|
||||||
this.scrollController,
|
this.scrollController,
|
||||||
this.onDragEnded,
|
this.onDragEnded,
|
||||||
@ -69,45 +75,59 @@ class _BoardColumnWidgetState extends State<BoardColumnWidget> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_overlayEntry = BoardOverlayEntry(
|
_overlayEntry = BoardOverlayEntry(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
final children = widget.dataController.items
|
final children = widget.dataController.items
|
||||||
.map((item) => _buildWidget(context, item))
|
.map((item) => _buildWidget(context, item))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
final dragTargetExtension = ReorderFlextDragTargetExtension(
|
final header = widget.headerBuilder
|
||||||
reorderFlexId: widget.columnId,
|
?.call(context, widget.dataController.columnData);
|
||||||
delegate: widget.phantomController,
|
|
||||||
acceptReorderFlexIds: widget.acceptColumns,
|
|
||||||
draggableTargetBuilder: PhantomReorderDraggableBuilder(),
|
|
||||||
);
|
|
||||||
|
|
||||||
return ReorderFlex(
|
final footer =
|
||||||
key: widget.key,
|
widget.footBuilder?.call(context, widget.dataController.columnData);
|
||||||
header: widget.header,
|
|
||||||
footer: widget.footer,
|
final interceptor = CrossReorderFlexDragTargetInterceptor(
|
||||||
scrollController: widget.scrollController,
|
reorderFlexId: widget.columnId,
|
||||||
config: widget.config,
|
delegate: widget.phantomController,
|
||||||
onDragStarted: (index) {
|
acceptReorderFlexIds: widget.acceptColumns,
|
||||||
widget.phantomController.columnStartDragging(widget.columnId);
|
draggableTargetBuilder: PhantomDraggableBuilder(),
|
||||||
widget.onDragStarted?.call(index);
|
);
|
||||||
},
|
|
||||||
onReorder: ((fromIndex, toIndex) {
|
final reorderFlex = ReorderFlex(
|
||||||
if (widget.phantomController.isFromColumn(widget.columnId)) {
|
key: widget.key,
|
||||||
widget.onReorder(widget.columnId, fromIndex, toIndex);
|
scrollController: widget.scrollController,
|
||||||
widget.phantomController.transformIndex(fromIndex, toIndex);
|
config: widget.config,
|
||||||
}
|
onDragStarted: (index) {
|
||||||
}),
|
widget.phantomController.columnStartDragging(widget.columnId);
|
||||||
onDragEnded: () {
|
widget.onDragStarted?.call(index);
|
||||||
widget.phantomController.columnEndDragging(widget.columnId);
|
},
|
||||||
widget.onDragEnded?.call(widget.columnId);
|
onReorder: ((fromIndex, toIndex) {
|
||||||
_printItems(widget.dataController);
|
if (widget.phantomController.isFromColumn(widget.columnId)) {
|
||||||
},
|
widget.onReorder(widget.columnId, fromIndex, toIndex);
|
||||||
dataSource: widget.dataController,
|
widget.phantomController.transformIndex(fromIndex, toIndex);
|
||||||
dragTargetExtension: dragTargetExtension,
|
}
|
||||||
children: children,
|
}),
|
||||||
);
|
onDragEnded: () {
|
||||||
},
|
widget.phantomController.columnEndDragging(widget.columnId);
|
||||||
opaque: false);
|
widget.onDragEnded?.call(widget.columnId);
|
||||||
|
_printItems(widget.dataController);
|
||||||
|
},
|
||||||
|
dataSource: widget.dataController,
|
||||||
|
interceptor: interceptor,
|
||||||
|
spacing: widget.spacing,
|
||||||
|
children: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
if (header != null) header,
|
||||||
|
Expanded(child: reorderFlex),
|
||||||
|
if (footer != null) footer,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
opaque: false,
|
||||||
|
);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +147,7 @@ class _BoardColumnWidgetState extends State<BoardColumnWidget> {
|
|||||||
passthroughPhantomContext: item.phantomContext,
|
passthroughPhantomContext: item.phantomContext,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return widget.builder(context, item);
|
return widget.cardBuilder(context, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../flowy_board.dart';
|
||||||
import '../rendering/board_overlay.dart';
|
import '../rendering/board_overlay.dart';
|
||||||
import 'flex/reorder_flex.dart';
|
import 'flex/reorder_flex.dart';
|
||||||
import 'board.dart';
|
|
||||||
|
|
||||||
class BoardColumnContainer extends StatefulWidget {
|
class BoardColumnContainer extends StatefulWidget {
|
||||||
final Widget? header;
|
|
||||||
final Widget? footer;
|
|
||||||
final ScrollController? scrollController;
|
final ScrollController? scrollController;
|
||||||
final OnDragStarted? onDragStarted;
|
final OnDragStarted? onDragStarted;
|
||||||
final OnReorder onReorder;
|
final OnReorder onReorder;
|
||||||
@ -15,6 +13,7 @@ class BoardColumnContainer extends StatefulWidget {
|
|||||||
final List<Widget> children;
|
final List<Widget> children;
|
||||||
final EdgeInsets? padding;
|
final EdgeInsets? padding;
|
||||||
final Widget? background;
|
final Widget? background;
|
||||||
|
final double spacing;
|
||||||
final ReorderFlexConfig config;
|
final ReorderFlexConfig config;
|
||||||
|
|
||||||
const BoardColumnContainer({
|
const BoardColumnContainer({
|
||||||
@ -23,11 +22,10 @@ class BoardColumnContainer extends StatefulWidget {
|
|||||||
required this.children,
|
required this.children,
|
||||||
this.onDragStarted,
|
this.onDragStarted,
|
||||||
this.onDragEnded,
|
this.onDragEnded,
|
||||||
this.header,
|
|
||||||
this.footer,
|
|
||||||
this.scrollController,
|
this.scrollController,
|
||||||
this.padding,
|
this.padding,
|
||||||
this.background,
|
this.background,
|
||||||
|
this.spacing = 0.0,
|
||||||
this.config = const ReorderFlexConfig(),
|
this.config = const ReorderFlexConfig(),
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@ -47,8 +45,6 @@ class _BoardColumnContainerState extends State<BoardColumnContainer> {
|
|||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
Widget reorderFlex = ReorderFlex(
|
Widget reorderFlex = ReorderFlex(
|
||||||
key: widget.key,
|
key: widget.key,
|
||||||
header: widget.header,
|
|
||||||
footer: widget.footer,
|
|
||||||
scrollController: widget.scrollController,
|
scrollController: widget.scrollController,
|
||||||
config: widget.config,
|
config: widget.config,
|
||||||
onDragStarted: (index) {},
|
onDragStarted: (index) {},
|
||||||
@ -56,6 +52,7 @@ class _BoardColumnContainerState extends State<BoardColumnContainer> {
|
|||||||
onDragEnded: () {},
|
onDragEnded: () {},
|
||||||
dataSource: widget.boardDataController,
|
dataSource: widget.boardDataController,
|
||||||
direction: Axis.horizontal,
|
direction: Axis.horizontal,
|
||||||
|
spacing: widget.spacing,
|
||||||
children: widget.children,
|
children: widget.children,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -65,17 +62,7 @@ class _BoardColumnContainerState extends State<BoardColumnContainer> {
|
|||||||
child: reorderFlex,
|
child: reorderFlex,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return _wrapStack(reorderFlex);
|
||||||
return Expanded(
|
|
||||||
child: Stack(
|
|
||||||
alignment: AlignmentDirectional.center,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
color: Colors.red,
|
|
||||||
),
|
|
||||||
reorderFlex
|
|
||||||
],
|
|
||||||
));
|
|
||||||
},
|
},
|
||||||
opaque: false);
|
opaque: false);
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -88,4 +75,14 @@ class _BoardColumnContainerState extends State<BoardColumnContainer> {
|
|||||||
initialEntries: [_overlayEntry],
|
initialEntries: [_overlayEntry],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _wrapStack(Widget child) {
|
||||||
|
return Stack(
|
||||||
|
alignment: AlignmentDirectional.topStart,
|
||||||
|
children: [
|
||||||
|
if (widget.background != null) widget.background!,
|
||||||
|
child,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ abstract class DragTargetData {
|
|||||||
int get draggingIndex;
|
int get draggingIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ReorderDraggableTargetBuilder {
|
abstract class ReorderFlexDraggableTargetBuilder {
|
||||||
Widget? build<T extends DragTargetData>(
|
Widget? build<T extends DragTargetData>(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
Widget child,
|
Widget child,
|
||||||
@ -51,7 +51,7 @@ class ReorderDragTarget<T extends DragTargetData> extends StatefulWidget {
|
|||||||
/// the target.
|
/// the target.
|
||||||
final void Function(T dragTargetData)? onLeave;
|
final void Function(T dragTargetData)? onLeave;
|
||||||
|
|
||||||
final ReorderDraggableTargetBuilder? draggableTargetBuilder;
|
final ReorderFlexDraggableTargetBuilder? draggableTargetBuilder;
|
||||||
|
|
||||||
ReorderDragTarget({
|
ReorderDragTarget({
|
||||||
Key? key,
|
Key? key,
|
||||||
@ -158,9 +158,10 @@ class _ReorderDragTargetState<T extends DragTargetData>
|
|||||||
transform: Matrix4.rotationZ(0),
|
transform: Matrix4.rotationZ(0),
|
||||||
alignment: FractionalOffset.topLeft,
|
alignment: FractionalOffset.topLeft,
|
||||||
child: Material(
|
child: Material(
|
||||||
elevation: 3.0,
|
elevation: 6.0,
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
borderRadius: BorderRadius.zero,
|
borderRadius: BorderRadius.zero,
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
child: ConstrainedBox(constraints: constraints, child: child),
|
child: ConstrainedBox(constraints: constraints, child: child),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,114 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../utils/log.dart';
|
||||||
|
import 'drag_state.dart';
|
||||||
|
import 'drag_target.dart';
|
||||||
|
import 'reorder_flex.dart';
|
||||||
|
|
||||||
|
abstract class ReorderFlexDragTargetInterceptor {
|
||||||
|
bool canHandler(FlexDragTargetData dragTargetData);
|
||||||
|
|
||||||
|
bool onWillAccept(
|
||||||
|
BuildContext context,
|
||||||
|
ReorderFlexState reorderFlexState,
|
||||||
|
FlexDragTargetData dragTargetData,
|
||||||
|
int itemIndex,
|
||||||
|
);
|
||||||
|
|
||||||
|
void onAccept(FlexDragTargetData dragTargetData);
|
||||||
|
|
||||||
|
void onLeave(FlexDragTargetData dragTargetData);
|
||||||
|
|
||||||
|
ReorderFlexDraggableTargetBuilder? get draggableTargetBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class CrossReorderFlexDragTargetDelegate {
|
||||||
|
bool acceptNewDragTargetData(
|
||||||
|
String columnId,
|
||||||
|
FlexDragTargetData dragTargetData,
|
||||||
|
int index,
|
||||||
|
);
|
||||||
|
void updateDragTargetData(
|
||||||
|
String columnId,
|
||||||
|
FlexDragTargetData dragTargetData,
|
||||||
|
int index,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class CrossReorderFlexDragTargetInterceptor
|
||||||
|
extends ReorderFlexDragTargetInterceptor {
|
||||||
|
final String reorderFlexId;
|
||||||
|
final List<String> acceptReorderFlexIds;
|
||||||
|
final CrossReorderFlexDragTargetDelegate delegate;
|
||||||
|
@override
|
||||||
|
final ReorderFlexDraggableTargetBuilder? draggableTargetBuilder;
|
||||||
|
|
||||||
|
CrossReorderFlexDragTargetInterceptor({
|
||||||
|
required this.reorderFlexId,
|
||||||
|
required this.delegate,
|
||||||
|
required this.acceptReorderFlexIds,
|
||||||
|
this.draggableTargetBuilder,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool canHandler(FlexDragTargetData dragTargetData) {
|
||||||
|
if (acceptReorderFlexIds.isEmpty) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acceptReorderFlexIds.contains(dragTargetData.reorderFlexId)) {
|
||||||
|
/// If the columnId equal to the dragTargetData's columnId,
|
||||||
|
/// it means the dragTarget is dragging on the top of its own list.
|
||||||
|
/// Otherwise, it means the dargTarget was moved to another list.
|
||||||
|
return reorderFlexId != dragTargetData.reorderFlexId;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool onWillAccept(
|
||||||
|
BuildContext context,
|
||||||
|
ReorderFlexState reorderFlexState,
|
||||||
|
FlexDragTargetData dragTargetData,
|
||||||
|
int itemIndex,
|
||||||
|
) {
|
||||||
|
final isNewDragTarget = delegate.acceptNewDragTargetData(
|
||||||
|
reorderFlexId,
|
||||||
|
dragTargetData,
|
||||||
|
itemIndex,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isNewDragTarget == false) {
|
||||||
|
delegate.updateDragTargetData(
|
||||||
|
reorderFlexId,
|
||||||
|
dragTargetData,
|
||||||
|
itemIndex,
|
||||||
|
);
|
||||||
|
|
||||||
|
reorderFlexState.onWillAccept(
|
||||||
|
context,
|
||||||
|
dragTargetData.draggingIndex,
|
||||||
|
itemIndex,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Log.debug(
|
||||||
|
'[$CrossReorderFlexDragTargetInterceptor] move Column${dragTargetData.reorderFlexId}:${dragTargetData.draggingIndex} '
|
||||||
|
'to Column$reorderFlexId:$itemIndex');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onAccept(FlexDragTargetData dragTargetData) {
|
||||||
|
Log.trace(
|
||||||
|
'[$CrossReorderFlexDragTargetInterceptor] Column$reorderFlexId on onAccept');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onLeave(FlexDragTargetData dragTargetData) {
|
||||||
|
Log.trace(
|
||||||
|
'[$CrossReorderFlexDragTargetInterceptor] Column$reorderFlexId on leave');
|
||||||
|
}
|
||||||
|
}
|
@ -2,12 +2,11 @@ import 'dart:math';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
|
||||||
import '../../utils/log.dart';
|
import '../../utils/log.dart';
|
||||||
import 'reorder_mixin.dart';
|
import 'reorder_mixin.dart';
|
||||||
import 'drag_target.dart';
|
import 'drag_target.dart';
|
||||||
import 'drag_state.dart';
|
import 'drag_state.dart';
|
||||||
import 'reorder_flex_ext.dart';
|
import 'drag_target_inteceptor.dart';
|
||||||
|
|
||||||
typedef OnDragStarted = void Function(int index);
|
typedef OnDragStarted = void Function(int index);
|
||||||
typedef OnDragEnded = void Function();
|
typedef OnDragEnded = void Function();
|
||||||
@ -33,14 +32,13 @@ class ReorderFlexConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ReorderFlex extends StatefulWidget with DraggingReorderFlex {
|
class ReorderFlex extends StatefulWidget with DraggingReorderFlex {
|
||||||
final Widget? header;
|
|
||||||
final Widget? footer;
|
|
||||||
final ReorderFlexConfig config;
|
final ReorderFlexConfig config;
|
||||||
|
|
||||||
final List<Widget> children;
|
final List<Widget> children;
|
||||||
final EdgeInsets? padding;
|
final EdgeInsets? padding;
|
||||||
|
final double? spacing;
|
||||||
final Axis direction;
|
final Axis direction;
|
||||||
final MainAxisAlignment mainAxisAlignment = MainAxisAlignment.spaceEvenly;
|
final MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start;
|
||||||
final ScrollController? scrollController;
|
final ScrollController? scrollController;
|
||||||
|
|
||||||
final OnDragStarted? onDragStarted;
|
final OnDragStarted? onDragStarted;
|
||||||
@ -49,12 +47,10 @@ class ReorderFlex extends StatefulWidget with DraggingReorderFlex {
|
|||||||
|
|
||||||
final ReoderFlextDataSource dataSource;
|
final ReoderFlextDataSource dataSource;
|
||||||
|
|
||||||
final ReorderFlextDragTargetExtension? dragTargetExtension;
|
final ReorderFlexDragTargetInterceptor? interceptor;
|
||||||
|
|
||||||
const ReorderFlex({
|
const ReorderFlex({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.header,
|
|
||||||
this.footer,
|
|
||||||
this.scrollController,
|
this.scrollController,
|
||||||
required this.dataSource,
|
required this.dataSource,
|
||||||
required this.children,
|
required this.children,
|
||||||
@ -62,9 +58,9 @@ class ReorderFlex extends StatefulWidget with DraggingReorderFlex {
|
|||||||
required this.onReorder,
|
required this.onReorder,
|
||||||
this.onDragStarted,
|
this.onDragStarted,
|
||||||
this.onDragEnded,
|
this.onDragEnded,
|
||||||
this.dragTargetExtension,
|
this.interceptor,
|
||||||
// ignore: unused_element
|
|
||||||
this.padding,
|
this.padding,
|
||||||
|
this.spacing,
|
||||||
this.direction = Axis.vertical,
|
this.direction = Axis.vertical,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -136,23 +132,19 @@ class ReorderFlexState extends State<ReorderFlex>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<Widget> children = [];
|
final List<Widget> children = [];
|
||||||
if (widget.header != null) {
|
|
||||||
children.add(widget.header!);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < widget.children.length; i += 1) {
|
for (int i = 0; i < widget.children.length; i += 1) {
|
||||||
Widget child = widget.children[i];
|
Widget child = widget.children[i];
|
||||||
|
if (widget.spacing != null) {
|
||||||
|
children.add(SizedBox(width: widget.spacing!));
|
||||||
|
}
|
||||||
|
|
||||||
final wrapChild = _wrap(child, i);
|
final wrapChild = _wrap(child, i);
|
||||||
children.add(wrapChild);
|
children.add(wrapChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.footer != null) {
|
final child = _wrapContainer(children);
|
||||||
children.add(widget.footer!);
|
return _wrapScrollView(child: child);
|
||||||
}
|
|
||||||
|
|
||||||
return _wrapScrollView(
|
|
||||||
child: _wrapContainer(children),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -316,54 +308,49 @@ class ReorderFlexState extends State<ReorderFlex>
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
onWillAccept: (FlexDragTargetData dragTargetData) {
|
onWillAccept: (FlexDragTargetData dragTargetData) {
|
||||||
Log.debug(
|
|
||||||
'[$ReorderDragTarget] ${widget.dataSource.identifier} on will accept, count: ${widget.dataSource.items.length}');
|
|
||||||
assert(widget.dataSource.items.length > childIndex);
|
assert(widget.dataSource.items.length > childIndex);
|
||||||
|
|
||||||
if (_requestDragExtensionToHanlder(
|
if (_interceptDragTarget(
|
||||||
dragTargetData,
|
dragTargetData,
|
||||||
(extension) {
|
(interceptor) => interceptor.onWillAccept(
|
||||||
extension.onWillAccept(
|
builderContext,
|
||||||
this,
|
this,
|
||||||
builderContext,
|
dragTargetData,
|
||||||
dragTargetData,
|
childIndex,
|
||||||
dragState.isDragging(),
|
),
|
||||||
dragTargetData.draggingIndex,
|
|
||||||
childIndex,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)) {
|
)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
Log.debug(
|
||||||
|
'[$ReorderDragTarget] ${widget.dataSource.identifier} on will accept, count: ${widget.dataSource.items.length}');
|
||||||
final dragIndex = dragTargetData.draggingIndex;
|
final dragIndex = dragTargetData.draggingIndex;
|
||||||
return onWillAccept(builderContext, dragIndex, childIndex);
|
return onWillAccept(builderContext, dragIndex, childIndex);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onAccept: (dragTargetData) {
|
onAccept: (dragTargetData) {
|
||||||
_requestDragExtensionToHanlder(
|
_interceptDragTarget(
|
||||||
dragTargetData,
|
dragTargetData,
|
||||||
(extension) => extension.onAccept(dragTargetData),
|
(interceptor) => interceptor.onAccept(dragTargetData),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onLeave: (dragTargetData) {
|
onLeave: (dragTargetData) {
|
||||||
_requestDragExtensionToHanlder(
|
_interceptDragTarget(
|
||||||
dragTargetData,
|
dragTargetData,
|
||||||
(extension) => extension.onLeave(dragTargetData),
|
(interceptor) => interceptor.onLeave(dragTargetData),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
draggableTargetBuilder:
|
draggableTargetBuilder: widget.interceptor?.draggableTargetBuilder,
|
||||||
widget.dragTargetExtension?.draggableTargetBuilder,
|
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _requestDragExtensionToHanlder(
|
bool _interceptDragTarget(
|
||||||
FlexDragTargetData dragTargetData,
|
FlexDragTargetData dragTargetData,
|
||||||
void Function(ReorderFlextDragTargetExtension) callback,
|
void Function(ReorderFlexDragTargetInterceptor) callback,
|
||||||
) {
|
) {
|
||||||
final extension = widget.dragTargetExtension;
|
final interceptor = widget.interceptor;
|
||||||
if (extension != null && extension.canHandler(dragTargetData)) {
|
if (interceptor != null && interceptor.canHandler(dragTargetData)) {
|
||||||
callback(extension);
|
callback(interceptor);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -466,14 +453,16 @@ class ReorderFlexState extends State<ReorderFlex>
|
|||||||
case Axis.horizontal:
|
case Axis.horizontal:
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: widget.mainAxisAlignment,
|
||||||
children: children,
|
children: children,
|
||||||
);
|
);
|
||||||
case Axis.vertical:
|
case Axis.vertical:
|
||||||
default:
|
default:
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: widget.mainAxisAlignment,
|
||||||
children: children,
|
children: children,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -514,9 +503,7 @@ class ReorderFlexState extends State<ReorderFlex>
|
|||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
)
|
)
|
||||||
.then((void value) {
|
.then((void value) {
|
||||||
setState(() {
|
setState(() => _scrolling = false);
|
||||||
_scrolling = false;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,78 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import '../../utils/log.dart';
|
|
||||||
import 'drag_state.dart';
|
|
||||||
import 'drag_target.dart';
|
|
||||||
import 'reorder_flex.dart';
|
|
||||||
|
|
||||||
abstract class DragTargetExtensionDelegate {
|
|
||||||
bool acceptNewDragTargetData(
|
|
||||||
String columnId,
|
|
||||||
FlexDragTargetData dragTargetData,
|
|
||||||
int index,
|
|
||||||
);
|
|
||||||
void updateDragTargetData(
|
|
||||||
String columnId,
|
|
||||||
FlexDragTargetData dragTargetData,
|
|
||||||
int index,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReorderFlextDragTargetExtension {
|
|
||||||
final String reorderFlexId;
|
|
||||||
final List<String> acceptReorderFlexIds;
|
|
||||||
final DragTargetExtensionDelegate delegate;
|
|
||||||
final ReorderDraggableTargetBuilder? draggableTargetBuilder;
|
|
||||||
|
|
||||||
ReorderFlextDragTargetExtension({
|
|
||||||
required this.reorderFlexId,
|
|
||||||
required this.delegate,
|
|
||||||
required this.acceptReorderFlexIds,
|
|
||||||
this.draggableTargetBuilder,
|
|
||||||
});
|
|
||||||
|
|
||||||
bool canHandler(FlexDragTargetData dragTargetData) {
|
|
||||||
/// If the columnId equal to the dragTargetData's columnId,
|
|
||||||
/// it means the dragTarget is dragging on the top of its own list.
|
|
||||||
/// Otherwise, it means the dargTarget was moved to another list.
|
|
||||||
///
|
|
||||||
if (!acceptReorderFlexIds.contains(dragTargetData.reorderFlexId)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return reorderFlexId != dragTargetData.reorderFlexId;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool onWillAccept(
|
|
||||||
ReorderFlexState reorderFlexState,
|
|
||||||
BuildContext context,
|
|
||||||
FlexDragTargetData dragTargetData,
|
|
||||||
bool isDragging,
|
|
||||||
int dragIndex,
|
|
||||||
int itemIndex,
|
|
||||||
) {
|
|
||||||
final isNewDragTarget = delegate.acceptNewDragTargetData(
|
|
||||||
reorderFlexId, dragTargetData, itemIndex);
|
|
||||||
|
|
||||||
if (isNewDragTarget == false) {
|
|
||||||
delegate.updateDragTargetData(reorderFlexId, dragTargetData, itemIndex);
|
|
||||||
reorderFlexState.onWillAccept(context, dragIndex, itemIndex);
|
|
||||||
} else {
|
|
||||||
Log.debug(
|
|
||||||
'[$ReorderFlextDragTargetExtension] move Column${dragTargetData.reorderFlexId}:${dragTargetData.draggingIndex} '
|
|
||||||
'to Column$reorderFlexId:$itemIndex');
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void onAccept(FlexDragTargetData dragTargetData) {
|
|
||||||
Log.trace(
|
|
||||||
'[$ReorderFlextDragTargetExtension] Column$reorderFlexId on onAccept');
|
|
||||||
}
|
|
||||||
|
|
||||||
void onLeave(FlexDragTargetData dragTargetData) {
|
|
||||||
Log.trace(
|
|
||||||
'[$ReorderFlextDragTargetExtension] Column$reorderFlexId on leave');
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ import '../../../flowy_board.dart';
|
|||||||
import '../../utils/log.dart';
|
import '../../utils/log.dart';
|
||||||
import '../flex/drag_state.dart';
|
import '../flex/drag_state.dart';
|
||||||
import '../flex/drag_target.dart';
|
import '../flex/drag_target.dart';
|
||||||
import '../flex/reorder_flex_ext.dart';
|
import '../flex/drag_target_inteceptor.dart';
|
||||||
import 'phantom_state.dart';
|
import 'phantom_state.dart';
|
||||||
|
|
||||||
abstract class BoardPhantomControllerDelegate {
|
abstract class BoardPhantomControllerDelegate {
|
||||||
@ -14,7 +14,7 @@ mixin ColumnDataPhantomMixim {
|
|||||||
BoardColumnDataController? get;
|
BoardColumnDataController? get;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BoardPhantomController extends DragTargetExtensionDelegate {
|
class BoardPhantomController extends CrossReorderFlexDragTargetDelegate {
|
||||||
final BoardPhantomControllerDelegate delegate;
|
final BoardPhantomControllerDelegate delegate;
|
||||||
|
|
||||||
PhantomRecord? phantomRecord;
|
PhantomRecord? phantomRecord;
|
||||||
@ -313,7 +313,7 @@ class PassthroughPhantomWidget extends PhantomWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class PhantomReorderDraggableBuilder extends ReorderDraggableTargetBuilder {
|
class PhantomDraggableBuilder extends ReorderFlexDraggableTargetBuilder {
|
||||||
@override
|
@override
|
||||||
Widget? build<T extends DragTargetData>(
|
Widget? build<T extends DragTargetData>(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user