Bladeren bron

feat: ⭐ configured android vscode workflow

Sean Riley Hawkins 2 jaren geleden
bovenliggende
commit
9023c58ddb
37 gewijzigde bestanden met toevoegingen van 485 en 116 verwijderingen
  1. 38 0
      frontend/.vscode/launch.json
  2. 27 0
      frontend/.vscode/tasks.json
  3. 17 0
      frontend/Makefile.toml
  4. 66 0
      frontend/app_flowy/android/README.md
  5. 10 3
      frontend/app_flowy/android/app/build.gradle
  6. 1 0
      frontend/app_flowy/android/app/src/main/AndroidManifest.xml
  7. 1 1
      frontend/app_flowy/android/build.gradle
  8. 1 0
      frontend/app_flowy/android/gradle.properties
  9. 16 0
      frontend/app_flowy/android/settings.gradle
  10. 8 3
      frontend/app_flowy/packages/appflowy_board/CHANGELOG.md
  11. 9 4
      frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart
  12. 3 5
      frontend/app_flowy/packages/appflowy_board/lib/src/utils/log.dart
  13. 27 15
      frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart
  14. 8 3
      frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart
  15. 2 1
      frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart
  16. 3 3
      frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_state.dart
  17. 34 4
      frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target.dart
  18. 42 4
      frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target_interceptor.dart
  19. 12 10
      frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/reorder_flex.dart
  20. 35 20
      frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_controller.dart
  21. 11 9
      frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_state.dart
  22. 1 1
      frontend/app_flowy/packages/appflowy_board/pubspec.yaml
  23. 1 0
      frontend/app_flowy/packages/flowy_infra_ui/android/build.gradle
  24. 0 0
      frontend/app_flowy/packages/flowy_infra_ui/android/src/main/java/com/example/flowy_infra_ui/FlowyInfraUIPlugin.java
  25. 20 3
      frontend/app_flowy/packages/flowy_infra_ui/example/android/app/build.gradle
  26. 0 7
      frontend/app_flowy/packages/flowy_infra_ui/example/android/app/src/main/java/com/example/flowy_infra_ui_example/MainActivity.java
  27. 1 1
      frontend/app_flowy/packages/flowy_infra_ui/example/android/build.gradle
  28. 16 0
      frontend/app_flowy/packages/flowy_infra_ui/example/android/settings.gradle
  29. 4 0
      frontend/app_flowy/packages/flowy_infra_ui/example/example/android/app/src/main/java/com/example/flowy_infra_ui_example/FlutterActivity.java
  30. 2 2
      frontend/app_flowy/packages/flowy_sdk/android/build.gradle
  31. 1 1
      frontend/app_flowy/packages/flowy_sdk/example/android/app/build.gradle
  32. 1 1
      frontend/app_flowy/packages/flowy_sdk/example/android/build.gradle
  33. 16 11
      frontend/app_flowy/packages/flowy_sdk/lib/ffi.dart
  34. 17 1
      frontend/rust-lib/.cargo/config.toml
  35. 2 2
      frontend/rust-lib/dart-ffi/Cargo.toml
  36. 1 1
      frontend/rust-lib/lib-sqlite/Cargo.toml
  37. 31 0
      frontend/scripts/makefile/desktop.toml

+ 38 - 0
frontend/.vscode/launch.json

@@ -16,6 +16,18 @@
             },
             },
             "cwd": "${workspaceRoot}/app_flowy"
             "cwd": "${workspaceRoot}/app_flowy"
         },
         },
+        {
+            // This task builds the Rust and Dart code of AppFlowy for android.
+            "name": "AF: Run Android",
+            "request": "launch",
+            "program": "./lib/main.dart",
+            "type": "dart",
+            "preLaunchTask": "AF: build_mobile_sdk",
+            "env": {
+                "RUST_LOG": "info"
+            },
+            "cwd": "${workspaceRoot}/app_flowy"
+        },
         {
         {
             "name": "AF: Debug Rust",
             "name": "AF: Debug Rust",
             "request": "attach",
             "request": "attach",
@@ -48,6 +60,21 @@
             },
             },
             "cwd": "${workspaceRoot}/app_flowy"
             "cwd": "${workspaceRoot}/app_flowy"
         },
         },
