Przeglądaj źródła

chore: config insert animation

appflowy 2 lat temu
rodzic
commit
c035c9cf93

+ 92 - 72
frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/drag_target.dart

@@ -1,25 +1,35 @@
 import 'package:flutter/material.dart';
+import '../transitions.dart';
 
 abstract class DragTargetData {
   int get draggingIndex;
 }
 
 abstract class ReorderFlexDraggableTargetBuilder {
-  Widget? build<T extends DragTargetData>(BuildContext context, Widget child, DragTargetOnStarted onDragStarted,
-      DragTargetOnEnded<T> onDragEnded, DragTargetWillAccpet<T> onWillAccept);
+  Widget? build<T extends DragTargetData>(
+    BuildContext context,
+    Widget child,
+    DragTargetOnStarted onDragStarted,
+    DragTargetOnEnded<T> onDragEnded,
+    DragTargetWillAccpet<T> onWillAccept,
+    AnimationController insertAnimationController,
+    AnimationController deleteAnimationController,
+  );
 }
 
 ///
-typedef DragTargetWillAccpet<T extends DragTargetData> = bool Function(T dragTargetData);
+typedef DragTargetWillAccpet<T extends DragTargetData> = bool Function(
+    T dragTargetData);
 
 ///
 typedef DragTargetOnStarted = void Function(Widget, int, Size?);
 
 ///
-typedef DragTargetOnEnded<T extends DragTargetData> = void Function(T dragTargetData);
+typedef DragTargetOnEnded<T extends DragTargetData> = void Function(
+    T dragTargetData);
 
 /// [ReorderDragTarget] is a [DragTarget] that carries the index information of
-/// the child.
+/// the child. You could check out this link for more information.
 ///
 /// The size of the [ReorderDragTarget] will become zero when it start dragging.
 ///
