mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #895 from AppFlowy-IO/feat/board_move_card_animation
Feat/board move card animation
This commit is contained in:
commit
c7b671ca07
@ -1,16 +1,21 @@
|
|||||||
|
# 0.0.5
|
||||||
|
* Optimize insert card animation
|
||||||
|
* Enable insert card at the end of the column
|
||||||
|
* Fix some bugs
|
||||||
|
|
||||||
# 0.0.4
|
# 0.0.4
|
||||||
* fix some bugs
|
* Fix some bugs
|
||||||
|
|
||||||
# 0.0.3
|
# 0.0.3
|
||||||
* Support customize UI
|
* Support customize UI
|
||||||
* Update example
|
* Update example
|
||||||
* Add AppFlowy style widget
|
* Add AppFlowy style widget
|
||||||
|
|
||||||
## 0.0.2
|
# 0.0.2
|
||||||
|
|
||||||
* Update documentation
|
* Update documentation
|
||||||
|
|
||||||
## 0.0.1
|
# 0.0.1
|
||||||
|
|
||||||
* Support drag and drop column
|
* Support drag and drop column
|
||||||
* Support drag and drop column items from one to another
|
* Support drag and drop column items from one to another
|
||||||
|
@ -26,13 +26,18 @@ class _MultiBoardListExampleState extends State<MultiBoardListExample> {
|
|||||||
List<AFColumnItem> a = [
|
List<AFColumnItem> a = [
|
||||||
TextItem("Card 1"),
|
TextItem("Card 1"),
|
||||||
TextItem("Card 2"),
|
TextItem("Card 2"),
|
||||||
// RichTextItem(title: "Card 3", subtitle: 'Aug 1, 2020 4:05 PM'),
|
RichTextItem(title: "Card 3", subtitle: 'Aug 1, 2020 4:05 PM'),
|
||||||
TextItem("Card 4"),
|
TextItem("Card 4"),
|
||||||
|
TextItem("Card 5"),
|
||||||
|
TextItem("Card 6"),
|
||||||
|
RichTextItem(title: "Card 7", subtitle: 'Aug 1, 2020 4:05 PM'),
|
||||||
|
RichTextItem(title: "Card 8", subtitle: 'Aug 1, 2020 4:05 PM'),
|
||||||
|
TextItem("Card 9"),
|
||||||
];
|
];
|
||||||
final column1 = AFBoardColumnData(id: "To Do", items: a);
|
final column1 = AFBoardColumnData(id: "To Do", items: a);
|
||||||
final column2 = AFBoardColumnData(id: "In Progress", items: <AFColumnItem>[
|
final column2 = AFBoardColumnData(id: "In Progress", items: <AFColumnItem>[
|
||||||
// RichTextItem(title: "Card 5", subtitle: 'Aug 1, 2020 4:05 PM'),
|
RichTextItem(title: "Card 10", subtitle: 'Aug 1, 2020 4:05 PM'),
|
||||||
// TextItem("Card 6"),
|
TextItem("Card 11"),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
final column3 = AFBoardColumnData(id: "Done", items: <AFColumnItem>[]);
|
final column3 = AFBoardColumnData(id: "Done", items: <AFColumnItem>[]);
|
||||||
@ -93,7 +98,7 @@ class _MultiBoardListExampleState extends State<MultiBoardListExample> {
|
|||||||
return Align(
|
return Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 40),
|
||||||
child: Text(item.s),
|
child: Text(item.s),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -4,8 +4,6 @@ import 'package:flutter/material.dart';
|
|||||||
const DART_LOG = "Dart_LOG";
|
const DART_LOG = "Dart_LOG";
|
||||||
|
|
||||||
class Log {
|
class Log {
|
||||||
// static const enableLog = bool.hasEnvironment(DART_LOG);
|
|
||||||
// static final shared = Log();
|
|
||||||
static const enableLog = false;
|
static const enableLog = false;
|
||||||
|
|
||||||
static void info(String? message) {
|
static void info(String? message) {
|
||||||
@ -16,19 +14,19 @@ class Log {
|
|||||||
|
|
||||||
static void debug(String? message) {
|
static void debug(String? message) {
|
||||||
if (enableLog) {
|
if (enableLog) {
|
||||||
debugPrint('🐛[Debug]=> $message');
|
debugPrint('🐛[Debug] - ${DateTime.now().second}=> $message');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void warn(String? message) {
|
static void warn(String? message) {
|
||||||
if (enableLog) {
|
if (enableLog) {
|
||||||
debugPrint('🐛[Warn]=> $message');
|
debugPrint('🐛[Warn] - ${DateTime.now().second} => $message');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void trace(String? message) {
|
static void trace(String? message) {
|
||||||
if (enableLog) {
|
if (enableLog) {
|
||||||
// debugPrint('❗️[Trace]=> $message');
|
debugPrint('❗️[Trace] - ${DateTime.now().second}=> $message');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ class _BoardContentState extends State<BoardContent> {
|
|||||||
dataSource: widget.dataController,
|
dataSource: widget.dataController,
|
||||||
direction: Axis.horizontal,
|
direction: Axis.horizontal,
|
||||||
interceptor: interceptor,
|
interceptor: interceptor,
|
||||||
children: _buildColumns(),
|
children: _buildColumns(interceptor.columnKeys),
|
||||||
);
|
);
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
@ -191,7 +191,7 @@ class _BoardContentState extends State<BoardContent> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildColumns() {
|
List<Widget> _buildColumns(List<ColumnKey> columnKeys) {
|
||||||
final List<Widget> children =
|
final List<Widget> children =
|
||||||
widget.dataController.columnDatas.asMap().entries.map(
|
widget.dataController.columnDatas.asMap().entries.map(
|
||||||
(item) {
|
(item) {
|
||||||
@ -208,9 +208,7 @@ class _BoardContentState extends State<BoardContent> {
|
|||||||
value: widget.dataController.columnController(columnData.id),
|
value: widget.dataController.columnController(columnData.id),
|
||||||
child: Consumer<AFBoardColumnDataController>(
|
child: Consumer<AFBoardColumnDataController>(
|
||||||
builder: (context, value, child) {
|
builder: (context, value, child) {
|
||||||
return ConstrainedBox(
|
final boardColumn = AFBoardColumnWidget(
|
||||||
constraints: widget.columnConstraints,
|
|
||||||
child: AFBoardColumnWidget(
|
|
||||||
margin: _marginFromIndex(columnIndex),
|
margin: _marginFromIndex(columnIndex),
|
||||||
itemMargin: widget.config.columnItemPadding,
|
itemMargin: widget.config.columnItemPadding,
|
||||||
headerBuilder: widget.headerBuilder,
|
headerBuilder: widget.headerBuilder,
|
||||||
@ -222,7 +220,21 @@ class _BoardContentState extends State<BoardContent> {
|
|||||||
onReorder: widget.dataController.moveColumnItem,
|
onReorder: widget.dataController.moveColumnItem,
|
||||||
cornerRadius: widget.config.cornerRadius,
|
cornerRadius: widget.config.cornerRadius,
|
||||||
backgroundColor: widget.config.columnBackgroundColor,
|
backgroundColor: widget.config.columnBackgroundColor,
|
||||||
),
|
);
|
||||||
|
|
||||||
|
// columnKeys
|
||||||
|
// .removeWhere((element) => element.columnId == columnData.id);
|
||||||
|
|
||||||
|
// columnKeys.add(
|
||||||
|
// ColumnKey(
|
||||||
|
// columnId: columnData.id,
|
||||||
|
// key: boardColumn.columnGlobalKey,
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
|
||||||
|
return ConstrainedBox(
|
||||||
|
constraints: widget.columnConstraints,
|
||||||
|
child: boardColumn,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -87,7 +87,9 @@ class AFBoardColumnWidget extends StatefulWidget {
|
|||||||
|
|
||||||
final Color backgroundColor;
|
final Color backgroundColor;
|
||||||
|
|
||||||
const AFBoardColumnWidget({
|
final GlobalKey columnGlobalKey = GlobalKey();
|
||||||
|
|
||||||
|
AFBoardColumnWidget({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.headerBuilder,
|
this.headerBuilder,
|
||||||
this.footBuilder,
|
this.footBuilder,
|
||||||
@ -136,8 +138,8 @@ class _AFBoardColumnWidgetState extends State<AFBoardColumnWidget> {
|
|||||||
draggableTargetBuilder: PhantomDraggableBuilder(),
|
draggableTargetBuilder: PhantomDraggableBuilder(),
|
||||||
);
|
);
|
||||||
|
|
||||||
final reorderFlex = ReorderFlex(
|
Widget reorderFlex = ReorderFlex(
|
||||||
key: widget.key,
|
key: widget.columnGlobalKey,
|
||||||
scrollController: widget.scrollController,
|
scrollController: widget.scrollController,
|
||||||
config: widget.config,
|
config: widget.config,
|
||||||
onDragStarted: (index) {
|
onDragStarted: (index) {
|
||||||
@ -160,6 +162,9 @@ class _AFBoardColumnWidgetState extends State<AFBoardColumnWidget> {
|
|||||||
children: children,
|
children: children,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// reorderFlex =
|
||||||
|
// KeyedSubtree(key: widget.columnGlobalKey, child: reorderFlex);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: widget.margin,
|
margin: widget.margin,
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
|
@ -197,7 +197,8 @@ class AFBoardDataController extends ChangeNotifier
|
|||||||
assert(index != -1);
|
assert(index != -1);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
if (index != newIndex) {
|
if (index != newIndex) {
|
||||||
// Log.debug('[$BoardPhantomController] update $toColumnId:$index to $toColumnId:$phantomIndex');
|
Log.trace(
|
||||||
|
'[$BoardPhantomController] update $columnId:$index to $columnId:$newIndex');
|
||||||
final item = columnDataController.removeAt(index, notify: false);
|
final item = columnDataController.removeAt(index, notify: false);
|
||||||
columnDataController.insert(newIndex, item, notify: false);
|
columnDataController.insert(newIndex, item, notify: false);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ class FlexDragTargetData extends DragTargetData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DraggingState {
|
class DraggingState {
|
||||||
final String id;
|
final String reorderFlexId;
|
||||||
|
|
||||||
/// The member of widget.children currently being dragged.
|
/// The member of widget.children currently being dragged.
|
||||||
Widget? _draggingWidget;
|
Widget? _draggingWidget;
|
||||||
@ -72,7 +72,7 @@ class DraggingState {
|
|||||||
/// The additional margin to place around a computed drop area.
|
/// The additional margin to place around a computed drop area.
|
||||||
static const double _dropAreaMargin = 0.0;
|
static const double _dropAreaMargin = 0.0;
|
||||||
|
|
||||||
DraggingState(this.id);
|
DraggingState(this.reorderFlexId);
|
||||||
|
|
||||||
Size get dropAreaSize {
|
Size get dropAreaSize {
|
||||||
if (feedbackSize == null) {
|
if (feedbackSize == null) {
|
||||||
@ -132,7 +132,7 @@ class DraggingState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateNextIndex(int index) {
|
void updateNextIndex(int index) {
|
||||||
Log.trace('updateNextIndex: $index');
|
Log.debug('$reorderFlexId updateNextIndex: $index');
|
||||||
nextIndex = index;
|
nextIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,10 +222,10 @@ class DragTargetAnimation {
|
|||||||
value: 0, vsync: vsync, duration: reorderAnimationDuration);
|
value: 0, vsync: vsync, duration: reorderAnimationDuration);
|
||||||
|
|
||||||
insertController = AnimationController(
|
insertController = AnimationController(
|
||||||
value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 100));
|
value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 200));
|
||||||
|
|
||||||
deleteController = AnimationController(
|
deleteController = AnimationController(
|
||||||
value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 10));
|
value: 0.0, vsync: vsync, duration: const Duration(milliseconds: 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void startDragging() {
|
void startDragging() {
|
||||||
@ -276,6 +276,31 @@ class IgnorePointerWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AbsorbPointerWidget extends StatelessWidget {
|
||||||
|
final Widget? child;
|
||||||
|
final bool useIntrinsicSize;
|
||||||
|
const AbsorbPointerWidget({
|
||||||
|
required this.child,
|
||||||
|
this.useIntrinsicSize = false,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final sizedChild = useIntrinsicSize
|
||||||
|
? child
|
||||||
|
: SizedBox(width: 0.0, height: 0.0, child: child);
|
||||||
|
|
||||||
|
final opacity = useIntrinsicSize ? 0.3 : 0.0;
|
||||||
|
return AbsorbPointer(
|
||||||
|
child: Opacity(
|
||||||
|
opacity: opacity,
|
||||||
|
child: sizedChild,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class PhantomWidget extends StatelessWidget {
|
class PhantomWidget extends StatelessWidget {
|
||||||
final Widget? child;
|
final Widget? child;
|
||||||
final double opacity;
|
final double opacity;
|
||||||
@ -371,6 +396,7 @@ class _DragTargeMovePlaceholderState extends State<DragTargeMovePlaceholder> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class FakeDragTargetEventTrigger {
|
abstract class FakeDragTargetEventTrigger {
|
||||||
|
void fakeOnDragStart(void Function(int?) callback);
|
||||||
void fakeOnDragEnded(VoidCallback callback);
|
void fakeOnDragEnded(VoidCallback callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,6 +447,10 @@ class _FakeDragTargetState<T extends DragTargetData>
|
|||||||
/// Start insert animation
|
/// Start insert animation
|
||||||
widget.insertAnimationController.forward(from: 0.0);
|
widget.insertAnimationController.forward(from: 0.0);
|
||||||
|
|
||||||
|
// widget.eventTrigger.fakeOnDragStart((insertIndex) {
|
||||||
|
// Log.trace("[$FakeDragTarget] on drag $insertIndex");
|
||||||
|
// });
|
||||||
|
|
||||||
widget.eventTrigger.fakeOnDragEnded(() {
|
widget.eventTrigger.fakeOnDragEnded(() {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
widget.onDragEnded(widget.eventData.dragTargetData as T);
|
widget.onDragEnded(widget.eventData.dragTargetData as T);
|
||||||
@ -436,7 +466,7 @@ class _FakeDragTargetState<T extends DragTargetData>
|
|||||||
return SizeTransitionWithIntrinsicSize(
|
return SizeTransitionWithIntrinsicSize(
|
||||||
sizeFactor: widget.deleteAnimationController,
|
sizeFactor: widget.deleteAnimationController,
|
||||||
axis: Axis.vertical,
|
axis: Axis.vertical,
|
||||||
child: IgnorePointerWidget(
|
child: AbsorbPointerWidget(
|
||||||
child: widget.child,
|
child: widget.child,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -444,7 +474,7 @@ class _FakeDragTargetState<T extends DragTargetData>
|
|||||||
return SizeTransitionWithIntrinsicSize(
|
return SizeTransitionWithIntrinsicSize(
|
||||||
sizeFactor: widget.insertAnimationController,
|
sizeFactor: widget.insertAnimationController,
|
||||||
axis: Axis.vertical,
|
axis: Axis.vertical,
|
||||||
child: IgnorePointerWidget(
|
child: AbsorbPointerWidget(
|
||||||
useIntrinsicSize: true,
|
useIntrinsicSize: true,
|
||||||
child: widget.child,
|
child: widget.child,
|
||||||
),
|
),
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../../utils/log.dart';
|
import '../../utils/log.dart';
|
||||||
@ -8,6 +10,8 @@ import 'reorder_flex.dart';
|
|||||||
/// [DragTargetInterceptor] is used to intercept the [DragTarget]'s
|
/// [DragTargetInterceptor] is used to intercept the [DragTarget]'s
|
||||||
/// [onWillAccept], [OnAccept], and [onLeave] event.
|
/// [onWillAccept], [OnAccept], and [onLeave] event.
|
||||||
abstract class DragTargetInterceptor {
|
abstract class DragTargetInterceptor {
|
||||||
|
String get reorderFlexId;
|
||||||
|
|
||||||
/// Returns [yes] to receive the [DragTarget]'s event.
|
/// Returns [yes] to receive the [DragTarget]'s event.
|
||||||
bool canHandler(FlexDragTargetData dragTargetData);
|
bool canHandler(FlexDragTargetData dragTargetData);
|
||||||
|
|
||||||
@ -37,7 +41,7 @@ abstract class OverlapDragTargetDelegate {
|
|||||||
int dragTargetIndex,
|
int dragTargetIndex,
|
||||||
);
|
);
|
||||||
|
|
||||||
bool canMoveTo(String dragTargetId);
|
int canMoveTo(String dragTargetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [OverlappingDragTargetInterceptor] is used to receive the overlapping
|
/// [OverlappingDragTargetInterceptor] is used to receive the overlapping
|
||||||
@ -47,9 +51,12 @@ abstract class OverlapDragTargetDelegate {
|
|||||||
/// Receive the [DragTarget] event if the [acceptedReorderFlexId] contains
|
/// Receive the [DragTarget] event if the [acceptedReorderFlexId] contains
|
||||||
/// the passed in dragTarget' reorderFlexId.
|
/// the passed in dragTarget' reorderFlexId.
|
||||||
class OverlappingDragTargetInterceptor extends DragTargetInterceptor {
|
class OverlappingDragTargetInterceptor extends DragTargetInterceptor {
|
||||||
|
@override
|
||||||
final String reorderFlexId;
|
final String reorderFlexId;
|
||||||
final List<String> acceptedReorderFlexId;
|
final List<String> acceptedReorderFlexId;
|
||||||
final OverlapDragTargetDelegate delegate;
|
final OverlapDragTargetDelegate delegate;
|
||||||
|
final List<ColumnKey> columnKeys = [];
|
||||||
|
Timer? _delayOperation;
|
||||||
|
|
||||||
OverlappingDragTargetInterceptor({
|
OverlappingDragTargetInterceptor({
|
||||||
required this.delegate,
|
required this.delegate,
|
||||||
@ -72,15 +79,38 @@ class OverlappingDragTargetInterceptor extends DragTargetInterceptor {
|
|||||||
if (dragTargetId == dragTargetData.reorderFlexId) {
|
if (dragTargetId == dragTargetData.reorderFlexId) {
|
||||||
delegate.cancel();
|
delegate.cancel();
|
||||||
} else {
|
} else {
|
||||||
if (delegate.canMoveTo(dragTargetId)) {
|
/// The priority of the column interactions is high than the cross column.
|
||||||
delegate.moveTo(dragTargetId, dragTargetData, 0);
|
/// Workaround: delay 100 milliseconds to lower the cross column event priority.
|
||||||
|
_delayOperation?.cancel();
|
||||||
|
_delayOperation = Timer(const Duration(milliseconds: 100), () {
|
||||||
|
final index = delegate.canMoveTo(dragTargetId);
|
||||||
|
if (index != -1) {
|
||||||
|
Log.trace(
|
||||||
|
'[$OverlappingDragTargetInterceptor] move to $dragTargetId at $index');
|
||||||
|
delegate.moveTo(dragTargetId, dragTargetData, index);
|
||||||
|
|
||||||
|
// final columnIndex = columnKeys
|
||||||
|
// .indexWhere((element) => element.columnId == dragTargetId);
|
||||||
|
// if (columnIndex != -1) {
|
||||||
|
// final state = columnKeys[columnIndex].key.currentState;
|
||||||
|
// if (state is ReorderFlexState) {
|
||||||
|
// state.handleOnWillAccept(context, index);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ColumnKey {
|
||||||
|
String columnId;
|
||||||
|
GlobalKey key;
|
||||||
|
ColumnKey({required this.columnId, required this.key});
|
||||||
|
}
|
||||||
|
|
||||||
abstract class CrossReorderFlexDragTargetDelegate {
|
abstract class CrossReorderFlexDragTargetDelegate {
|
||||||
/// * [reorderFlexId] is the id that the [ReorderFlex] passed in.
|
/// * [reorderFlexId] is the id that the [ReorderFlex] passed in.
|
||||||
bool acceptNewDragTargetData(
|
bool acceptNewDragTargetData(
|
||||||
@ -96,6 +126,7 @@ abstract class CrossReorderFlexDragTargetDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CrossReorderFlexDragTargetInterceptor extends DragTargetInterceptor {
|
class CrossReorderFlexDragTargetInterceptor extends DragTargetInterceptor {
|
||||||
|
@override
|
||||||
final String reorderFlexId;
|
final String reorderFlexId;
|
||||||
final List<String> acceptedReorderFlexIds;
|
final List<String> acceptedReorderFlexIds;
|
||||||
final CrossReorderFlexDragTargetDelegate delegate;
|
final CrossReorderFlexDragTargetDelegate delegate;
|
||||||
@ -119,8 +150,12 @@ class CrossReorderFlexDragTargetInterceptor extends DragTargetInterceptor {
|
|||||||
/// If the columnId equal to the dragTargetData's columnId,
|
/// If the columnId equal to the dragTargetData's columnId,
|
||||||
/// it means the dragTarget is dragging on the top of its own list.
|
/// it means the dragTarget is dragging on the top of its own list.
|
||||||
/// Otherwise, it means the dargTarget was moved to another list.
|
/// Otherwise, it means the dargTarget was moved to another list.
|
||||||
|
Log.trace(
|
||||||
|
"[$CrossReorderFlexDragTargetInterceptor] $reorderFlexId accept ${dragTargetData.reorderFlexId} ${reorderFlexId != dragTargetData.reorderFlexId}");
|
||||||
return reorderFlexId != dragTargetData.reorderFlexId;
|
return reorderFlexId != dragTargetData.reorderFlexId;
|
||||||
} else {
|
} else {
|
||||||
|
Log.trace(
|
||||||
|
"[$CrossReorderFlexDragTargetInterceptor] not accept ${dragTargetData.reorderFlexId}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,6 +186,9 @@ class CrossReorderFlexDragTargetInterceptor extends DragTargetInterceptor {
|
|||||||
dragTargetIndex,
|
dragTargetIndex,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Log.debug(
|
||||||
|
'[$CrossReorderFlexDragTargetInterceptor] dargTargetIndex: $dragTargetIndex, reorderFlexId: $reorderFlexId');
|
||||||
|
|
||||||
if (isNewDragTarget == false) {
|
if (isNewDragTarget == false) {
|
||||||
delegate.updateDragTargetData(reorderFlexId, dragTargetIndex);
|
delegate.updateDragTargetData(reorderFlexId, dragTargetIndex);
|
||||||
reorderFlexState.handleOnWillAccept(context, dragTargetIndex);
|
reorderFlexState.handleOnWillAccept(context, dragTargetIndex);
|
||||||
|
@ -36,10 +36,10 @@ class ReorderFlexConfig {
|
|||||||
final double draggingWidgetOpacity = 0.3;
|
final double draggingWidgetOpacity = 0.3;
|
||||||
|
|
||||||
// How long an animation to reorder an element
|
// How long an animation to reorder an element
|
||||||
final Duration reorderAnimationDuration = const Duration(milliseconds: 250);
|
final Duration reorderAnimationDuration = const Duration(milliseconds: 300);
|
||||||
|
|
||||||
// How long an animation to scroll to an off-screen element
|
// How long an animation to scroll to an off-screen element
|
||||||
final Duration scrollAnimationDuration = const Duration(milliseconds: 250);
|
final Duration scrollAnimationDuration = const Duration(milliseconds: 300);
|
||||||
|
|
||||||
final bool useMoveAnimation;
|
final bool useMoveAnimation;
|
||||||
|
|
||||||
@ -214,7 +214,7 @@ class ReorderFlexState extends State<ReorderFlex>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Log.trace(
|
Log.trace(
|
||||||
'Rebuild: Column:[${dragState.id}] ${dragState.toString()}, childIndex: $childIndex shiftedIndex: $shiftedIndex');
|
'Rebuild: Column:[${dragState.reorderFlexId}] ${dragState.toString()}, childIndex: $childIndex shiftedIndex: $shiftedIndex');
|
||||||
final currentIndex = dragState.currentIndex;
|
final currentIndex = dragState.currentIndex;
|
||||||
final dragPhantomIndex = dragState.phantomIndex;
|
final dragPhantomIndex = dragState.phantomIndex;
|
||||||
|
|
||||||
@ -330,6 +330,8 @@ class ReorderFlexState extends State<ReorderFlex>
|
|||||||
widget.onDragStarted?.call(draggingIndex);
|
widget.onDragStarted?.call(draggingIndex);
|
||||||
},
|
},
|
||||||
onDragEnded: (dragTargetData) {
|
onDragEnded: (dragTargetData) {
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
Log.debug(
|
Log.debug(
|
||||||
"[DragTarget]: Column:[${widget.dataSource.identifier}] end dragging");
|
"[DragTarget]: Column:[${widget.dataSource.identifier}] end dragging");
|
||||||
_notifier.updateDragTargetIndex(-1);
|
_notifier.updateDragTargetIndex(-1);
|
||||||
@ -346,21 +348,21 @@ class ReorderFlexState extends State<ReorderFlex>
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
onWillAccept: (FlexDragTargetData dragTargetData) {
|
onWillAccept: (FlexDragTargetData dragTargetData) {
|
||||||
|
// Do not receive any events if the Insert item is animating.
|
||||||
if (_animation.deleteController.isAnimating) {
|
if (_animation.deleteController.isAnimating) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(widget.dataSource.items.length > dragTargetIndex);
|
assert(widget.dataSource.items.length > dragTargetIndex);
|
||||||
if (_interceptDragTarget(
|
if (_interceptDragTarget(dragTargetData, (interceptor) {
|
||||||
dragTargetData,
|
interceptor.onWillAccept(
|
||||||
(interceptor) => interceptor.onWillAccept(
|
|
||||||
context: builderContext,
|
context: builderContext,
|
||||||
reorderFlexState: this,
|
reorderFlexState: this,
|
||||||
dragTargetData: dragTargetData,
|
dragTargetData: dragTargetData,
|
||||||
dragTargetId: reorderFlexItem.id,
|
dragTargetId: reorderFlexItem.id,
|
||||||
dragTargetIndex: dragTargetIndex,
|
dragTargetIndex: dragTargetIndex,
|
||||||
),
|
);
|
||||||
)) {
|
})) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return handleOnWillAccept(builderContext, dragTargetIndex);
|
return handleOnWillAccept(builderContext, dragTargetIndex);
|
||||||
@ -435,7 +437,7 @@ class ReorderFlexState extends State<ReorderFlex>
|
|||||||
/// The [willAccept] will be true if the dargTarget is the widget that gets
|
/// The [willAccept] will be true if the dargTarget is the widget that gets
|
||||||
/// dragged and it is dragged on top of the other dragTargets.
|
/// dragged and it is dragged on top of the other dragTargets.
|
||||||
///
|
///
|
||||||
Log.debug(
|
Log.trace(
|
||||||
'[$ReorderDragTarget] ${widget.dataSource.identifier} on will accept, dragIndex:$dragIndex, dragTargetIndex:$dragTargetIndex, count: ${widget.dataSource.items.length}');
|
'[$ReorderDragTarget] ${widget.dataSource.identifier} on will accept, dragIndex:$dragIndex, dragTargetIndex:$dragTargetIndex, count: ${widget.dataSource.items.length}');
|
||||||
|
|
||||||
bool willAccept =
|
bool willAccept =
|
||||||
@ -524,7 +526,7 @@ class ReorderFlexState extends State<ReorderFlex>
|
|||||||
// screen, then it is already on-screen.
|
// screen, then it is already on-screen.
|
||||||
final double margin = widget.direction == Axis.horizontal
|
final double margin = widget.direction == Axis.horizontal
|
||||||
? dragState.dropAreaSize.width
|
? dragState.dropAreaSize.width
|
||||||
: dragState.dropAreaSize.height;
|
: dragState.dropAreaSize.height / 2.0;
|
||||||
if (_scrollController.hasClients) {
|
if (_scrollController.hasClients) {
|
||||||
final double scrollOffset = _scrollController.offset;
|
final double scrollOffset = _scrollController.offset;
|
||||||
final double topOffset = max(
|
final double topOffset = max(
|
||||||
|
@ -59,12 +59,13 @@ class BoardPhantomController extends OverlapDragTargetDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
void columnStartDragging(String columnId) {
|
void columnStartDragging(String columnId) {
|
||||||
columnsState.setColumnIsDragging(columnId, false);
|
columnsState.setColumnIsDragging(columnId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the phantom in the column when the column is end dragging.
|
/// Remove the phantom in the column when the column is end dragging.
|
||||||
void columnEndDragging(String columnId) {
|
void columnEndDragging(String columnId) {
|
||||||
columnsState.setColumnIsDragging(columnId, true);
|
columnsState.setColumnIsDragging(columnId, false);
|
||||||
|
|
||||||
if (phantomRecord == null) return;
|
if (phantomRecord == null) return;
|
||||||
|
|
||||||
final fromColumnId = phantomRecord!.fromColumnId;
|
final fromColumnId = phantomRecord!.fromColumnId;
|
||||||
@ -73,10 +74,7 @@ class BoardPhantomController extends OverlapDragTargetDelegate
|
|||||||
columnsState.notifyDidRemovePhantom(toColumnId);
|
columnsState.notifyDidRemovePhantom(toColumnId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (columnsState.isDragging(fromColumnId) == false) {
|
if (phantomRecord!.toColumnId == columnId) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate.swapColumnItem(
|
delegate.swapColumnItem(
|
||||||
fromColumnId,
|
fromColumnId,
|
||||||
phantomRecord!.fromColumnIndex,
|
phantomRecord!.fromColumnIndex,
|
||||||
@ -84,9 +82,11 @@ class BoardPhantomController extends OverlapDragTargetDelegate
|
|||||||
phantomRecord!.toColumnIndex,
|
phantomRecord!.toColumnIndex,
|
||||||
);
|
);
|
||||||
|
|
||||||
Log.debug("[$BoardPhantomController] did move ${phantomRecord.toString()}");
|
Log.debug(
|
||||||
|
"[$BoardPhantomController] did move ${phantomRecord.toString()}");
|
||||||
phantomRecord = null;
|
phantomRecord = null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove the phantom in the column if it contains phantom
|
/// Remove the phantom in the column if it contains phantom
|
||||||
void _removePhantom(String columnId) {
|
void _removePhantom(String columnId) {
|
||||||
@ -113,7 +113,7 @@ class BoardPhantomController extends OverlapDragTargetDelegate
|
|||||||
PhantomColumnItem(phantomContext),
|
PhantomColumnItem(phantomContext),
|
||||||
);
|
);
|
||||||
|
|
||||||
columnsState.notifyDidInsertPhantom(toColumnId);
|
columnsState.notifyDidInsertPhantom(toColumnId, phantomIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset or initial the [PhantomRecord]
|
/// Reset or initial the [PhantomRecord]
|
||||||
@ -128,8 +128,9 @@ class BoardPhantomController extends OverlapDragTargetDelegate
|
|||||||
FlexDragTargetData dragTargetData,
|
FlexDragTargetData dragTargetData,
|
||||||
int dragTargetIndex,
|
int dragTargetIndex,
|
||||||
) {
|
) {
|
||||||
// Log.debug('[$BoardPhantomController] move Column:[${dragTargetData.reorderFlexId}]:${dragTargetData.draggingIndex} '
|
// Log.debug(
|
||||||
// 'to Column:[$columnId]:$index');
|
// '[$BoardPhantomController] move Column:[${dragTargetData.reorderFlexId}]:${dragTargetData.draggingIndex} '
|
||||||
|
// 'to Column:[$columnId]:$dragTargetIndex');
|
||||||
|
|
||||||
phantomRecord = PhantomRecord(
|
phantomRecord = PhantomRecord(
|
||||||
toColumnId: columnId,
|
toColumnId: columnId,
|
||||||
@ -202,8 +203,17 @@ class BoardPhantomController extends OverlapDragTargetDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool canMoveTo(String dragTargetId) {
|
int canMoveTo(String dragTargetId) {
|
||||||
return delegate.controller(dragTargetId)?.columnData.items.isEmpty ?? false;
|
if (columnsState.isDragging(dragTargetId)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
final controller = delegate.controller(dragTargetId);
|
||||||
|
if (controller != null) {
|
||||||
|
return controller.columnData.items.length;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,7 +304,7 @@ class PassthroughPhantomContext extends FakeDragTargetEventTrigger
|
|||||||
AFColumnItem get itemData => dragTargetData.reorderFlexItem as AFColumnItem;
|
AFColumnItem get itemData => dragTargetData.reorderFlexItem as AFColumnItem;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
VoidCallback? onInserted;
|
void Function(int?)? onInserted;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
VoidCallback? onDragEnded;
|
VoidCallback? onDragEnded;
|
||||||
@ -308,6 +318,11 @@ class PassthroughPhantomContext extends FakeDragTargetEventTrigger
|
|||||||
void fakeOnDragEnded(VoidCallback callback) {
|
void fakeOnDragEnded(VoidCallback callback) {
|
||||||
onDragEnded = callback;
|
onDragEnded = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void fakeOnDragStart(void Function(int? index) callback) {
|
||||||
|
onInserted = callback;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PassthroughPhantomWidget extends PhantomWidget {
|
class PassthroughPhantomWidget extends PhantomWidget {
|
||||||
|
@ -14,7 +14,7 @@ class ColumnPhantomStateController {
|
|||||||
|
|
||||||
void addColumnListener(String columnId, PassthroughPhantomListener listener) {
|
void addColumnListener(String columnId, PassthroughPhantomListener listener) {
|
||||||
_stateWithId(columnId).notifier.addListener(
|
_stateWithId(columnId).notifier.addListener(
|
||||||
onInserted: (c) => listener.onInserted?.call(),
|
onInserted: (index) => listener.onInserted?.call(index),
|
||||||
onDeleted: () => listener.onDragEnded?.call(),
|
onDeleted: () => listener.onDragEnded?.call(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -24,8 +24,8 @@ class ColumnPhantomStateController {
|
|||||||
_states.remove(columnId);
|
_states.remove(columnId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void notifyDidInsertPhantom(String columnId) {
|
void notifyDidInsertPhantom(String columnId, int index) {
|
||||||
_stateWithId(columnId).notifier.insert();
|
_stateWithId(columnId).notifier.insert(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void notifyDidRemovePhantom(String columnId) {
|
void notifyDidRemovePhantom(String columnId) {
|
||||||
@ -48,7 +48,7 @@ class ColumnState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class PassthroughPhantomListener {
|
abstract class PassthroughPhantomListener {
|
||||||
VoidCallback? get onInserted;
|
void Function(int?)? get onInserted;
|
||||||
VoidCallback? get onDragEnded;
|
VoidCallback? get onDragEnded;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,8 +57,8 @@ class PassthroughPhantomNotifier {
|
|||||||
|
|
||||||
final removeNotifier = PhantomDeleteNotifier();
|
final removeNotifier = PhantomDeleteNotifier();
|
||||||
|
|
||||||
void insert() {
|
void insert(int index) {
|
||||||
insertNotifier.insert();
|
insertNotifier.insert(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove() {
|
void remove() {
|
||||||
@ -66,12 +66,12 @@ class PassthroughPhantomNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void addListener({
|
void addListener({
|
||||||
void Function(PassthroughPhantomContext? insertedPhantom)? onInserted,
|
void Function(int? insertedIndex)? onInserted,
|
||||||
void Function()? onDeleted,
|
void Function()? onDeleted,
|
||||||
}) {
|
}) {
|
||||||
if (onInserted != null) {
|
if (onInserted != null) {
|
||||||
insertNotifier.addListener(() {
|
insertNotifier.addListener(() {
|
||||||
onInserted(insertNotifier.insertedPhantom);
|
onInserted(insertNotifier.insertedIndex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,9 +89,11 @@ class PassthroughPhantomNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PhantomInsertNotifier extends ChangeNotifier {
|
class PhantomInsertNotifier extends ChangeNotifier {
|
||||||
|
int insertedIndex = -1;
|
||||||
PassthroughPhantomContext? insertedPhantom;
|
PassthroughPhantomContext? insertedPhantom;
|
||||||
|
|
||||||
void insert() {
|
void insert(int index) {
|
||||||
|
insertedIndex = index;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
name: appflowy_board
|
name: appflowy_board
|
||||||
description: AppFlowy board implementation.
|
description: AppFlowy board implementation.
|
||||||
version: 0.0.4
|
version: 0.0.5
|
||||||
homepage: https://github.com/AppFlowy-IO/AppFlowy
|
homepage: https://github.com/AppFlowy-IO/AppFlowy
|
||||||
repository: https://github.com/AppFlowy-IO/AppFlowy/tree/main/frontend/app_flowy/packages/appflowy_board
|
repository: https://github.com/AppFlowy-IO/AppFlowy/tree/main/frontend/app_flowy/packages/appflowy_board
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user