+        {
+            // This task builds will:
+            // - call the clean task,
+            // - rebuild all the generated Files (including freeze and language files)
+            // - rebuild the the Rust and Dart code of AppFlowy.
+            "name": "AF: Clean + Rebuild All (Android)",
+            "request": "launch",
+            "program": "./lib/main.dart",
+            "type": "dart",
+            "preLaunchTask": "AF: Clean + Rebuild All (Android)",
+            "env": {
+                "RUST_LOG": "info"
+            },
+            "cwd": "${workspaceRoot}/app_flowy"
+        },
         {
         {
             "name": "AF: Build All (rustlog: trace)",
             "name": "AF: Build All (rustlog: trace)",
             "request": "launch",
             "request": "launch",
@@ -59,6 +86,17 @@
             },
             },
             "cwd": "${workspaceRoot}/app_flowy"
             "cwd": "${workspaceRoot}/app_flowy"
         },
         },
+        {
+            "name": "AF: Build All Android (rustlog: trace)",
+            "request": "launch",
+            "program": "./lib/main.dart",
+            "type": "dart",
+            "preLaunchTask": "AF: build_mobile_sdk",
+            "env": {
+                "RUST_LOG": "trace"
+            },
+            "cwd": "${workspaceRoot}/app_flowy"
+        },
         {
         {
             "name": "AF: app_flowy (profile mode)",
             "name": "AF: app_flowy (profile mode)",
             "request": "launch",
             "request": "launch",

+ 27 - 0
frontend/.vscode/tasks.json

@@ -27,6 +27,33 @@
 				"panel": "new"
 				"panel": "new"
 			}
 			}
 		},
 		},