@@ -52,6 +62,9 @@ class ReorderDragTarget<T extends DragTargetData> extends StatefulWidget {
 
   final ReorderFlexDraggableTargetBuilder? draggableTargetBuilder;
 
+  final AnimationController insertAnimationController;
+  final AnimationController deleteAnimationController;
+
   ReorderDragTarget({
     Key? key,
     required this.child,
@@ -59,6 +72,8 @@ class ReorderDragTarget<T extends DragTargetData> extends StatefulWidget {
     required this.onDragStarted,
     required this.onDragEnded,
     required this.onWillAccept,
+    required this.insertAnimationController,
+    required this.deleteAnimationController,
     this.onAccept,
     this.onLeave,
     this.draggableTargetBuilder,
@@ -69,7 +84,8 @@ class ReorderDragTarget<T extends DragTargetData> extends StatefulWidget {
   State<ReorderDragTarget<T>> createState() => _ReorderDragTargetState<T>();
 }
 
-class _ReorderDragTargetState<T extends DragTargetData> extends State<ReorderDragTarget<T>> {
+class _ReorderDragTargetState<T extends DragTargetData>
+    extends State<ReorderDragTarget<T>> {
   /// Returns the dragTarget's size
   Size? _draggingFeedbackSize = Size.zero;
 
@@ -101,7 +117,8 @@ class _ReorderDragTargetState<T extends DragTargetData> extends State<ReorderDra
     List<dynamic> rejectedCandidates,
   ) {
     Widget feedbackBuilder = Builder(builder: (BuildContext context) {
-      BoxConstraints contentSizeConstraints = BoxConstraints.loose(_draggingFeedbackSize!);
+      BoxConstraints contentSizeConstraints =
+          BoxConstraints.loose(_draggingFeedbackSize!);
       return _buildDraggableFeedback(
         context,
         contentSizeConstraints,
@@ -115,6 +132,8 @@ class _ReorderDragTargetState<T extends DragTargetData> extends State<ReorderDra
           widget.onDragStarted,
           widget.onDragEnded,
           widget.onWillAccept,
+          widget.insertAnimationController,
+          widget.deleteAnimationController,
         ) ??
         LongPressDraggable<DragTargetData>(
           maxSimultaneousDrags: 1,
@@ -141,14 +160,16 @@ class _ReorderDragTargetState<T extends DragTargetData> extends State<ReorderDra
           /// When the drag does not end inside a DragTarget widget, the
           /// drag fails, but we still reorder the widget to the last position it
           /// had been dragged to.
-          onDraggableCanceled: (Velocity velocity, Offset offset) => widget.onDragEnded(widget.dragTargetData),
+          onDraggableCanceled: (Velocity velocity, Offset offset) =>
+              widget.onDragEnded(widget.dragTargetData),
           child: widget.child,
         );
 
     return draggableWidget;
   }
 
-  Widget _buildDraggableFeedback(BuildContext context, BoxConstraints constraints, Widget child) {
+  Widget _buildDraggableFeedback(
+      BuildContext context, BoxConstraints constraints, Widget child) {
     return Transform(
       transform: Matrix4.rotationZ(0),
       alignment: FractionalOffset.topLeft,
@@ -163,7 +184,7 @@ class _ReorderDragTargetState<T extends DragTargetData> extends State<ReorderDra
   }
 }
 
-class DragAnimationController {
+class DragTargetAnimation {
   // How long an animation to reorder an element in the list takes.
   final Duration reorderAnimationDuration;
 
@@ -174,17 +195,28 @@ class DragAnimationController {
   // where the widget used to be.
   late AnimationController phantomController;
 
-  DragAnimationController({
+  late AnimationController insertController;
+
+  late AnimationController deleteController;
+
+  DragTargetAnimation({
     required this.reorderAnimationDuration,
     required TickerProvider vsync,
     required void Function(AnimationStatus) entranceAnimateStatusChanged,
   }) {
-    entranceController = AnimationController(value: 1.0, vsync: vsync, duration: reorderAnimationDuration);
-    phantomController = AnimationController(value: 0, vsync: vsync, duration: reorderAnimationDuration);
+    entranceController = AnimationController(
+        value: 1.0, vsync: vsync, duration: reorderAnimationDuration);
     entranceController.addStatusListener(entranceAnimateStatusChanged);
-  }
 
-  bool get isEntranceAnimationCompleted => entranceController.isCompleted;
+    phantomController = AnimationController(
+        value: 0, vsync: vsync, duration: reorderAnimationDuration);
+
+    insertController = AnimationController(
+        value: 0.0, vsync: vsync, duration: reorderAnimationDuration);
+
+    deleteController = AnimationController(
+        value: 0.0, vsync: vsync, duration: reorderAnimationDuration);
+  }
 
   void startDargging() {
     entranceController.value = 1.0;
@@ -203,6 +235,8 @@ class DragAnimationController {
   void dispose() {
     entranceController.dispose();
     phantomController.dispose();
+    insertController.dispose();
+    deleteController.dispose();
   }
 }
 
@@ -217,7 +251,9 @@ class IgnorePointerWidget extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    final sizedChild = useIntrinsicSize ? child : SizedBox(width: 0.0, height: 0.0, child: child);
+    final sizedChild = useIntrinsicSize
+        ? child
+        : SizedBox(width: 0.0, height: 0.0, child: child);
     return IgnorePointer(
       ignoring: true,
       child: Opacity(
@@ -246,42 +282,7 @@ class PhantomWidget extends StatelessWidget {
   }
 }
 
-class PhantomAnimateContorller {
-  // How long an animation to reorder an element in the list takes.
-  final Duration reorderAnimationDuration;
-  late AnimationController appearController;
-  late AnimationController disappearController;
-
-  PhantomAnimateContorller({
-    required TickerProvider vsync,
-    required this.reorderAnimationDuration,
-    required void Function(AnimationStatus) appearAnimateStatusChanged,
-  }) {
-    appearController = AnimationController(value: 1.0, vsync: vsync, duration: reorderAnimationDuration);
-    disappearController = AnimationController(value: 0, vsync: vsync, duration: reorderAnimationDuration);
-    appearController.addStatusListener(appearAnimateStatusChanged);
-  }
-
-  bool get isAppearAnimationCompleted => appearController.isCompleted;
-
-  void animateToNext() {
-    disappearController.reverse(from: 1.0);
-    appearController.forward(from: 0.0);
-  }
-
-  void performReorderAnimation() {
-    disappearController.reverse(from: 0.1);
-    appearController.reverse(from: 0.0);
-  }
-
-  void dispose() {
-    appearController.dispose();
-    disappearController.dispose();
-  }
-}
-
 abstract class FakeDragTargetEventTrigger {
-  void fakeOnDragStarted(VoidCallback callback);
   void fakeOnDragEnded(VoidCallback callback);
 }
 
@@ -292,12 +293,15 @@ abstract class FakeDragTargetEventData {
 }
 
 class FakeDragTarget<T extends DragTargetData> extends StatefulWidget {
+  final Duration animationDuration;
   final FakeDragTargetEventTrigger eventTrigger;
   final FakeDragTargetEventData eventData;
   final DragTargetOnStarted onDragStarted;
   final DragTargetOnEnded<T> onDragEnded;
   final DragTargetWillAccpet<T> onWillAccept;
   final Widget child;
+  final AnimationController insertAnimationController;
+  final AnimationController deleteAnimationController;
   const FakeDragTarget({
     Key? key,
     required this.eventTrigger,
@@ -305,34 +309,43 @@ class FakeDragTarget<T extends DragTargetData> extends StatefulWidget {
     required this.onDragStarted,
     required this.onDragEnded,
     required this.onWillAccept,
+    required this.insertAnimationController,
+    required this.deleteAnimationController,
     required this.child,
+    this.animationDuration = const Duration(milliseconds: 250),
   }) : super(key: key);
 
   @override
   State<FakeDragTarget<T>> createState() => _FakeDragTargetState<T>();
 }
 
-class _FakeDragTargetState<T extends DragTargetData> extends State<FakeDragTarget<T>> {
-  bool isDragging = false;
+class _FakeDragTargetState<T extends DragTargetData>
+    extends State<FakeDragTarget<T>>
+    with TickerProviderStateMixin<FakeDragTarget<T>> {
+  bool simulateDragging = false;
 
   @override
   void initState() {
-    widget.eventTrigger.fakeOnDragStarted(() {
-      if (mounted) {
-        setState(() {
-          widget.onWillAccept(widget.eventData.dragTargetData as T);
-
-          widget.onDragStarted(
-            widget.child,
-            widget.eventData.index,
-            widget.eventData.feedbackSize,
-          );
-
-          isDragging = true;
+    widget.insertAnimationController.addStatusListener(
+      (status) {
+        if (status != AnimationStatus.completed) return;
+        if (!mounted) return;
+        WidgetsBinding.instance.addPostFrameCallback((_) {
+          setState(() {
+            simulateDragging = true;
+            widget.deleteAnimationController.reverse(from: 1.0);
+            widget.onWillAccept(widget.eventData.dragTargetData as T);
+            widget.onDragStarted(
+              widget.child,
+              widget.eventData.index,
+              widget.eventData.feedbackSize,
+            );
+          });
         });
-      }
-    });
+      },
+    );
 
+    widget.insertAnimationController.forward(from: 0.0);
     widget.eventTrigger.fakeOnDragEnded(() {
       if (mounted) {
         widget.onDragEnded(widget.eventData.dragTargetData as T);
@@ -344,10 +357,17 @@ class _FakeDragTargetState<T extends DragTargetData> extends State<FakeDragTarge
 
   @override
   Widget build(BuildContext context) {
-    if (isDragging) {
-      return IgnorePointerWidget(child: widget.child);
-    } else {
-      return IgnorePointerWidget(useIntrinsicSize: true, child: widget.child);
-    }
+    final child = IgnorePointerWidget(
+        useIntrinsicSize: !simulateDragging, child: widget.child);
+
+    final animationController = simulateDragging
+        ? widget.deleteAnimationController
+        : widget.insertAnimationController;
+
+    return SizeTransitionWithIntrinsicSize(
+      sizeFactor: animationController,
+      axis: Axis.vertical,
+      child: child,
+    );
   }
 }

+ 4 - 2
frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/drag_target_inteceptor.dart

@@ -127,12 +127,14 @@ class CrossReorderFlexDragTargetInterceptor extends DragTargetInterceptor {
 
   @override
   void onAccept(FlexDragTargetData dragTargetData) {
-    Log.trace('[$CrossReorderFlexDragTargetInterceptor] Column$reorderFlexId on onAccept');
+    Log.trace(
+        '[$CrossReorderFlexDragTargetInterceptor] Column$reorderFlexId on onAccept');
   }
 
   @override
   void onLeave(FlexDragTargetData dragTargetData) {
-    Log.trace('[$CrossReorderFlexDragTargetInterceptor] Column$reorderFlexId on leave');
+    Log.trace(
+        '[$CrossReorderFlexDragTargetInterceptor] Column$reorderFlexId on leave');
   }
 
   @override

+ 19 - 11
frontend/app_flowy/packages/flowy_board/lib/src/widgets/flex/reorder_flex.dart

@@ -105,14 +105,14 @@ class ReorderFlexState extends State<ReorderFlex>
   /// [dragState] records the dragging state including dragStartIndex, and phantomIndex, etc.
   late DraggingState dragState;
 
-  /// [_dragAnimationController] controls the dragging animations
-  late DragAnimationController _dragAnimationController;
+  /// [_animation] controls the dragging animations
+  late DragTargetAnimation _animation;
 
   @override
   void initState() {
     dragState = DraggingState(widget.reorderFlexId);
 
-    _dragAnimationController = DragAnimationController(
+    _animation = DragTargetAnimation(
       reorderAnimationDuration: widget.config.reorderAnimationDuration,
       entranceAnimateStatusChanged: (status) {
         if (status == AnimationStatus.completed) {
@@ -174,7 +174,7 @@ class ReorderFlexState extends State<ReorderFlex>
       _attachedScrollPosition = null;
     }
 
-    _dragAnimationController.dispose();
+    _animation.dispose();
     super.dispose();
   }
 
@@ -183,7 +183,7 @@ class ReorderFlexState extends State<ReorderFlex>
     /// dragging animation is completed. Otherwise, it will get called again
     /// when the animation finishs.
 
-    if (_dragAnimationController.isEntranceAnimationCompleted) {
+    if (_animation.entranceController.isCompleted) {
       dragState.removePhantom();
 
       if (!isAcceptingNewTarget && dragState.didDragTargetMoveToNext()) {
@@ -191,7 +191,7 @@ class ReorderFlexState extends State<ReorderFlex>
       }
 
       dragState.moveDragTargetToNext();
-      _dragAnimationController.animateToNext();
+      _animation.animateToNext();
     }
   }
 
@@ -238,7 +238,7 @@ class ReorderFlexState extends State<ReorderFlex>
         Widget appearSpace = _makeAppearSpace(dragSpace, feedbackSize);
         Widget disappearSpace = _makeDisappearSpace(dragSpace, feedbackSize);
 
-        /// When start dragging, the dragTarget, [BoardDragTarget], will
+        /// When start dragging, the dragTarget, [ReorderDragTarget], will
         /// return a [IgnorePointerWidget] which size is zero.
         if (dragState.isPhantomAboveDragTarget()) {
           //the phantom is moving down, i.e. the tile below the phantom is moving up
@@ -337,6 +337,12 @@ class ReorderFlexState extends State<ReorderFlex>
         });
       },
       onWillAccept: (FlexDragTargetData dragTargetData) {
+        Log.debug('Insert animation: ${_animation.deleteController.status}');
+
+        if (_animation.deleteController.isAnimating) {
+          return false;
+        }
+
         assert(widget.dataSource.items.length > dragTargetIndex);
 
         if (_interceptDragTarget(
@@ -366,6 +372,8 @@ class ReorderFlexState extends State<ReorderFlex>
           (interceptor) => interceptor.onLeave(dragTargetData),
         );
       },
+      insertAnimationController: _animation.insertController,
+      deleteAnimationController: _animation.deleteController,
       draggableTargetBuilder: widget.interceptor?.draggableTargetBuilder,
       child: child,
     );
@@ -387,7 +395,7 @@ class ReorderFlexState extends State<ReorderFlex>
   Widget _makeAppearSpace(Widget child, Size? feedbackSize) {
     return makeAppearingWidget(
       child,
-      _dragAnimationController.entranceController,
+      _animation.entranceController,
       feedbackSize,
       widget.direction,
     );
@@ -396,7 +404,7 @@ class ReorderFlexState extends State<ReorderFlex>
   Widget _makeDisappearSpace(Widget child, Size? feedbackSize) {
     return makeDisappearingWidget(
       child,
-      _dragAnimationController.phantomController,
+      _animation.phantomController,
       feedbackSize,
       widget.direction,
     );
@@ -409,7 +417,7 @@ class ReorderFlexState extends State<ReorderFlex>
   ) {
     setState(() {
       dragState.startDragging(draggingWidget, dragIndex, feedbackSize);
-      _dragAnimationController.startDargging();
+      _animation.startDargging();
     });
   }
 
@@ -446,7 +454,7 @@ class ReorderFlexState extends State<ReorderFlex>
       widget.onReorder.call(fromIndex, toIndex);
     }
 
-    _dragAnimationController.reverseAnimation();
+    _animation.reverseAnimation();
   }
 
   Widget _wrapScrollView({required Widget child}) {

+ 27 - 17
frontend/app_flowy/packages/flowy_board/lib/src/widgets/phantom/phantom_controller.dart

@@ -6,6 +6,7 @@ import '../flex/drag_target.dart';
 import '../flex/drag_target_inteceptor.dart';
 import 'phantom_state.dart';
 
+@protected
 abstract class BoardPhantomControllerDelegate {
   BoardColumnDataController? controller(String columnId);
 
@@ -34,7 +35,9 @@ abstract class BoardPhantomControllerDelegate {
   );
 }
 
-class BoardPhantomController extends OverlapDragTargetDelegate with CrossReorderFlexDragTargetDelegate {
+@protected
+class BoardPhantomController extends OverlapDragTargetDelegate
+    with CrossReorderFlexDragTargetDelegate {
   PhantomRecord? phantomRecord;
   final BoardPhantomControllerDelegate delegate;
   final columnsState = ColumnPhantomStateController();
@@ -60,6 +63,7 @@ class BoardPhantomController extends OverlapDragTargetDelegate with CrossReorder
     columnsState.setColumnIsDragging(columnId, false);
   }
 
+  /// Remove the phanton in the column when the column is end dragging.
   void columnEndDragging(String columnId) {
     columnsState.setColumnIsDragging(columnId, true);
     if (phantomRecord != null) {
@@ -91,6 +95,8 @@ class BoardPhantomController extends OverlapDragTargetDelegate with CrossReorder
 
   /// Remove the phantom in the column if it contains phantom
   void _removePhantom(String columnId) {
+    // columnsState.notifyDidRemovePhantom(columnId);
+    // columnsState.removeColumnListener(columnId);
     if (delegate.removePhantom(columnId)) {
       columnsState.notifyDidRemovePhantom(columnId);
       columnsState.removeColumnListener(columnId);
@@ -114,12 +120,7 @@ class BoardPhantomController extends OverlapDragTargetDelegate with CrossReorder
       PhantomColumnItem(phantomContext),
     );
 
-    WidgetsBinding.instance.addPostFrameCallback((_) {
-      Future.delayed(const Duration(milliseconds: 100), () {
-        Log.debug('[$BoardPhantomController] notify $toColumnId to insert phantom');
-        columnsState.notifyDidInsertPhantom(toColumnId);
-      });
-    });
+    columnsState.notifyDidInsertPhantom(toColumnId);
   }
 
   /// Reset or initial the [PhantomRecord]
@@ -160,6 +161,7 @@ class BoardPhantomController extends OverlapDragTargetDelegate with CrossReorder
 
     final isNewDragTarget = phantomRecord!.toColumnId != reorderFlexId;
     if (isNewDragTarget) {
+      /// Remove the phantom when the dragTarget is moved from one column to another column.
       _removePhantom(phantomRecord!.toColumnId);
       _resetPhantomRecord(reorderFlexId, dragTargetData, dragTargetIndex);
       _insertPhantom(reorderFlexId, dragTargetData, dragTargetIndex);
@@ -188,6 +190,7 @@ class BoardPhantomController extends OverlapDragTargetDelegate with CrossReorder
       return;
     }
 
+    /// Remove the phantom when the dragTarge is go back to the original column.
     _removePhantom(phantomRecord!.toColumnId);
     phantomRecord = null;
   }
@@ -214,6 +217,7 @@ class BoardPhantomController extends OverlapDragTargetDelegate with CrossReorder
 /// [toColumnId] the column that the phantom moves into
 /// [toColumnIndex] the index of the phantom moves into the column
 ///
+@protected
 class PhantomRecord {
   final String fromColumnId;
   int fromColumnIndex;
@@ -232,7 +236,8 @@ class PhantomRecord {
     if (fromColumnIndex == index) {
       return;
     }
-    Log.debug('[$PhantomRecord] Update Column$fromColumnId remove position to $index');
+    Log.debug(
+        '[$PhantomRecord] Update Column$fromColumnId remove position to $index');
     fromColumnIndex = index;
   }
 
@@ -241,7 +246,8 @@ class PhantomRecord {
       return;
     }
 
-    Log.debug('[$PhantomRecord] Column$toColumnId update position $toColumnIndex -> $index');
+    Log.debug(
+        '[$PhantomRecord] Column$toColumnId update position $toColumnIndex -> $index');
     toColumnIndex = index;
   }
 
@@ -254,7 +260,8 @@ class PhantomRecord {
 class PhantomColumnItem extends ColumnItem {
   final PassthroughPhantomContext phantomContext;
 
-  PhantomColumnItem(PassthroughPhantomContext insertedPhantom) : phantomContext = insertedPhantom;
+  PhantomColumnItem(PassthroughPhantomContext insertedPhantom)
+      : phantomContext = insertedPhantom;
 
   @override
   bool get isPhantom => true;
@@ -264,8 +271,9 @@ class PhantomColumnItem extends ColumnItem {
 
   Size? get feedbackSize => phantomContext.feedbackSize;
 
-  Widget get draggingWidget =>
-      phantomContext.draggingWidget == null ? const SizedBox() : phantomContext.draggingWidget!;
+  Widget get draggingWidget => phantomContext.draggingWidget == null
+      ? const SizedBox()
+      : phantomContext.draggingWidget!;
 
   @override
   String toString() {
@@ -303,13 +311,9 @@ class PassthroughPhantomContext extends FakeDragTargetEventTrigger
   void fakeOnDragEnded(VoidCallback callback) {
     onDragEnded = callback;
   }
-
-  @override
-  void fakeOnDragStarted(VoidCallback callback) {
-    onInserted = callback;
-  }
 }
 
+@protected
 class PassthroughPhantomWidget extends PhantomWidget {
   final PassthroughPhantomContext passthroughPhantomContext;
 
@@ -324,7 +328,9 @@ class PassthroughPhantomWidget extends PhantomWidget {
         );
 }
 
+@protected
 class PhantomDraggableBuilder extends ReorderFlexDraggableTargetBuilder {
+  PhantomDraggableBuilder();
   @override
   Widget? build<T extends DragTargetData>(
     BuildContext context,
@@ -332,6 +338,8 @@ class PhantomDraggableBuilder extends ReorderFlexDraggableTargetBuilder {
     DragTargetOnStarted onDragStarted,
     DragTargetOnEnded<T> onDragEnded,
     DragTargetWillAccpet<T> onWillAccept,
+    AnimationController insertAnimationController,
+    AnimationController deleteAnimationController,
   ) {
     if (child is PassthroughPhantomWidget) {
       return FakeDragTarget<T>(
@@ -340,6 +348,8 @@ class PhantomDraggableBuilder extends ReorderFlexDraggableTargetBuilder {
         onDragStarted: onDragStarted,
         onDragEnded: onDragEnded,
         onWillAccept: onWillAccept,
+        insertAnimationController: insertAnimationController,
+        deleteAnimationController: deleteAnimationController,
         child: child,
       );
     } else {