+		{
+			"label": "AF: Clean + Rebuild All (Android)",
+			"type": "shell",
+			"dependsOrder": "sequence",
+			"dependsOn": [
+				"AF: Rust Clean",
+				"AF: Flutter Clean",
+				"AF: build_flowy_sdk_for_android",
+				"AF: Flutter Pub Get",
+				"AF: Flutter Package Get",
+				"AF: Generate Language Files",
+				"AF: Generate Freezed Files",
+			],
+			"presentation": {
+				"reveal": "always",
+				"panel": "new",
+			},
+		},
+		{
+			"label": "AF: build_flowy_sdk_for_android",
+			"type": "shell",
+			"command": "cargo make --profile development-android flowy-sdk-dev-android",
+			"group": "build",
+			"options": {
+				"cwd": "${workspaceFolder}"
+			}
+		},
 		{
 		{
 			"label": "AF: build_flowy_sdk",
 			"label": "AF: build_flowy_sdk",
 			"type": "shell",
 			"type": "shell",

+ 17 - 0
frontend/Makefile.toml

@@ -161,6 +161,23 @@ TARGET_OS = "ios"
 FLUTTER_OUTPUT_DIR = "Release"
 FLUTTER_OUTPUT_DIR = "Release"
 PRODUCT_EXT = "ipa"
 PRODUCT_EXT = "ipa"
 
 
+[env.development-android]
+BUILD_FLAG = "debug"
+TARGET_OS = "android"
+CRATE_TYPE = "cdylib"
+FLUTTER_OUTPUT_DIR = "Debug"
+
+[tasks.setup-crate-android]
+private = true
+script_runner = "@duckscript"
+script = [
+    """
+      toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
+      val = replace ${toml} "cdylib" ${CRATE_TYPE}
+      result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
+      assert ${result}
+      """,
+]
 
 
 [tasks.setup-crate-type]
 [tasks.setup-crate-type]
 private = true
 private = true

+ 66 - 0
frontend/app_flowy/android/README.md

@@ -0,0 +1,66 @@
+# Description
+
+This is a guide on how to build the rust SDK for AppFlowy on android.
+Compiling the sdk is easy it just needs a few tweaks.
+When compiling for android we need the following pre-requisites:
+
+- Android NDK Tools. (v24 has been tested).
+- Cargo NDK. (@latest version).
+- The rust targets.
+
+## How to build the SDK
+
+**Getting the tools**
+- Install cargo-ndk ```bash cargo install cargo-ndk```.
+- [Download](https://developer.android.com/ndk/downloads/) Android NDK version 24.
+- Or you can download it through [Android Studio](https://developer.android.com/studio).
+- After downloading the NDK you need to set the environment variables. For Windows that's a seperate process.
+    On MacOs and Linux the process is similar.
+- The variables needed are '$ANDROID_NDK_HOME', this will point to where the NDK is located.
+---
+
+**Cargo Config File**
+This code needs to be written in ~/.cargo/config, this helps cargo know where to locate the android tools(linker and archiver).
+**NB** Keep in mind just replace 'user' with your own user name. Or just point it to the location of where you put the NDK.
+
+```toml
+[target.aarch64-linux-android]
+ar = "/home/user/Android/Sdk/ndk/24.0.8215888/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar"
+linker = "/home/user/Android/Sdk/ndk/24.0.8215888/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang"
+
+[target.armv7-linux-androideabi]
+ar = "/home/user/Android/Sdk/ndk/24.0.8215888/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar"
+linker = "/home/user/Android/Sdk/ndk/24.0.8215888/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi29-clang"
+
+[target.i686-linux-android]
+ar = "/home/user/Android/Sdk/ndk/24.0.8215888/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar"
+linker = "/home/user/Android/Sdk/ndk/24.0.8215888/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android29-clang"
+
+[target.x86_64-linux-android]
+ar = "/home/user/Android/Sdk/ndk/24.0.8215888/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar"
+linker = "/home/user/Android/Sdk/ndk/24.0.8215888/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android29-clang"
+```
+
+**Clang Fix**
+ In order to get clang to work properly with version 24 you need to create this file.
+ libgcc.a, then add this one line.
+ ```
+ INPUT(-lunwind)
+ ```
+
+**Folder path: 'Android/Sdk/ndk/24.0.8215888/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/14.0.1/lib/linux'.**
+After that you have to copy this file into three different folders namely aarch64, arm, i386 and x86_64.
+We have to do this so we Android NDK can find clang on our system, if we used NDK 22 we wouldnt have to do this process.
+Though using NDK v22 will not give us alot of features to work with.
+This github [issue](https://github.com/fzyzcjy/flutter_rust_bridge/issues/419) explains the reason why we are doing this.
+
+ ---
+
+ **Android NDK**
+
+ After installing the NDK tools for android you should export the PATH to your config file
+ (.vimrc, .zshrc, .profile, .bashrc file), That way it can be found.
+
+ ```vim
+ export PATH=/home/user/Android/Sdk/ndk/24.0.8215888
+ ```

+ 10 - 3
frontend/app_flowy/android/app/build.gradle

@@ -26,7 +26,8 @@ apply plugin: 'kotlin-android'
 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
 
 
 android {
 android {
-    compileSdkVersion 30
+    compileSdkVersion 31
+    ndkVersion "24.0.8215888"
 
 
     compileOptions {
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_8
         sourceCompatibility JavaVersion.VERSION_1_8
@@ -39,21 +40,26 @@ android {
 
 
     sourceSets {
     sourceSets {
         main.java.srcDirs += 'src/main/kotlin'
         main.java.srcDirs += 'src/main/kotlin'
+        main.jniLibs.srcDirs += 'jniLibs/'
     }
     }
 
 
     defaultConfig {
     defaultConfig {
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
         applicationId "com.example.app_flowy"
         applicationId "com.example.app_flowy"
-        minSdkVersion 16
-        targetSdkVersion 30
+        minSdkVersion 19
+        targetSdkVersion 31
         versionCode flutterVersionCode.toInteger()
         versionCode flutterVersionCode.toInteger()
         versionName flutterVersionName
         versionName flutterVersionName
+        multiDexEnabled true
     }
     }
 
 
     buildTypes {
     buildTypes {
         release {
         release {
             // TODO: Add your own signing config for the release build.
             // TODO: Add your own signing config for the release build.
             // Signing with the debug keys for now, so `flutter run --release` works.
             // Signing with the debug keys for now, so `flutter run --release` works.
+            minifyEnabled true
+            shrinkResources true
+
             signingConfig signingConfigs.debug
             signingConfig signingConfigs.debug
         }
         }
     }
     }
@@ -65,4 +71,5 @@ flutter {
 
 
 dependencies {
 dependencies {
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+    implementation "com.android.support:multidex:2.0.1"
 }
 }

+ 1 - 0
frontend/app_flowy/android/app/src/main/AndroidManifest.xml

@@ -3,6 +3,7 @@
    <application
    <application
         android:label="app_flowy"
         android:label="app_flowy"
         android:icon="@mipmap/ic_launcher">
         android:icon="@mipmap/ic_launcher">
+        android:name="${applicationName}"
         <activity
         <activity
             android:name=".MainActivity"
             android:name=".MainActivity"
             android:launchMode="singleTop"
             android:launchMode="singleTop"

+ 1 - 1
frontend/app_flowy/android/build.gradle

@@ -1,5 +1,5 @@
 buildscript {
 buildscript {
-    ext.kotlin_version = '1.3.50'
+    ext.kotlin_version = '1.6.10'
     repositories {
     repositories {
         google()
         google()
         mavenCentral()
         mavenCentral()

+ 1 - 0
frontend/app_flowy/android/gradle.properties

@@ -1,3 +1,4 @@
 org.gradle.jvmargs=-Xmx1536M
 org.gradle.jvmargs=-Xmx1536M
 android.useAndroidX=true
 android.useAndroidX=true
 android.enableJetifier=true
 android.enableJetifier=true
+org.gradle.caching=true

+ 16 - 0
frontend/app_flowy/android/settings.gradle

@@ -9,3 +9,19 @@ localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
 def flutterSdkPath = properties.getProperty("flutter.sdk")
 def flutterSdkPath = properties.getProperty("flutter.sdk")
 assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
 assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
 apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
 apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
+
+
+def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+
+def plugins = new Properties()
+def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
+
+if(pluginsFile.exists()){
+    pluginsFile.withReader('UTF-8'){reader -> plugins.load(reader)}
+}
+
+plugins.each{name, path ->
+        def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
+        include ":$name"
+        project(":$name").projectDir  = pluginDirectory
+}

+ 8 - 3
frontend/app_flowy/packages/appflowy_board/CHANGELOG.md

@@ -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

+ 9 - 4
frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart

@@ -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'),
-      // TextItem("Card 6"),
+      RichTextItem(title: "Card 10", subtitle: 'Aug 1, 2020 4:05 PM'),
+      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),
         ),
         ),
       );
       );

+ 3 - 5
frontend/app_flowy/packages/appflowy_board/lib/src/utils/log.dart

@@ -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');
     }
     }
   }
   }
 }
 }

+ 27 - 15
frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart

@@ -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,21 +208,33 @@ 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) {
+              final boardColumn = AFBoardColumnWidget(
+                margin: _marginFromIndex(columnIndex),
+                itemMargin: widget.config.columnItemPadding,
+                headerBuilder: widget.headerBuilder,
+                footBuilder: widget.footBuilder,
+                cardBuilder: widget.cardBuilder,
+                dataSource: dataSource,
+                scrollController: ScrollController(),
+                phantomController: widget.phantomController,
+                onReorder: widget.dataController.moveColumnItem,
+                cornerRadius: widget.config.cornerRadius,
+                backgroundColor: widget.config.columnBackgroundColor,
+              );
+
+              // columnKeys
+              //     .removeWhere((element) => element.columnId == columnData.id);
+
+              // columnKeys.add(
+              //   ColumnKey(
+              //     columnId: columnData.id,
+              //     key: boardColumn.columnGlobalKey,
+              //   ),
+              // );
+
               return ConstrainedBox(
               return ConstrainedBox(
                 constraints: widget.columnConstraints,
                 constraints: widget.columnConstraints,
-                child: AFBoardColumnWidget(
-                  margin: _marginFromIndex(columnIndex),
-                  itemMargin: widget.config.columnItemPadding,
-                  headerBuilder: widget.headerBuilder,
-                  footBuilder: widget.footBuilder,
-                  cardBuilder: widget.cardBuilder,
-                  dataSource: dataSource,
-                  scrollController: ScrollController(),
-                  phantomController: widget.phantomController,
-                  onReorder: widget.dataController.moveColumnItem,
-                  cornerRadius: widget.config.cornerRadius,
-                  backgroundColor: widget.config.columnBackgroundColor,
-                ),
+                child: boardColumn,
               );
               );
             },
             },
           ),
           ),

+ 8 - 3
frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart

@@ -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(
-          key: widget.key,
+        Widget reorderFlex = ReorderFlex(
+          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,

+ 2 - 1
frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart

@@ -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);
       }
       }

+ 3 - 3
frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_state.dart

@@ -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;
   }
   }
 
 

+ 34 - 4
frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target.dart

@@ -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,
         ),
         ),

+ 42 - 4
frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target_interceptor.dart

@@ -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)) {
-        delegate.moveTo(dragTargetId, dragTargetData, 0);
-      }
+      /// The priority of the column interactions is high than the cross column.
+      /// 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);

+ 12 - 10
frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/reorder_flex.dart

@@ -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(
-          dragTargetData,
-          (interceptor) => interceptor.onWillAccept(
+        if (_interceptDragTarget(dragTargetData, (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(

+ 35 - 20
frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_controller.dart

@@ -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,19 +74,18 @@ class BoardPhantomController extends OverlapDragTargetDelegate
       columnsState.notifyDidRemovePhantom(toColumnId);
       columnsState.notifyDidRemovePhantom(toColumnId);
     }
     }
 
 
-    if (columnsState.isDragging(fromColumnId) == false) {
-      return;
-    }
-
-    delegate.swapColumnItem(
-      fromColumnId,
-      phantomRecord!.fromColumnIndex,
-      toColumnId,
-      phantomRecord!.toColumnIndex,
-    );
+    if (phantomRecord!.toColumnId == columnId) {
+      delegate.swapColumnItem(
+        fromColumnId,
+        phantomRecord!.fromColumnIndex,
+        toColumnId,
+        phantomRecord!.toColumnIndex,
+      );
 
 
-    Log.debug("[$BoardPhantomController] did move ${phantomRecord.toString()}");
-    phantomRecord = null;
+      Log.debug(
+          "[$BoardPhantomController] did move ${phantomRecord.toString()}");
+      phantomRecord = null;
+    }
   }
   }
 
 
   /// Remove the phantom in the column if it contains phantom
   /// Remove the phantom in the column if it contains phantom
@@ -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} '
-    //     'to Column:[$columnId]:$index');
+    // Log.debug(
+    //     '[$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) {
-    return delegate.controller(dragTargetId)?.columnData.items.isEmpty ?? false;
+  int canMoveTo(String dragTargetId) {
+    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 {

+ 11 - 9
frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_state.dart

@@ -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) {
-    _stateWithId(columnId).notifier.insert();
+  void notifyDidInsertPhantom(String columnId, int index) {
+    _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() {
-    insertNotifier.insert();
+  void insert(int index) {
+    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 - 1
frontend/app_flowy/packages/appflowy_board/pubspec.yaml

@@ -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
 
 

+ 1 - 0
frontend/app_flowy/packages/flowy_infra_ui/android/build.gradle

@@ -2,6 +2,7 @@ group 'com.example.flowy_infra_ui'
 version '1.0'
 version '1.0'
 
 
 buildscript {
 buildscript {
+    ext.kotlin_version  = '1.6.10'
     repositories {
     repositories {
         google()
         google()
         mavenCentral()
         mavenCentral()

+ 0 - 0
frontend/app_flowy/packages/flowy_infra_ui/android/src/main/java/com/example/flowy_infra_ui/FlowyInfraUiPlugin.java → frontend/app_flowy/packages/flowy_infra_ui/android/src/main/java/com/example/flowy_infra_ui/FlowyInfraUIPlugin.java


+ 20 - 3
frontend/app_flowy/packages/flowy_infra_ui/example/android/app/build.gradle

@@ -24,8 +24,16 @@ if (flutterVersionName == null) {
 apply plugin: 'com.android.application'
 apply plugin: 'com.android.application'
 apply plugin: 'kotlin-android'
 apply plugin: 'kotlin-android'
 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+//apply plugin: 'kotlin-android-extensions'
+
+
+//androidExtensions {
+//    experimental = true
+//}
 
 
 android {
 android {
+    compileSdkVersion flutter.compileSdkVersion
+    ndkVersion flutter.ndkVersion
 
 
     compileOptions {
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_8
         sourceCompatibility JavaVersion.VERSION_1_8
@@ -43,20 +51,28 @@ android {
     defaultConfig {
     defaultConfig {
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
         applicationId "com.example.flowy_infra_ui_example"
         applicationId "com.example.flowy_infra_ui_example"
-        minSdkVersion 16
-        targetSdkVersion 30
+        minSdkVersion flutter.minSdkVersion
+        targetSdkVersion flutter.targetSdkVersion
         versionCode flutterVersionCode.toInteger()
         versionCode flutterVersionCode.toInteger()
         versionName flutterVersionName
         versionName flutterVersionName
+        multiDexEnabled true
     }
     }
 
 
     buildTypes {
     buildTypes {
         release {
         release {
+            minifyEnabled true
+            shrinkResources true
+//            useProguard true
+//            proguardFiles getDefaultProguardFiles(
+//                    'proguard-android-optimize.txt'
+//            ),
+//                    'proguard-rules.pro'
             // TODO: Add your own signing config for the release build.
             // TODO: Add your own signing config for the release build.
             // Signing with the debug keys for now, so `flutter run --release` works.
             // Signing with the debug keys for now, so `flutter run --release` works.
             signingConfig signingConfigs.debug
             signingConfig signingConfigs.debug
         }
         }
     }
     }
-    compileSdkVersion 30
+
 }
 }
 
 
 flutter {
 flutter {
@@ -65,4 +81,5 @@ flutter {
 
 
 dependencies {
 dependencies {
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+    implementation 'com.android.support:multidex:2.0.1'
 }
 }

+ 0 - 7
frontend/app_flowy/packages/flowy_infra_ui/example/android/app/src/main/java/com/example/flowy_infra_ui_example/MainActivity.java

@@ -1,7 +0,0 @@
-package com.example.flowy_infra_ui_example;
-
-import io.flutter.embedding.android.FlutterActivity;
-
-public class MainActivity extends FlutterActivity {
-
-}

+ 1 - 1
frontend/app_flowy/packages/flowy_infra_ui/example/android/build.gradle

@@ -1,5 +1,5 @@
 buildscript {
 buildscript {
-    ext.kotlin_version = '1.3.50'
+    ext.kotlin_version = '1.6.10'
     repositories {
     repositories {
         google()
         google()
         mavenCentral()
         mavenCentral()

+ 16 - 0
frontend/app_flowy/packages/flowy_infra_ui/example/android/settings.gradle

@@ -9,3 +9,19 @@ localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
 def flutterSdkPath = properties.getProperty("flutter.sdk")
 def flutterSdkPath = properties.getProperty("flutter.sdk")
 assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
 assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
 apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
 apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
+
+
+
+def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+
+def plugins = new Properties()
+def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
+if (pluginsFile.exists()) {
+    pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
+}
+
+plugins.each { name, path ->
+    def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
+    include ":$name"
+    project(":$name").projectDir = pluginDirectory
+}

+ 4 - 0
frontend/app_flowy/packages/flowy_infra_ui/example/example/android/app/src/main/java/com/example/flowy_infra_ui_example/FlutterActivity.java

@@ -0,0 +1,4 @@
+package example.android.app.src.main.java.com.example.flowy_infra_ui_example;
+
+public class FlutterActivity {
+}

+ 2 - 2
frontend/app_flowy/packages/flowy_sdk/android/build.gradle

@@ -2,7 +2,7 @@ group 'com.plugin.flowy_sdk'
 version '1.0-SNAPSHOT'
 version '1.0-SNAPSHOT'
 
 
 buildscript {
 buildscript {
-    ext.kotlin_version = '1.3.50'
+    ext.kotlin_version = '1.6.10'
     repositories {
     repositories {
         google()
         google()
         jcenter()
         jcenter()
@@ -25,7 +25,7 @@ apply plugin: 'com.android.library'
 apply plugin: 'kotlin-android'
 apply plugin: 'kotlin-android'
 
 
 android {
 android {
-    compileSdkVersion 30
+    compileSdkVersion 31
 
 
     sourceSets {
     sourceSets {
         main.java.srcDirs += 'src/main/kotlin'
         main.java.srcDirs += 'src/main/kotlin'

+ 1 - 1
frontend/app_flowy/packages/flowy_sdk/example/android/app/build.gradle

@@ -36,7 +36,7 @@ android {
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
         applicationId "com.plugin.flowy_sdk_example"
         applicationId "com.plugin.flowy_sdk_example"
         minSdkVersion 16
         minSdkVersion 16
-        targetSdkVersion 30
+        targetSdkVersion 31
         versionCode flutterVersionCode.toInteger()
         versionCode flutterVersionCode.toInteger()
         versionName flutterVersionName
         versionName flutterVersionName
     }
     }

+ 1 - 1
frontend/app_flowy/packages/flowy_sdk/example/android/build.gradle

@@ -1,5 +1,5 @@
 buildscript {
 buildscript {
-    ext.kotlin_version = '1.3.50'
+    ext.kotlin_version = '1.6.10'
     repositories {
     repositories {
         google()
         google()
         jcenter()
         jcenter()

+ 16 - 11
frontend/app_flowy/packages/flowy_sdk/lib/ffi.dart

@@ -14,11 +14,11 @@ final DynamicLibrary dl = _dl;
 DynamicLibrary _open() {
 DynamicLibrary _open() {
   if (Platform.environment.containsKey('FLUTTER_TEST')) {
   if (Platform.environment.containsKey('FLUTTER_TEST')) {
     final prefix = "${Directory.current.path}/.sandbox";
     final prefix = "${Directory.current.path}/.sandbox";
-    if (Platform.isLinux) return DynamicLibrary.open('${prefix}/libdart_ffi.so');
-    if (Platform.isAndroid) return DynamicLibrary.open('${prefix}/libdart_ffi.so');
+    if (Platform.isAndroid) return DynamicLibrary.open('libdart_ffi.so');
     if (Platform.isMacOS) return DynamicLibrary.open('${prefix}/libdart_ffi.a');
     if (Platform.isMacOS) return DynamicLibrary.open('${prefix}/libdart_ffi.a');
     if (Platform.isIOS) return DynamicLibrary.open('${prefix}/libdart_ffi.a');
     if (Platform.isIOS) return DynamicLibrary.open('${prefix}/libdart_ffi.a');
-    if (Platform.isWindows) return DynamicLibrary.open('${prefix}/dart_ffi.dll');
+    if (Platform.isWindows)
+      return DynamicLibrary.open('${prefix}/dart_ffi.dll');
   } else {
   } else {
     if (Platform.isLinux) return DynamicLibrary.open('libdart_ffi.so');
     if (Platform.isLinux) return DynamicLibrary.open('libdart_ffi.so');
     if (Platform.isAndroid) return DynamicLibrary.open('libdart_ffi.so');
     if (Platform.isAndroid) return DynamicLibrary.open('libdart_ffi.so');
@@ -39,7 +39,8 @@ void async_event(
   _invoke_async(port, input, len);
   _invoke_async(port, input, len);
 }
 }
 
 
-final _invoke_async_Dart _invoke_async = _dl.lookupFunction<_invoke_async_C, _invoke_async_Dart>('async_event');
+final _invoke_async_Dart _invoke_async =
+    _dl.lookupFunction<_invoke_async_C, _invoke_async_Dart>('async_event');
 typedef _invoke_async_C = Void Function(
 typedef _invoke_async_C = Void Function(
   Int64 port,
   Int64 port,
   Pointer<Uint8> input,
   Pointer<Uint8> input,
@@ -59,7 +60,8 @@ Pointer<Uint8> sync_event(
   return _invoke_sync(input, len);
   return _invoke_sync(input, len);
 }
 }
 
 
-final _invoke_sync_Dart _invoke_sync = _dl.lookupFunction<_invoke_sync_C, _invoke_sync_Dart>('sync_event');
+final _invoke_sync_Dart _invoke_sync =
+    _dl.lookupFunction<_invoke_sync_C, _invoke_sync_Dart>('sync_event');
 typedef _invoke_sync_C = Pointer<Uint8> Function(
 typedef _invoke_sync_C = Pointer<Uint8> Function(
   Pointer<Uint8> input,
   Pointer<Uint8> input,
   Uint64 len,
   Uint64 len,
@@ -76,7 +78,8 @@ int init_sdk(
   return _init_sdk(path);
   return _init_sdk(path);
 }
 }
 
 
-final _init_sdk_Dart _init_sdk = _dl.lookupFunction<_init_sdk_C, _init_sdk_Dart>('init_sdk');
+final _init_sdk_Dart _init_sdk =
+    _dl.lookupFunction<_init_sdk_C, _init_sdk_Dart>('init_sdk');
 typedef _init_sdk_C = Int64 Function(
 typedef _init_sdk_C = Int64 Function(
   Pointer<ffi.Utf8> path,
   Pointer<ffi.Utf8> path,
 );
 );
@@ -90,7 +93,8 @@ int set_stream_port(int port) {
 }
 }
 
 
 final _set_stream_port_Dart _set_stream_port =
 final _set_stream_port_Dart _set_stream_port =
-    _dl.lookupFunction<_set_stream_port_C, _set_stream_port_Dart>('set_stream_port');
+    _dl.lookupFunction<_set_stream_port_C, _set_stream_port_Dart>(
+        'set_stream_port');
 
 
 typedef _set_stream_port_C = Int32 Function(
 typedef _set_stream_port_C = Int32 Function(
   Int64 port,
   Int64 port,
@@ -104,8 +108,8 @@ void link_me_please() {
   _link_me_please();
   _link_me_please();
 }
 }
 
 
-final _link_me_please_Dart _link_me_please =
-    _dl.lookupFunction<_link_me_please_C, _link_me_please_Dart>('link_me_please');
+final _link_me_please_Dart _link_me_please = _dl
+    .lookupFunction<_link_me_please_C, _link_me_please_Dart>('link_me_please');
 typedef _link_me_please_C = Void Function();
 typedef _link_me_please_C = Void Function();
 typedef _link_me_please_Dart = void Function();
 typedef _link_me_please_Dart = void Function();
 
 
@@ -116,8 +120,9 @@ void store_dart_post_cobject(
   _store_dart_post_cobject(ptr);
   _store_dart_post_cobject(ptr);
 }
 }
 
 
-final _store_dart_post_cobject_Dart _store_dart_post_cobject =
-    _dl.lookupFunction<_store_dart_post_cobject_C, _store_dart_post_cobject_Dart>('store_dart_post_cobject');
+final _store_dart_post_cobject_Dart _store_dart_post_cobject = _dl
+    .lookupFunction<_store_dart_post_cobject_C, _store_dart_post_cobject_Dart>(
+        'store_dart_post_cobject');
 typedef _store_dart_post_cobject_C = Void Function(
 typedef _store_dart_post_cobject_C = Void Function(
   Pointer<NativeFunction<Int8 Function(Int64, Pointer<Dart_CObject>)>> ptr,
   Pointer<NativeFunction<Int8 Function(Int64, Pointer<Dart_CObject>)>> ptr,
 );
 );

+ 17 - 1
frontend/rust-lib/.cargo/config.toml

@@ -5,4 +5,20 @@
 rustflags=["-C", "link-arg=-mmacosx-version-min=10.11"]
 rustflags=["-C", "link-arg=-mmacosx-version-min=10.11"]
 
 
 [target.aarch64-apple-darwin]
 [target.aarch64-apple-darwin]
-rustflags=["-C", "link-arg=-mmacosx-version-min=10.11"]
+rustflags=["-C", "link-arg=-mmacosx-version-min=10.11"]
+
+[target.aarch64-linux-android]
+ar = "path-to-ndk/llvm-ar"
+linker = "path-to-ndk/aarch64-linux-android29-clang"
+
+[target.armv7-linux-androideabi]
+ar = "path-to-ndk/llvm-ar"
+linker = "path-to-ndk/armv7a-linux-androideabi29-clang"
+
+[target.i686-linux-android]
+ar = "path-to-ndk/llvm-ar"
+linker = "path-to-ndk/i686-linux-android29-clang"
+
+[target.x86_64-linux-android]
+ar = "path-to-ndk/llvm-ar"
+linker = "path-to-ndk/x86_64-linux-android29-clang"

+ 2 - 2
frontend/rust-lib/dart-ffi/Cargo.toml

@@ -8,7 +8,7 @@ edition = "2018"
 name = "dart_ffi"
 name = "dart_ffi"
 # this value will change depending on the target os
 # this value will change depending on the target os
 # default static lib
 # default static lib
-crate-type = ["staticlib"]
+crate-type = ["staticlib", "cdylib"]
 
 
 
 
 [dependencies]
 [dependencies]
@@ -38,4 +38,4 @@ http_sync = ["flowy-sdk/http_sync", "flowy-sdk/use_bunyan"]
 #use_protobuf= ["protobuf"]
 #use_protobuf= ["protobuf"]
 
 
 [build-dependencies]
 [build-dependencies]
-lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file_gen", "dart"] }
+lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file_gen", "dart"] }

+ 1 - 1
frontend/rust-lib/lib-sqlite/Cargo.toml

@@ -15,6 +15,6 @@ lazy_static = "1.4.0"
 scheduled-thread-pool = "0.2.5"
 scheduled-thread-pool = "0.2.5"
 error-chain = "=0.12.0"
 error-chain = "=0.12.0"
 log = "0.4.11"
 log = "0.4.11"
-
+openssl = { version = "0.10.38", features = ["vendored"] }
 #[features]
 #[features]
 #windows = ["libsqlite3-sys/bundled-windows"]
 #windows = ["libsqlite3-sys/bundled-windows"]

+ 31 - 0
frontend/scripts/makefile/desktop.toml

@@ -13,6 +13,11 @@ mac_alias = "flowy-sdk-dev-macos"
 windows_alias = "flowy-sdk-dev-windows"
 windows_alias = "flowy-sdk-dev-windows"
 linux_alias = "flowy-sdk-dev-linux"
 linux_alias = "flowy-sdk-dev-linux"
 
 
+[tasks.flowy-sdk-dev-android]
+category = "Build"
+dependencies = ["env_check"]
+run_task = { name = ["setup-crate-type","sdk-build-android", "restore-crate-type"] }
+
 [tasks.flowy-sdk-dev-macos]
 [tasks.flowy-sdk-dev-macos]
 category = "Build"
 category = "Build"
 dependencies = ["env_check"]
 dependencies = ["env_check"]
@@ -42,6 +47,32 @@ script = [
 ]
 ]
 script_runner = "@shell"
 script_runner = "@shell"
 
 
+[tasks.sdk-build-android]
+private = true
+script = [
+  """
+    cd rust-lib/
+    rustup show
+    rustup target add aarch64-linux-android \
+      armv7-linux-androideabi \
+      i686-linux-android \
+      x86_64-linux-android
+    DEST=${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/app_flowy/android/app/src/main/jniLibs
+    rm -rf $DEST/arm64-v8a \
+      $DEST/armeabi-v7a \
+      $DEST/x86 \
+      $DEST/x86_64
+    cargo ndk \
+      -t arm64-v8a \
+      -t armeabi-v7a \
+      -t x86 \
+      -t x86_64 \
+      -o $DEST build
+    cd ../
+  """,
+]
+script_runner = "@shell"
+
 [tasks.sdk-build.windows]
 [tasks.sdk-build.windows]
 private = true
 private = true
 script = [
 script = [