Browse Source

ConversionFlutter - Android端转档实现

liuxiaolong 2 years ago
parent
commit
4f169b42ac
36 changed files with 1646 additions and 25 deletions
  1. 11 2
      android/app/build.gradle
  2. BIN
      android/app/libs/ComPDFKitConversion.aar
  3. 32 1
      android/app/src/main/AndroidManifest.xml
  4. 39 0
      android/app/src/main/kotlin/com/compdfkit/conversion/flutter/kmpdfkit_demo/ConversionSDKFlutterPlugin.kt
  5. 207 0
      android/app/src/main/kotlin/com/compdfkit/conversion/flutter/kmpdfkit_demo/ConvertFlutterPlugin.kt
  6. 12 0
      android/app/src/main/kotlin/com/compdfkit/conversion/flutter/kmpdfkit_demo/ConvertType.kt
  7. 10 0
      android/app/src/main/kotlin/com/compdfkit/conversion/flutter/kmpdfkit_demo/MainActivity.kt
  8. 30 0
      android/app/src/main/kotlin/com/compdfkit/conversion/flutter/kmpdfkit_demo/utils/FileUtils.kt
  9. 35 0
      android/app/src/main/res/xml/provider_paths.xml
  10. 1 0
      ios/Flutter/Debug.xcconfig
  11. 1 0
      ios/Flutter/Release.xcconfig
  12. 68 0
      ios/Runner.xcodeproj/project.pbxproj
  13. 3 0
      ios/Runner.xcworkspace/contents.xcworkspacedata
  14. 30 0
      lib/convert_flutter.dart
  15. 22 0
      lib/convert_sdk_info_flutter.dart
  16. 31 22
      lib/main.dart
  17. 39 0
      lib/models/convert_bean.dart
  18. 70 0
      lib/models/convert_contain_options_bean.dart
  19. 15 0
      lib/models/convert_type.dart
  20. 153 0
      lib/states/convert_options_provider.dart
  21. 34 0
      lib/states/convert_provider.dart
  22. 41 0
      lib/widgets/conversion_sdk_info_widget.dart
  23. 146 0
      lib/widgets/convert_config.dart
  24. 61 0
      lib/widgets/convert_list_widget.dart
  25. 2 0
      lib/widgets/convert_type_options_widget.dart
  26. 40 0
      lib/widgets/options/convert_csv_options_widget.dart
  27. 126 0
      lib/widgets/options/convert_excel_options_widget.dart
  28. 89 0
      lib/widgets/options/convert_html_options_widget.dart
  29. 97 0
      lib/widgets/options/convert_image_options_widget.dart
  30. 55 0
      lib/widgets/options/convert_ppt_options_widget.dart
  31. 55 0
      lib/widgets/options/convert_ref_options_widget.dart
  32. 45 0
      lib/widgets/options/convert_word_options_widget.dart
  33. 1 0
      macos/Flutter/Flutter-Debug.xcconfig
  34. 1 0
      macos/Flutter/Flutter-Release.xcconfig
  35. 41 0
      pubspec.lock
  36. 3 0
      pubspec.yaml

+ 11 - 2
android/app/build.gradle

@@ -44,10 +44,10 @@ android {
 
     defaultConfig {
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
-        applicationId "com.compdfkit.conversion.flutter.kmpdfkit_demo"
+        applicationId "com.compdfkit.conversion.flutter"
         // You can update the following values to match your application needs.
         // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
-        minSdkVersion flutter.minSdkVersion
+        minSdkVersion 21
         targetSdkVersion flutter.targetSdkVersion
         versionCode flutterVersionCode.toInteger()
         versionName flutterVersionName
@@ -67,5 +67,14 @@ flutter {
 }
 
 dependencies {
+    implementation fileTree(dir: "libs", include: ["*.jar","*.aar"])
+
+    implementation "androidx.core:core-ktx:1.7.0"
+    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
+    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
+    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
+    api 'androidx.documentfile:documentfile:1.0.1'
+
 }

BIN
android/app/libs/ComPDFKitConversion.aar


+ 32 - 1
android/app/src/main/AndroidManifest.xml

@@ -1,6 +1,16 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.compdfkit.conversion.flutter.kmpdfkit_demo">
-   <application
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+    <queries>
+        <intent>
+            <action android:name="android.intent.action.GET_CONTENT" />
+            <data android:mimeType="*/*"/>
+        </intent>
+    </queries>
+    <application
         android:label="kmpdfkit_demo"
         android:name="${applicationName}"
         android:icon="@mipmap/ic_launcher">
@@ -9,6 +19,7 @@
             android:exported="true"
             android:launchMode="singleTop"
             android:theme="@style/LaunchTheme"
+            android:requestLegacyExternalStorage="true"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
             android:hardwareAccelerated="true"
             android:windowSoftInputMode="adjustResize">
@@ -30,5 +41,25 @@
         <meta-data
             android:name="flutterEmbedding"
             android:value="2" />
+
+        <provider
+            android:name="androidx.core.content.FileProvider"
+            android:authorities="${applicationId}.fileprovider"
+            android:exported="false"
+            android:grantUriPermissions="true">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/provider_paths" />
+        </provider>
+
+        <meta-data
+            android:name="CPDFConverter_key"
+            android:value="V5ZIAt7B3n3/V82MVCuOW2fdPEnldbemWjaAaxz4Gf8G2xkO/OAXarjtstBgqJFjUhe5wWky9rqCQx+Hl8G1vLN9s+obgAMpwPYWrLotFut4F6b4OUMYTsoMT8Lf6iMmnNpU/jETLGibspsVDHRZ+HPRpClAB3sjbpnb24nhZiw=" />
+        <meta-data
+            android:name="CPDFConverter_secret"
+            android:value="mG0c3O3Mzeu5dkZJW3gpqiAYrD3HuVAh2a+2rjOsIRiPkXOBYdZofN07Z1Wrsqj+VrBm8+cjeMi4JbhDcdQ3xcSx+g9ESJiPZnXkiFC2786cM/fgvvcctF5rcqYAH1dpMv7C2jvq4o0q6sQtN3jR43QdIezW2vV5wIeyNjPTddUNBwTRKvRkQ75Y56jVVUcsWiExjnRPpqLnyppZAnYJ1tnsa41fJuMqWbPW2c3qJUemdtKP7E4eFYSl6tjgcetK" />
+
+
+
     </application>
 </manifest>

+ 39 - 0
android/app/src/main/kotlin/com/compdfkit/conversion/flutter/kmpdfkit_demo/ConversionSDKFlutterPlugin.kt

@@ -0,0 +1,39 @@
+package com.compdfkit.conversion.flutter.kmpdfkit_demo
+
+import android.util.Log
+import com.compdfkit.conversion.CPDFConverter
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import java.lang.reflect.Method
+
+/**
+ * @classname:
+ * @author: LiuXiaoLong
+ * @date: 2023/2/23
+ * description:
+ */
+
+class ConversionSDKFlutterPlugin(flutterPlugin: FlutterEngine) : MethodChannel.MethodCallHandler {
+
+    val channelName = "com.compdfkit.conversion.flutter.convert.sdk.info"
+
+    private var channel: MethodChannel
+
+    init {
+        channel = MethodChannel(flutterPlugin.dartExecutor.binaryMessenger, channelName)
+        channel.setMethodCallHandler(this)
+    }
+
+
+    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
+        if (call.method.equals("request_convert_sdk_version")) {
+            result.success(CPDFConverter.getSDKBuildTag())
+        }
+    }
+
+
+}

+ 207 - 0
android/app/src/main/kotlin/com/compdfkit/conversion/flutter/kmpdfkit_demo/ConvertFlutterPlugin.kt

@@ -0,0 +1,207 @@
+package com.compdfkit.conversion.flutter.kmpdfkit_demo
+
+import android.content.Context
+import android.util.Log
+import androidx.lifecycle.lifecycleScope
+import com.compdfkit.conversion.*
+import com.compdfkit.conversion.flutter.kmpdfkit_demo.utils.FileUtils
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.common.EventChannel
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import java.io.File
+import java.lang.reflect.Method
+
+/**
+ * @classname:
+ * @author: LiuXiaoLong
+ * @date: 2023/2/23
+ * description:
+ */
+
+class ConvertFlutterPlugin(var activity: MainActivity, flutterPlugin: FlutterEngine) : EventChannel.StreamHandler {
+
+    val channelName = "com.compdfkit.conversion.flutter.convert"
+    val START_CONVERT = "start_convert"
+    val PARAMS_CONVERT_TYPE = "convertType"
+    val PARAMS_FILE_PATH = "filePath"
+    val PARAMS_FILE_NAME = "fileName"
+    val PARAMS_UPDATE_INDEX = "updateIndex"
+    val PARAMS_CONTAIN_IMAGES = "containImages"
+    val PARAMS_CONTAIN_ANNOTATIONS = "containAnnotations"
+    val PARAMS_WORK_SHEET_OPTIONS = "workSheetOptions"
+    val PARAMS_CONTENTOPTIONS = "contentOptions"
+    val PARAMS_IMAGE_DPI = "imageDpi"
+    val PARAMS_IMAGE_PAGE_OPTIONS = "imagePageOptions"
+    val PARAMS_MERGE_CSV = "mergeCSV"
+    val PARAMS_HTML_PAGE_OPTIONS = "htmlPageOptions"
+
+
+    val CONVERTING = 1
+    val CONVERT_SUCCESS = 2
+    val CONVERT_FAIL = 3
+
+    private var channel: EventChannel
+
+    init {
+        channel = EventChannel(flutterPlugin.dartExecutor.binaryMessenger, channelName)
+        channel.setStreamHandler(this)
+    }
+
+
+    override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
+        Log.e("转档Plugin", "arguments:${arguments}")
+        var convertInfoMap = arguments as Map<String, String>
+        val options = getConvertOptions(convertInfoMap[PARAMS_CONVERT_TYPE] ?: "", convertInfoMap)
+        val updateIndex = convertInfoMap[PARAMS_UPDATE_INDEX]
+        activity.lifecycleScope.launch(Dispatchers.IO) {
+            onConvert(context = activity,
+                filePath = convertInfoMap[PARAMS_FILE_PATH] ?: "",
+                convertType = convertInfoMap[PARAMS_CONVERT_TYPE] ?: "", pageArrays = null, params = options, onHandle = {
+
+                }, onProgress = { current, total ->
+                    val progress = ((current / total.toFloat()) * 1000).toInt() / 10
+                    val process = mapOf("progress" to progress, "updateIndex" to updateIndex, "status" to CONVERTING)
+                    Log.e("转档Plugin", "进度:${progress}")
+                    activity.lifecycleScope.launch(Dispatchers.Main){
+                        events?.success(process)
+
+                    }
+                }, onPost = { code, outFilePath ->
+                    Log.e("转档Plugin", "onPost, code:${code.name}, outputPath:${outFilePath}")
+                    when (code) {
+                        ConvertError.ERR_SUCCESS -> {
+                            val process = mapOf("progress" to 100, "updateIndex" to updateIndex, "status" to CONVERT_SUCCESS, "outputPath" to outFilePath)
+                            activity.lifecycleScope.launch(Dispatchers.Main) {
+                                events?.success(process)
+                                events?.endOfStream()
+
+                            }
+                        }
+                        else -> {
+                            val process = mapOf("progress" to 100, "updateIndex" to updateIndex, "status" to CONVERT_FAIL, "msg" to code.name)
+                            activity.lifecycleScope.launch(Dispatchers.Main) {
+                                events?.success(process)
+                                events?.endOfStream()
+                            }
+                        }
+                    }
+                })
+        }
+
+    }
+
+    override fun onCancel(arguments: Any?) {
+
+    }
+
+
+    private fun getConvertOptions(convertType: String, optionsMap: Map<String, String>): CPDFConvertOptions {
+        val isContainImages = optionsMap[PARAMS_CONTAIN_IMAGES].toBoolean()
+        val isContainAnnotation = optionsMap[PARAMS_CONTAIN_ANNOTATIONS].toBoolean()
+        val workSheetOptions = WorkSheetOptions.valueOf(optionsMap[PARAMS_WORK_SHEET_OPTIONS]?:"")
+        val contentOptions = ContentOptions.valueOf(optionsMap[PARAMS_CONTENTOPTIONS]?:"")
+        val imageDpi = optionsMap[PARAMS_IMAGE_DPI]
+        val imagePageOptions = ImgType.valueOf(optionsMap[PARAMS_IMAGE_PAGE_OPTIONS]?:"PNG")
+        val mergeCSV = optionsMap[PARAMS_MERGE_CSV]?.toBoolean()?:false
+        val htmlPageOptions = PageAndNavigationPaneOptions.valueOf(optionsMap[PARAMS_HTML_PAGE_OPTIONS]?:"")
+        return when (ConvertType.valueOf(convertType.uppercase())) {
+            ConvertType.PPT -> CPDFConvertPPTOptions().also {
+                it.isContainImages = isContainImages
+                it.isContainAnnotations = isContainAnnotation
+            }
+            ConvertType.WORD -> CPDFConvertWordOptions().also {
+                it.isContainImages = isContainImages
+                it.isContainAnnotations = isContainAnnotation
+            }
+            ConvertType.EXCEL -> CPDFConvertExcelOptions().also {
+                it.isContainImages = isContainImages
+                it.isContainAnnotations = isContainAnnotation
+                it.workSheetOptions = workSheetOptions
+                it.contentOptions = contentOptions
+            }
+
+            ConvertType.TXT -> CPDFConvertTxtOptions()
+            ConvertType.IMAGE -> CPDFConvertImgOptions().also {
+                it.isContainAnnotations = isContainAnnotation
+                it.imageDpi = imageDpi?.toInt()?:300
+                it.imgType = imagePageOptions
+            }
+            ConvertType.CSV -> CPDFConvertCsvOptions().also {
+                it.isMergeCsv = mergeCSV
+            }
+            ConvertType.RTF -> CPDFConvertRtfOptions().also {
+                it.isContainImages = isContainImages
+                it.isContainAnnotations = isContainAnnotation
+            }
+            ConvertType.HTML -> CPDFConvertHtmlOptions().also {
+                it.isContainImages = isContainImages
+                it.isContainAnnotations = isContainAnnotation
+                it.pageAndNavigationPaneOptions = htmlPageOptions
+            }
+            else -> CPDFConvertOptions()
+        }
+    }
+
+    var cPDFConvert: CPDFConverter? = null
+
+    fun onConvert(
+        context: Context,
+        filePath: String,
+        convertType: String,
+        pageArrays: IntArray? = null,
+        params: CPDFConvertOptions,
+        onHandle: ((objptr: Long) -> Unit)? = null,
+        onProgress: ((current: Int, total: Int) -> Unit)? = null,
+        onPost: ((code: ConvertError, outFilePath: String?) -> Unit)? = null
+    ): ConvertError {
+        val outputDir = FileUtils.getOutputFilePath(context)
+        val uri = FileUtils.onGetUriBySystem(context, filePath) ?: return ConvertError.ERR_FILE
+        return when (ConvertType.valueOf(convertType.uppercase())) {
+            ConvertType.PPT -> CPDFConverterPPT(context, uri, "")
+                .run {
+                    cPDFConvert = this
+                    convert(outputDir, "", params, pageArrays, onHandle, onProgress, onPost)
+                }
+            ConvertType.WORD -> CPDFConverterWord(context, uri, "")
+                .run {
+                    cPDFConvert = this
+                    convert(outputDir, "", params as CPDFConvertWordOptions, pageArrays, onHandle, onProgress, onPost)
+                }
+            ConvertType.EXCEL -> CPDFConverterExcel(context, uri, "")
+                .run {
+                    cPDFConvert = this
+                    convert(outputDir, "", params as CPDFConvertExcelOptions, pageArrays, onHandle, onProgress, onPost)
+                }
+            ConvertType.TXT -> CPDFConverterTxt(context, uri, "")
+                .run {
+                    cPDFConvert = this
+                    convert(outputDir, "", params as CPDFConvertTxtOptions, pageArrays, onHandle, onProgress, onPost)
+                }
+            ConvertType.IMAGE -> CPDFConverterImg(context, uri, "").run {
+                cPDFConvert = this
+                convert(outputDir, "", params, pageArrays, onHandle, onProgress, onPost)
+            }
+            ConvertType.CSV -> CPDFConverterCsv(context, uri, "").run {
+                cPDFConvert = this
+                convert(outputDir, "", params, pageArrays, onHandle, onProgress, onPost)
+            }
+            ConvertType.RTF -> CPDFConverterRtf(context, uri, "").run {
+                cPDFConvert = this
+                convert(outputDir, "", params, pageArrays, onHandle, onProgress, onPost)
+            }
+            ConvertType.HTML -> CPDFConverterHtml(context, uri, "").run {
+                cPDFConvert = this
+                convert(outputDir, "", params, pageArrays, onHandle, onProgress, onPost)
+            }
+            else -> ConvertError.ERR_UNKNOWN
+        }
+    }
+
+
+}

+ 12 - 0
android/app/src/main/kotlin/com/compdfkit/conversion/flutter/kmpdfkit_demo/ConvertType.kt

@@ -0,0 +1,12 @@
+package com.compdfkit.conversion.flutter.kmpdfkit_demo
+
+/**
+ * @classname:
+ * @author: LiuXiaoLong
+ * @date: 2023/2/23
+ * description:
+ */
+
+enum class ConvertType {
+    PPT, WORD, EXCEL, TXT, IMAGE,CSV,RTF,HTML, NONE
+}

+ 10 - 0
android/app/src/main/kotlin/com/compdfkit/conversion/flutter/kmpdfkit_demo/MainActivity.kt

@@ -1,6 +1,16 @@
 package com.compdfkit.conversion.flutter.kmpdfkit_demo
 
 import io.flutter.embedding.android.FlutterActivity
+import io.flutter.embedding.engine.FlutterEngine
 
 class MainActivity: FlutterActivity() {
+
+
+    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
+        super.configureFlutterEngine(flutterEngine)
+        ConvertFlutterPlugin(this, flutterEngine)
+        ConversionSDKFlutterPlugin(flutterEngine)
+    }
+
+
 }

+ 30 - 0
android/app/src/main/kotlin/com/compdfkit/conversion/flutter/kmpdfkit_demo/utils/FileUtils.kt

@@ -0,0 +1,30 @@
+package com.compdfkit.conversion.flutter.kmpdfkit_demo.utils
+
+import android.content.Context
+import android.net.Uri
+import android.os.Build
+import android.os.Environment
+import androidx.core.content.FileProvider
+import java.io.File
+
+/**
+ * @classname:
+ * @author: LiuXiaoLong
+ * @date: 2023/2/23
+ * description:
+ */
+object FileUtils {
+
+    fun onGetUriBySystem(context: Context, filePath: String?): Uri? {
+        var file = File(filePath)
+        return when {
+            (null == file) || !file.exists() -> null
+            (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) -> Uri.fromFile(file)
+            else -> FileProvider.getUriForFile(context, context.packageName + ".fileprovider", file)
+        }
+    }
+
+    fun getOutputFilePath(context: Context): String {
+        return File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "ConPDFKitConversion").absolutePath
+    }
+}

+ 35 - 0
android/app/src/main/res/xml/provider_paths.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths>
+    <!--
+        files-path             :  Context.getFilesDir()
+        cache-path             :  Context.getCacheDir()
+        external-path          :  Environment.getExternalStorageDirectory()
+        external-files-path    :  Context.getExternalFilesDir(null)
+        external-cache-path    :  Context.getExternalCacheDir()
+        root-path              :  外置sd卡的路径
+    -->
+
+    <files-path
+        name="pdf_convert_files"
+        path="."/>
+
+    <cache-path
+        name="pdf_convert_caches"
+        path="."/>
+
+    <external-path
+        name="pdf_convert_external_root"
+        path="."/>
+
+    <external-files-path
+        name="pdf_convert_external_files"
+        path="."/>
+
+    <external-cache-path
+        name="pdf_convert_external_cache"
+        path="."/>
+
+    <root-path
+        name="pdf_convert_sdcard_files"
+        path="." />
+</paths>

+ 1 - 0
ios/Flutter/Debug.xcconfig

@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
 #include "Generated.xcconfig"

+ 1 - 0
ios/Flutter/Release.xcconfig

@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
 #include "Generated.xcconfig"

+ 68 - 0
ios/Runner.xcodeproj/project.pbxproj

@@ -8,6 +8,7 @@
 
 /* Begin PBXBuildFile section */
 		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+		213CDB98C21625DCD1162CCC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 059E20BC1E428EEB5CDC6568 /* Pods_Runner.framework */; };
 		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
 		74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
 		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
@@ -29,9 +30,13 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+		059E20BC1E428EEB5CDC6568 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
 		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+		2D01BD4E557C05A8D5B08EE5 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
 		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+		50B8585759C13AC272A61A9D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
+		633A684658B71F515E893868 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
 		74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
 		74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
@@ -49,12 +54,21 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				213CDB98C21625DCD1162CCC /* Pods_Runner.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		66B65B34F846DA13528D227B /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				059E20BC1E428EEB5CDC6568 /* Pods_Runner.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
 		9740EEB11CF90186004384FC /* Flutter */ = {
 			isa = PBXGroup;
 			children = (
@@ -72,6 +86,8 @@
 				9740EEB11CF90186004384FC /* Flutter */,
 				97C146F01CF9000F007C117D /* Runner */,
 				97C146EF1CF9000F007C117D /* Products */,
+				ACC0A6894496D2B2A7FA3A96 /* Pods */,
+				66B65B34F846DA13528D227B /* Frameworks */,
 			);
 			sourceTree = "<group>";
 		};
@@ -98,6 +114,17 @@
 			path = Runner;
 			sourceTree = "<group>";
 		};
+		ACC0A6894496D2B2A7FA3A96 /* Pods */ = {
+			isa = PBXGroup;
+			children = (
+				2D01BD4E557C05A8D5B08EE5 /* Pods-Runner.debug.xcconfig */,
+				633A684658B71F515E893868 /* Pods-Runner.release.xcconfig */,
+				50B8585759C13AC272A61A9D /* Pods-Runner.profile.xcconfig */,
+			);
+			name = Pods;
+			path = Pods;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
@@ -105,12 +132,14 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
 			buildPhases = (
+				5A5835082C08C78DF9A97685 /* [CP] Check Pods Manifest.lock */,
 				9740EEB61CF901F6004384FC /* Run Script */,
 				97C146EA1CF9000F007C117D /* Sources */,
 				97C146EB1CF9000F007C117D /* Frameworks */,
 				97C146EC1CF9000F007C117D /* Resources */,
 				9705A1C41CF9048500538489 /* Embed Frameworks */,
 				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+				CEA91C2E89EAD5965A4C6095 /* [CP] Embed Pods Frameworks */,
 			);
 			buildRules = (
 			);
@@ -184,6 +213,28 @@
 			shellPath = /bin/sh;
 			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
 		};
+		5A5835082C08C78DF9A97685 /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
 		9740EEB61CF901F6004384FC /* Run Script */ = {
 			isa = PBXShellScriptBuildPhase;
 			alwaysOutOfDate = 1;
@@ -199,6 +250,23 @@
 			shellPath = /bin/sh;
 			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
 		};
+		CEA91C2E89EAD5965A4C6095 /* [CP] Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+			);
+			name = "[CP] Embed Pods Frameworks";
+			outputFileListPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */

+ 3 - 0
ios/Runner.xcworkspace/contents.xcworkspacedata

@@ -4,4 +4,7 @@
    <FileRef
       location = "group:Runner.xcodeproj">
    </FileRef>
+   <FileRef
+      location = "group:Pods/Pods.xcodeproj">
+   </FileRef>
 </Workspace>

+ 30 - 0
lib/convert_flutter.dart

@@ -0,0 +1,30 @@
+import 'package:ConversionFlutterDemo/models/convert_bean.dart';
+import 'package:ConversionFlutterDemo/models/convert_contain_options_bean.dart';
+import 'package:ConversionFlutterDemo/states/convert_provider.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/services.dart';
+import 'package:provider/provider.dart';
+
+class ConvertFlutter {
+  static const EventChannel _channel =
+      EventChannel('com.compdfkit.conversion.flutter.convert');
+
+  static void convert(BuildContext context, int index, ConvertBean bean,
+      ConvertOptionsBean optionsBean) {
+    _channel.receiveBroadcastStream(optionsBean.getOptions(index, bean)).listen((event) {
+      Map<dynamic, dynamic> result = event;
+      int updateIndex = int.parse(result['updateIndex']);
+      int progress = result['progress'];
+      int status = result['status'];
+      context
+          .read<ConvertProvider>()
+          .updateStatus(updateIndex, progress, status, null);
+      if (status == convertSuccess) {
+        print("转档成功:${result['outputPath']}");
+        context
+            .read<ConvertProvider>()
+            .updateStatus(updateIndex, progress, status, result['outputPath']);
+      }
+    }, onDone: () {}, onError: (object) {});
+  }
+}

+ 22 - 0
lib/convert_sdk_info_flutter.dart

@@ -0,0 +1,22 @@
+
+import 'package:flutter/services.dart';
+
+class ConvertSDKInfoFlutter{
+
+
+  static const MethodChannel _channel =
+  MethodChannel('com.compdfkit.conversion.flutter.convert.sdk.info');
+
+
+
+  static Future<String> getConversionSDKVersion() async {
+    var versionInfo = await _channel.invokeMethod('request_convert_sdk_version' );
+    return versionInfo;
+  }
+
+
+
+
+
+
+}

+ 31 - 22
lib/main.dart

@@ -1,7 +1,19 @@
+import 'package:ConversionFlutterDemo/states/convert_options_provider.dart';
+import 'package:ConversionFlutterDemo/states/convert_provider.dart';
+import 'package:ConversionFlutterDemo/widgets/conversion_sdk_info_widget.dart';
+import 'package:ConversionFlutterDemo/widgets/convert_config.dart';
+import 'package:ConversionFlutterDemo/widgets/convert_list_widget.dart';
 import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
 
 void main() {
-  runApp(const MyApp());
+  runApp(MultiProvider(
+    providers: [
+      ChangeNotifierProvider(create: (_) => ConvertProvider()),
+      ChangeNotifierProvider(create: (_) => ConvertOptionsProvider())
+    ],
+    child: const MyApp(),
+  ));
 }
 
 class MyApp extends StatelessWidget {
@@ -10,12 +22,11 @@ class MyApp extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return MaterialApp(
-      title: 'ComPDFKitConversionDemo',
-      theme: ThemeData(
-        primarySwatch: Colors.blue,
-      ),
-      home: const MyHomePage(title: 'ComPDFKitConversionDemo'),
-    );
+        title: 'ComPDFKitConversionDemo',
+        theme: ThemeData(
+          primarySwatch: Colors.blue,
+        ),
+        home: const MyHomePage(title: 'ComPDFKitConversionDemo'));
   }
 }
 
@@ -29,24 +40,22 @@ class MyHomePage extends StatefulWidget {
 }
 
 class _MyHomePageState extends State<MyHomePage> {
-
   @override
   Widget build(BuildContext context) {
-
     return Scaffold(
-      appBar: AppBar(
-        title: Text(widget.title),
-      ),
-      body: Center(
-        child: Column(
-          mainAxisAlignment: MainAxisAlignment.center,
-          children: <Widget>[
-            const Text(
-              'The flutter version uses the conversion sdk demo',
-            ),
-          ],
+        appBar: AppBar(
+          title: Text(widget.title),
         ),
-      ),
-    );
+        body: Padding(
+          padding: EdgeInsets.all(8),
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: const <Widget>[
+              ConversionSdkInfoWidget(),
+              ConvertConfigWidget(),
+              Expanded(child: ConvertListWidget())
+            ],
+          ),
+        ));
   }
 }

+ 39 - 0
lib/models/convert_bean.dart

@@ -0,0 +1,39 @@
+
+
+
+const convertConverting = 1;
+
+const convertSuccess = 2;
+
+const convertFail = 3;
+
+
+class ConvertBean{
+  String filePath;
+  String fileName;
+  String convertType;
+  int status;
+  int progress;
+  String? outputPath;
+
+
+
+  ConvertBean({required this.filePath, required this.fileName, required this.convertType, required this.status, required this.progress, this.outputPath});
+  
+
+
+  String getStatusName(){
+    switch(status){
+      case convertConverting:
+        return "Converting ($progress%)";
+      case convertSuccess :
+        return "SUCCESS";
+      case convertFail:
+        return "FAIL";
+      default :
+        return "";
+    }
+  }
+
+
+}

+ 70 - 0
lib/models/convert_contain_options_bean.dart

@@ -0,0 +1,70 @@
+import 'package:ConversionFlutterDemo/models/convert_bean.dart';
+
+class ConvertOptionsBean {
+
+
+  bool containImages;
+  bool containAnnotations;
+
+  //convert excel only
+  ExcelWorkSheetOptions workSheetOptions;
+
+  //convert excel only
+  ContentOptions contentOptions = ContentOptions.AllContent;
+
+  //convert image only, 1~1000
+  int imageDpi = 300;
+
+  //convert image only
+  ImagePageOptions imagePageOptions;
+
+  //convert csv only
+  bool mergeCSV = false;
+
+  //convert html only
+  HtmlPageOptions htmlPageOptions;
+
+  ConvertOptionsBean(
+      {this.containImages = true,
+      this.containAnnotations = true,
+      this.workSheetOptions = ExcelWorkSheetOptions.ForEachPage,
+      this.contentOptions = ContentOptions.AllContent,
+      this.imageDpi = 300,
+      this.imagePageOptions = ImagePageOptions.PNG,
+      this.mergeCSV = false,
+      this.htmlPageOptions = HtmlPageOptions.SinglePage});
+
+
+  Map<String, String> getOptions(int index, ConvertBean bean) {
+    return {
+      'updateIndex' : index.toString(),
+      'convertType' : bean.convertType,
+      'filePath' : bean.filePath,
+      'fileName' : bean.fileName,
+      'containImages': containImages.toString(),
+      'containAnnotations': containAnnotations.toString(),
+      'workSheetOptions' : workSheetOptions.name,
+      'contentOptions' : contentOptions.name,
+      'imageDpi' : imageDpi.toString(),
+      'imagePageOptions' : imagePageOptions.name,
+      'mergeCSV' : mergeCSV.toString(),
+      'htmlPageOptions' : htmlPageOptions.name
+    };
+  }
+}
+
+enum ExcelWorkSheetOptions { ForEachTable, ForEachPage, ForTheDocument }
+
+enum ContentOptions { OnlyText, OnlyTable, AllContent }
+
+enum ImagePageOptions {
+  JPEG,
+  PNG,
+}
+
+enum HtmlPageOptions {
+  SinglePage,
+  SinglePageNavigationByBookmark,
+  MultiplePages,
+  MultiplePagesSplitByBookmarks
+}

+ 15 - 0
lib/models/convert_type.dart

@@ -0,0 +1,15 @@
+
+
+enum ConvertType{
+
+  word,
+  ppt,
+  excel,
+  txt,
+  image,
+  csv,
+  rtf,
+  html,
+  none,
+
+}

+ 153 - 0
lib/states/convert_options_provider.dart

@@ -0,0 +1,153 @@
+import 'package:ConversionFlutterDemo/models/convert_contain_options_bean.dart';
+import 'package:ConversionFlutterDemo/models/convert_type.dart';
+import 'package:ConversionFlutterDemo/widgets/options/convert_excel_options_widget.dart';
+import 'package:flutter/material.dart';
+
+class ConvertOptionsProvider extends ChangeNotifier {
+  ConvertType convertType = ConvertType.none;
+
+  ConvertOptionsBean pptOptions =
+      ConvertOptionsBean(containImages: true, containAnnotations: true);
+
+  ConvertOptionsBean wordOptions =
+      ConvertOptionsBean(containImages: true, containAnnotations: true);
+
+  ConvertOptionsBean excelOptions = ConvertOptionsBean(
+      containImages: true,
+      containAnnotations: false,
+      workSheetOptions: ExcelWorkSheetOptions.ForEachPage,
+      contentOptions: ContentOptions.AllContent);
+
+  ConvertOptionsBean imageOptions = ConvertOptionsBean(
+      containImages: true,
+      containAnnotations: true,
+      imageDpi: 300,
+      imagePageOptions: ImagePageOptions.PNG);
+
+  ConvertOptionsBean csvOptions = ConvertOptionsBean(mergeCSV: false);
+
+  ConvertOptionsBean rtfOptions =
+      ConvertOptionsBean(containImages: true, containAnnotations: true);
+
+  ConvertOptionsBean htmlOptions = ConvertOptionsBean(
+      containImages: true,
+      containAnnotations: false,
+      htmlPageOptions: HtmlPageOptions.SinglePage);
+
+  ConvertOptionsBean getOptions() {
+    switch (convertType) {
+      case ConvertType.ppt:
+        return pptOptions;
+      case ConvertType.word:
+        return wordOptions;
+      case ConvertType.excel:
+        return excelOptions;
+      case ConvertType.txt:
+        return ConvertOptionsBean();
+      case ConvertType.image:
+        return imageOptions;
+      case ConvertType.csv:
+        return csvOptions;
+      case ConvertType.rtf:
+        return rtfOptions;
+      case ConvertType.html:
+        return htmlOptions;
+      default:
+        return ConvertOptionsBean();
+    }
+  }
+
+  void changeConvertType(ConvertType convertType) {
+    this.convertType = convertType;
+    notifyListeners();
+  }
+
+  void changePPTOptions({bool? containImages, bool? containAnnotations}) {
+    if (containImages != null) {
+      pptOptions.containImages = containImages;
+    }
+    if (containAnnotations != null) {
+      pptOptions.containAnnotations = containAnnotations;
+    }
+    notifyListeners();
+  }
+
+  void changeWordOptions({bool? containImages, bool? containAnnotations}) {
+    if (containImages != null) {
+      wordOptions.containImages = containImages;
+    }
+    if (containAnnotations != null) {
+      wordOptions.containAnnotations = containAnnotations;
+    }
+    notifyListeners();
+  }
+
+  void changeExcelOptions(
+      {bool? containImages,
+      bool? containAnnotations,
+      ExcelWorkSheetOptions? workSheetOptions,
+      ContentOptions? contentOptions}) {
+    if (containImages != null) {
+      excelOptions.containImages = containImages;
+    }
+    if (containAnnotations != null) {
+      excelOptions.containAnnotations = containAnnotations;
+    }
+    if (workSheetOptions != null) {
+      excelOptions.workSheetOptions = workSheetOptions;
+    }
+    if (contentOptions != null) {
+      excelOptions.contentOptions = contentOptions;
+    }
+    notifyListeners();
+  }
+
+  void changeImageOptions(
+      {bool? containImages,
+      bool? containAnnotations,
+      int? imageDpi,
+      ImagePageOptions? imagePageOptions}) {
+    if (containImages != null) {
+      imageOptions.containImages = containImages;
+    }
+    if (containAnnotations != null) {
+      imageOptions.containAnnotations = containAnnotations;
+    }
+    if (imageDpi != null) {
+      imageOptions.imageDpi = imageDpi;
+    }
+    if (imagePageOptions != null) {
+      imageOptions.imagePageOptions = imagePageOptions;
+    }
+    notifyListeners();
+  }
+
+  void changeCSVOptions(bool mergeCSV) {
+    csvOptions.mergeCSV = mergeCSV;
+    notifyListeners();
+  }
+
+  void changeRTFOptions({bool? containImages, bool? containAnnotations}) {
+    if (containImages != null) {
+      rtfOptions.containImages = containImages;
+    }
+    if (containAnnotations != null) {
+      rtfOptions.containAnnotations = containAnnotations;
+    }
+    notifyListeners();
+  }
+
+  void changeHtmlOptions({bool? containImages, bool? containAnnotations,
+      HtmlPageOptions? htmlPageOptions}) {
+    if (containImages != null) {
+      htmlOptions.containImages = containImages;
+    }
+    if (containAnnotations != null) {
+      htmlOptions.containAnnotations = containAnnotations;
+    }
+    if (htmlPageOptions != null) {
+      htmlOptions.htmlPageOptions = htmlPageOptions;
+    }
+    notifyListeners();
+  }
+}

+ 34 - 0
lib/states/convert_provider.dart

@@ -0,0 +1,34 @@
+import 'package:ConversionFlutterDemo/convert_flutter.dart';
+import 'package:ConversionFlutterDemo/models/convert_contain_options_bean.dart';
+import 'package:ConversionFlutterDemo/models/convert_type.dart';
+import 'package:flutter/material.dart';
+import '../models/convert_bean.dart';
+import 'package:path/path.dart';
+
+class ConvertProvider extends ChangeNotifier {
+  List<ConvertBean> _items = [];
+
+  List<ConvertBean> get items => _items;
+
+  void addConvertData(BuildContext context, String filePath,
+      ConvertType convertType, ConvertOptionsBean options) {
+    ConvertBean convertBean = ConvertBean(
+        filePath: filePath,
+        fileName: basename(filePath),
+        convertType: convertType.name,
+        status: convertConverting,
+        progress: 0);
+    _items.add(convertBean);
+    ConvertFlutter.convert(context, _items.length - 1, convertBean, options);
+    notifyListeners();
+  }
+
+  void updateStatus(
+      int position, int progress, int status, String? outputPath) {
+    _items[position]
+      ..status = status
+      ..progress = progress
+      ..outputPath = outputPath;
+    notifyListeners();
+  }
+}

+ 41 - 0
lib/widgets/conversion_sdk_info_widget.dart

@@ -0,0 +1,41 @@
+import 'package:ConversionFlutterDemo/convert_sdk_info_flutter.dart';
+import 'package:flutter/material.dart';
+
+class ConversionSdkInfoWidget extends StatefulWidget {
+  const ConversionSdkInfoWidget({Key? key}) : super(key: key);
+
+
+
+  @override
+  State<ConversionSdkInfoWidget> createState() =>
+      _ConversionSdkInfoWidgetState();
+}
+
+class _ConversionSdkInfoWidgetState extends State<ConversionSdkInfoWidget> {
+
+  String conversionSDKVersion = "Version:";
+
+  @override
+  void initState() {
+    super.initState();
+
+     ConvertSDKInfoFlutter.getConversionSDKVersion().then((value) {
+       setState(() {
+          conversionSDKVersion = "Version:$value";
+       });
+
+    });
+  }
+
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Text(conversionSDKVersion),
+        Text('OutputPath:xxx')
+      ],
+    );
+  }
+}

+ 146 - 0
lib/widgets/convert_config.dart

@@ -0,0 +1,146 @@
+import 'package:ConversionFlutterDemo/models/convert_contain_options_bean.dart';
+import 'package:ConversionFlutterDemo/models/convert_type.dart';
+import 'package:ConversionFlutterDemo/states/convert_options_provider.dart';
+import 'package:ConversionFlutterDemo/widgets/options/convert_csv_options_widget.dart';
+import 'package:ConversionFlutterDemo/widgets/options/convert_excel_options_widget.dart';
+import 'package:ConversionFlutterDemo/widgets/options/convert_html_options_widget.dart';
+import 'package:ConversionFlutterDemo/widgets/options/convert_image_options_widget.dart';
+import 'package:ConversionFlutterDemo/widgets/options/convert_ppt_options_widget.dart';
+import 'package:ConversionFlutterDemo/widgets/options/convert_ref_options_widget.dart';
+import 'package:ConversionFlutterDemo/widgets/options/convert_word_options_widget.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_document_picker/flutter_document_picker.dart';
+import 'package:provider/provider.dart';
+
+import '../states/convert_provider.dart';
+
+class ConvertConfigWidget extends StatefulWidget {
+  const ConvertConfigWidget({Key? key}) : super(key: key);
+
+  @override
+  State<ConvertConfigWidget> createState() => _ConvertConfigWidgetState();
+}
+
+class _ConvertConfigWidgetState extends State<ConvertConfigWidget> {
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      children: [
+        Row(
+          children: [
+            const Text('ConvertType:'),
+            Consumer<ConvertOptionsProvider>(
+                builder: (context, optionsProvider, child) {
+              return Expanded(
+                  child: TextButton(
+                child: Text(
+                  optionsProvider.convertType == ConvertType.none
+                      ? 'Please click to select a conversion type'
+                      : optionsProvider.convertType.name,
+                  style: const TextStyle(fontSize: 12),
+                ),
+                onPressed: () async {
+                  _selectConvertType(context);
+                },
+              ));
+            }),
+            IconButton(
+                onPressed: () {
+                  ConvertType convertType =
+                      context.read<ConvertOptionsProvider>().convertType;
+                  _showConvertOptionsModalBottomSheet(context, convertType);
+                },
+                icon: const Icon(Icons.settings))
+          ],
+        ),
+        SizedBox(
+            width: double.infinity,
+            child: ElevatedButton(
+                onPressed: () {
+                  ConvertType convertType =
+                      context.read<ConvertOptionsProvider>().convertType;
+                  if (convertType != ConvertType.none) {
+                    getPDFFileList(context);
+                  }
+                },
+                child: const Text('Convert')))
+      ],
+    );
+  }
+
+  void getPDFFileList(BuildContext context) async {
+    FlutterDocumentPickerParams? params = FlutterDocumentPickerParams(
+        allowedFileExtensions: ['pdf'], allowedMimeTypes: ['application/pdf']);
+    String? path = await FlutterDocumentPicker.openDocument(params: params);
+
+    if (path != null) {
+      ConvertOptionsBean options =
+          context.read<ConvertOptionsProvider>().getOptions();
+      ConvertType convertType =
+          context.read<ConvertOptionsProvider>().convertType;
+      context
+          .read<ConvertProvider>()
+          .addConvertData(context, path, convertType, options);
+    }
+  }
+
+  void _showConvertOptionsModalBottomSheet(context, ConvertType convertType) {
+    showModalBottomSheet<int>(
+        context: context,
+        isScrollControlled: false,
+        builder: (BuildContext context) {
+          switch (convertType) {
+            case ConvertType.ppt:
+              return const ConvertPPTOptionsWidget();
+            case ConvertType.word:
+              return const ConvertWordOptionsWidget();
+            case ConvertType.excel:
+              return const ConvertExcelOptionsWidget();
+            case ConvertType.image:
+              return const ConvertImageOptionsWidget();
+            case ConvertType.csv:
+              return const ConvertCSVOptionsWidget();
+            case ConvertType.rtf:
+              return const ConvertRTFOptionsWidget();
+            case ConvertType.html:
+              return const ConvertHtmlOptionsWidget();
+            default:
+              return const Padding(
+                  padding: EdgeInsets.all(16),
+                  child: Text('Options not support'));
+          }
+        });
+  }
+
+  void _selectConvertType(context) {
+    List<ConvertType> convertList = [
+      ConvertType.ppt,
+      ConvertType.word,
+      ConvertType.excel,
+      ConvertType.txt,
+      ConvertType.image,
+      ConvertType.csv,
+      ConvertType.rtf,
+      ConvertType.html
+    ];
+
+    showModalBottomSheet(
+        context: context,
+        builder: (BuildContext context) {
+          return ListView.builder(
+              itemCount: convertList.length,
+              itemBuilder: (BuildContext context, int index) {
+                ConvertType type = convertList[index];
+                return ListTile(
+                  title: Text(type.name),
+                  onTap: () {
+                    context
+                        .read<ConvertOptionsProvider>()
+                        .changeConvertType(type);
+                    Navigator.of(context).pop();
+                  },
+                );
+              });
+        });
+  }
+}

+ 61 - 0
lib/widgets/convert_list_widget.dart

@@ -0,0 +1,61 @@
+import 'package:ConversionFlutterDemo/models/convert_bean.dart';
+import 'package:ConversionFlutterDemo/states/convert_provider.dart';
+import 'package:flutter/material.dart';
+import 'package:open_file/open_file.dart';
+import 'package:provider/provider.dart';
+
+class ConvertListWidget extends StatefulWidget {
+  const ConvertListWidget({Key? key}) : super(key: key);
+
+  @override
+  State<ConvertListWidget> createState() => _ConvertListWidgetState();
+}
+
+class _ConvertListWidgetState extends State<ConvertListWidget> {
+  @override
+  Widget build(BuildContext context) {
+    var count =
+        context.select<ConvertProvider, int>((value) => value.items.length);
+    return ListView.builder(
+      itemCount: count,
+      primary: true,
+      shrinkWrap: true,
+      physics: new ClampingScrollPhysics(),
+      itemBuilder: (BuildContext context, int index) {
+        //设置子条目
+        return Builder(builder: (context) {
+          ConvertBean bean = context.select<ConvertProvider, ConvertBean>(
+              (value) => value.items[index]);
+          context.select<ConvertProvider, int>(
+              (value) => value.items[index].progress);
+          int status = context.select<ConvertProvider, int>(
+              (value) => value.items[index].status);
+          return Column(
+            children: [
+              ListTile(
+                isThreeLine: true,
+                title: Text(
+                  bean.fileName,
+                  style: const TextStyle(fontSize: 18),
+                ),
+                subtitle: Text(
+                  "PDF转${bean.convertType.toUpperCase()} ${bean.outputPath != null ? "\n${bean.outputPath}" : ''}",
+                  style: const TextStyle(fontSize: 12),
+                ),
+                trailing: Text(bean.getStatusName()),
+                // item 直观感受是整体大小
+                contentPadding:
+                    const EdgeInsets.symmetric(horizontal: 12, vertical: 2),
+                onTap: () {
+                  if (status == convertSuccess) {
+                    OpenFile.open(bean.outputPath);
+                  }
+                },
+              ),
+            ],
+          );
+        });
+      },
+    );
+  }
+}

+ 2 - 0
lib/widgets/convert_type_options_widget.dart

@@ -0,0 +1,2 @@
+import 'package:flutter/material.dart';
+

+ 40 - 0
lib/widgets/options/convert_csv_options_widget.dart

@@ -0,0 +1,40 @@
+import 'package:ConversionFlutterDemo/models/convert_contain_options_bean.dart';
+import 'package:ConversionFlutterDemo/states/convert_options_provider.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+class ConvertCSVOptionsWidget extends StatefulWidget {
+  const ConvertCSVOptionsWidget({Key? key}) : super(key: key);
+
+  @override
+  State<ConvertCSVOptionsWidget> createState() =>
+      _ConvertCSVOptionsWidgetState();
+}
+
+class _ConvertCSVOptionsWidgetState extends State<ConvertCSVOptionsWidget> {
+  @override
+  Widget build(BuildContext context) {
+    bool mergeCSV = context.select<ConvertOptionsProvider, bool>(
+        (value) => value.csvOptions.mergeCSV);
+    return Padding(
+        padding: EdgeInsets.all(8),
+        child: Column(
+          mainAxisSize: MainAxisSize.min,
+          children: [
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                const Text('MergeCSV'),
+                Switch(
+                    value: mergeCSV,
+                    onChanged: (value) {
+                      context
+                          .read<ConvertOptionsProvider>()
+                          .changeCSVOptions(value);
+                    })
+              ],
+            ),
+          ],
+        ));
+  }
+}

+ 126 - 0
lib/widgets/options/convert_excel_options_widget.dart

@@ -0,0 +1,126 @@
+import 'package:ConversionFlutterDemo/models/convert_contain_options_bean.dart';
+import 'package:ConversionFlutterDemo/states/convert_options_provider.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+class ConvertExcelOptionsWidget extends StatefulWidget {
+  const ConvertExcelOptionsWidget({Key? key}) : super(key: key);
+
+  @override
+  State<ConvertExcelOptionsWidget> createState() =>
+      _ConvertExcelOptionsWidgetState();
+}
+
+class _ConvertExcelOptionsWidgetState extends State<ConvertExcelOptionsWidget> {
+  @override
+  Widget build(BuildContext context) {
+    bool containImages = context.select<ConvertOptionsProvider, bool>(
+        (value) => value.excelOptions.containImages);
+    bool containAnnotations = context.select<ConvertOptionsProvider, bool>(
+        (value) => value.excelOptions.containAnnotations);
+
+    return SingleChildScrollView(child: Padding(
+        padding: EdgeInsets.all(8),
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                const Text('ContainImages'),
+                Switch(
+                    value: containImages,
+                    onChanged: (value) {
+                      context
+                          .read<ConvertOptionsProvider>()
+                          .changeExcelOptions(containImages: value);
+                    })
+              ],
+            ),
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                const Text('ContainAnnotations'),
+                Switch(
+                    value: containAnnotations,
+                    onChanged: (value) {
+                      context
+                          .read<ConvertOptionsProvider>()
+                          .changeExcelOptions(containAnnotations: value);
+                    })
+              ],
+            ),
+            excelWorkSheetOptions((value) {
+              context
+                  .read<ConvertOptionsProvider>()
+                  .changeExcelOptions(workSheetOptions: value);
+            }),
+            excelContentOptions((value) {
+              context
+                  .read<ConvertOptionsProvider>()
+                  .changeExcelOptions(contentOptions: value);
+            })
+          ],
+        )),);
+  }
+
+  Widget excelWorkSheetOptions(ValueChanged<ExcelWorkSheetOptions> onChanged) {
+    ExcelWorkSheetOptions workSheetOptions =
+        context.select<ConvertOptionsProvider, ExcelWorkSheetOptions>(
+            (value) => value.excelOptions.workSheetOptions);
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Text('WorkSheetOptions:'),
+        _excelWorkSheetOptionsItem(
+            workSheetOptions, ExcelWorkSheetOptions.ForEachTable, onChanged),
+        _excelWorkSheetOptionsItem(
+            workSheetOptions, ExcelWorkSheetOptions.ForEachPage, onChanged),
+        _excelWorkSheetOptionsItem(
+            workSheetOptions, ExcelWorkSheetOptions.ForTheDocument, onChanged),
+      ],
+    );
+  }
+
+  Widget _excelWorkSheetOptionsItem(
+      ExcelWorkSheetOptions currentWorkSheetOptions,
+      ExcelWorkSheetOptions options,
+      ValueChanged<ExcelWorkSheetOptions> onChanged) {
+    return CheckboxListTile(
+        controlAffinity: ListTileControlAffinity.leading,
+        title: Text(options.name),
+        value: currentWorkSheetOptions == options,
+        onChanged: (value) {
+          onChanged(options);
+        });
+  }
+
+  Widget excelContentOptions(ValueChanged<ContentOptions> onChanged) {
+    ContentOptions contentOptions =
+        context.select<ConvertOptionsProvider, ContentOptions>(
+            (value) => value.excelOptions.contentOptions);
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Text('ContentOptions:'),
+        _excelContentOptionsItem(
+            contentOptions, ContentOptions.OnlyText, onChanged),
+        _excelContentOptionsItem(
+            contentOptions, ContentOptions.OnlyTable, onChanged),
+        _excelContentOptionsItem(
+            contentOptions, ContentOptions.AllContent, onChanged),
+      ],
+    );
+  }
+
+  Widget _excelContentOptionsItem(ContentOptions currentOptions,
+      ContentOptions options, ValueChanged<ContentOptions> onChanged) {
+    return CheckboxListTile(
+        controlAffinity: ListTileControlAffinity.leading,
+        title: Text(options.name),
+        value: currentOptions == options,
+        onChanged: (value) {
+          onChanged(options);
+        });
+  }
+}

+ 89 - 0
lib/widgets/options/convert_html_options_widget.dart

@@ -0,0 +1,89 @@
+import 'package:ConversionFlutterDemo/models/convert_contain_options_bean.dart';
+import 'package:ConversionFlutterDemo/states/convert_options_provider.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+class ConvertHtmlOptionsWidget extends StatefulWidget {
+  const ConvertHtmlOptionsWidget({Key? key}) : super(key: key);
+
+  @override
+  State<ConvertHtmlOptionsWidget> createState() =>
+      _ConvertHtmlOptionsWidgetState();
+}
+
+class _ConvertHtmlOptionsWidgetState extends State<ConvertHtmlOptionsWidget> {
+  @override
+  Widget build(BuildContext context) {
+    bool containImages = context.select<ConvertOptionsProvider, bool>(
+        (value) => value.htmlOptions.containImages);
+    bool containAnnotations = context.select<ConvertOptionsProvider, bool>(
+        (value) => value.htmlOptions.containAnnotations);
+    HtmlPageOptions htmlPageOptions = context.select<ConvertOptionsProvider, HtmlPageOptions>(
+            (value) => value.htmlOptions.htmlPageOptions);
+    return Padding(
+        padding: EdgeInsets.all(8),
+        child: Column(
+          mainAxisSize: MainAxisSize.min,
+          children: [
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                const Text('ContainImages'),
+                Switch(
+                    value: containImages,
+                    onChanged: (value) {
+                      context
+                          .read<ConvertOptionsProvider>()
+                          .changeHtmlOptions(containImages: value);
+                    })
+              ],
+            ),
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                const Text('ContainAnnotations'),
+                Switch(
+                    value: containAnnotations,
+                    onChanged: (value) {
+                      context
+                          .read<ConvertOptionsProvider>()
+                          .changeHtmlOptions(containAnnotations: value);
+                    })
+              ],
+            )
+          ],
+        ));
+  }
+
+
+  Widget htmlPageOptions(ValueChanged<HtmlPageOptions> onChanged) {
+    HtmlPageOptions contentOptions =
+    context.select<ConvertOptionsProvider, HtmlPageOptions>(
+            (value) => value.htmlOptions.htmlPageOptions);
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Text('Page Options:'),
+        _htmlPageOptionsItem(
+            contentOptions, HtmlPageOptions.SinglePage, onChanged),
+        _htmlPageOptionsItem(
+            contentOptions, HtmlPageOptions.SinglePageNavigationByBookmark, onChanged),
+        _htmlPageOptionsItem(
+            contentOptions, HtmlPageOptions.MultiplePages, onChanged),
+        _htmlPageOptionsItem(
+            contentOptions, HtmlPageOptions.MultiplePagesSplitByBookmarks, onChanged),
+      ],
+    );
+  }
+
+  Widget _htmlPageOptionsItem(HtmlPageOptions currentOptions,
+      HtmlPageOptions options, ValueChanged<HtmlPageOptions> onChanged) {
+    return CheckboxListTile(
+        controlAffinity: ListTileControlAffinity.leading,
+        title: Text(options.name),
+        value: currentOptions == options,
+        onChanged: (value) {
+          onChanged(options);
+        });
+  }
+}

+ 97 - 0
lib/widgets/options/convert_image_options_widget.dart

@@ -0,0 +1,97 @@
+import 'package:ConversionFlutterDemo/models/convert_contain_options_bean.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:provider/provider.dart';
+
+import '../../states/convert_options_provider.dart';
+
+class ConvertImageOptionsWidget extends StatefulWidget {
+  const ConvertImageOptionsWidget({Key? key}) : super(key: key);
+
+  @override
+  State<ConvertImageOptionsWidget> createState() =>
+      _ConvertImageOptionsWidgetState();
+}
+
+class _ConvertImageOptionsWidgetState extends State<ConvertImageOptionsWidget> {
+  @override
+  Widget build(BuildContext context) {
+    bool containAnnotations = context.select<ConvertOptionsProvider, bool>(
+        (value) => value.imageOptions.containAnnotations);
+
+    return Padding(
+      padding: EdgeInsets.all(8),
+      child: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Row(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            children: [
+              const Text('ContainAnnotations'),
+              Switch(
+                  value: containAnnotations,
+                  onChanged: (value) {
+                    context
+                        .read<ConvertOptionsProvider>()
+                        .changeImageOptions(containAnnotations: value);
+                  })
+            ],
+          ),
+          Text('ImageDpi:'),
+          TextFormField(
+            keyboardType: TextInputType.number,
+            textInputAction: TextInputAction.done,
+            initialValue: "300",
+            maxLength: 1000,
+            inputFormatters:
+            [
+              FilteringTextInputFormatter.allow(RegExp(r'^\d{1,4}(\.\d{0,2})?')),
+              FilteringTextInputFormatter.deny(RegExp(r'[\\,|\\.]')),
+              LengthLimitingTextInputFormatter(4)
+            ],
+            decoration: InputDecoration(labelText: '请输入范围在1~1000内的整数'),
+            onChanged: (value){
+              context
+                  .read<ConvertOptionsProvider>()
+                  .changeImageOptions(imageDpi: int.parse(value));
+            },
+          ),
+          imagePageOptions((value) {
+            context
+                .read<ConvertOptionsProvider>()
+                .changeImageOptions(imagePageOptions: value);
+          })
+        ],
+      ),
+    );
+  }
+
+
+
+  Widget imagePageOptions(ValueChanged<ImagePageOptions> onChanged) {
+    ImagePageOptions imagePageOptions =
+    context.select<ConvertOptionsProvider, ImagePageOptions>(
+            (value) => value.imageOptions.imagePageOptions);
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Text('Page Options:'),
+        _imagePageOptionsItem(
+            imagePageOptions, ImagePageOptions.JPEG, onChanged),
+        _imagePageOptionsItem(
+            imagePageOptions, ImagePageOptions.PNG, onChanged),
+      ],
+    );
+  }
+
+  Widget _imagePageOptionsItem(ImagePageOptions currentOptions,
+      ImagePageOptions options, ValueChanged<ImagePageOptions> onChanged) {
+    return CheckboxListTile(
+        controlAffinity: ListTileControlAffinity.leading,
+        title: Text(options.name),
+        value: currentOptions == options,
+        onChanged: (value) {
+          onChanged(options);
+        });
+  }
+}

+ 55 - 0
lib/widgets/options/convert_ppt_options_widget.dart

@@ -0,0 +1,55 @@
+import 'package:ConversionFlutterDemo/models/convert_contain_options_bean.dart';
+import 'package:ConversionFlutterDemo/states/convert_options_provider.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+class ConvertPPTOptionsWidget extends StatefulWidget {
+  const ConvertPPTOptionsWidget({Key? key}) : super(key: key);
+
+  @override
+  State<ConvertPPTOptionsWidget> createState() =>
+      _ConvertPPTOptionsWidgetState();
+}
+
+class _ConvertPPTOptionsWidgetState extends State<ConvertPPTOptionsWidget> {
+  @override
+  Widget build(BuildContext context) {
+    bool containImages = context.select<ConvertOptionsProvider, bool>(
+        (value) => value.pptOptions.containImages);
+    bool containAnnotations = context.select<ConvertOptionsProvider, bool>(
+        (value) => value.pptOptions.containAnnotations);
+    return Padding(
+        padding: EdgeInsets.all(8),
+        child: Column(
+          mainAxisSize: MainAxisSize.min,
+          children: [
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                const Text('ContainImages'),
+                Switch(
+                    value: containImages,
+                    onChanged: (value) {
+                      context
+                          .read<ConvertOptionsProvider>()
+                          .changePPTOptions(containImages: value);
+                    })
+              ],
+            ),
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                const Text('ContainAnnotations'),
+                Switch(
+                    value: containAnnotations,
+                    onChanged: (value) {
+                      context
+                          .read<ConvertOptionsProvider>()
+                          .changePPTOptions(containAnnotations: value);
+                    })
+              ],
+            )
+          ],
+        ));
+  }
+}

+ 55 - 0
lib/widgets/options/convert_ref_options_widget.dart

@@ -0,0 +1,55 @@
+import 'package:ConversionFlutterDemo/models/convert_contain_options_bean.dart';
+import 'package:ConversionFlutterDemo/states/convert_options_provider.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+class ConvertRTFOptionsWidget extends StatefulWidget {
+  const ConvertRTFOptionsWidget({Key? key}) : super(key: key);
+
+  @override
+  State<ConvertRTFOptionsWidget> createState() =>
+      _ConvertRTFOptionsWidgetState();
+}
+
+class _ConvertRTFOptionsWidgetState extends State<ConvertRTFOptionsWidget> {
+  @override
+  Widget build(BuildContext context) {
+    bool containImages = context.select<ConvertOptionsProvider, bool>(
+        (value) => value.rtfOptions.containImages);
+    bool containAnnotations = context.select<ConvertOptionsProvider, bool>(
+        (value) => value.rtfOptions.containAnnotations);
+    return Padding(
+        padding: EdgeInsets.all(8),
+        child: Column(
+          mainAxisSize: MainAxisSize.min,
+          children: [
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                const Text('ContainImages'),
+                Switch(
+                    value: containImages,
+                    onChanged: (value) {
+                      context
+                          .read<ConvertOptionsProvider>()
+                          .changeRTFOptions(containImages: value);
+                    })
+              ],
+            ),
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                const Text('ContainAnnotations'),
+                Switch(
+                    value: containAnnotations,
+                    onChanged: (value) {
+                      context
+                          .read<ConvertOptionsProvider>()
+                          .changeRTFOptions(containAnnotations: value);
+                    })
+              ],
+            )
+          ],
+        ));
+  }
+}

+ 45 - 0
lib/widgets/options/convert_word_options_widget.dart

@@ -0,0 +1,45 @@
+import 'package:ConversionFlutterDemo/models/convert_contain_options_bean.dart';
+import 'package:ConversionFlutterDemo/states/convert_options_provider.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+class ConvertWordOptionsWidget extends StatefulWidget {
+  const ConvertWordOptionsWidget({Key? key}) : super(key: key);
+
+  @override
+  State<ConvertWordOptionsWidget> createState() =>
+      _ConvertWordOptionsWidgetState();
+}
+
+class _ConvertWordOptionsWidgetState extends State<ConvertWordOptionsWidget> {
+  @override
+  Widget build(BuildContext context) {
+    bool containImages = context.select<ConvertOptionsProvider, bool>((value) => value.wordOptions.containImages);
+    bool containAnnotations = context.select<ConvertOptionsProvider, bool>((value) => value.wordOptions.containAnnotations);
+    return Padding(
+        padding: EdgeInsets.all(8),
+        child: Column(
+          mainAxisSize: MainAxisSize.min,
+          children: [
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                const Text('ContainImages'),
+                Switch(value: containImages, onChanged: (value) {
+                  context.read<ConvertOptionsProvider>().changeWordOptions(containImages: value);
+                })
+              ],
+            ),
+            Row(
+              mainAxisAlignment: MainAxisAlignment.spaceBetween,
+              children: [
+                const Text('ContainAnnotations'),
+                Switch(value: containAnnotations, onChanged: (value) {
+                  context.read<ConvertOptionsProvider>().changeWordOptions(containAnnotations: value);
+                })
+              ],
+            )
+          ],
+        ));
+  }
+}

+ 1 - 0
macos/Flutter/Flutter-Debug.xcconfig

@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
 #include "ephemeral/Flutter-Generated.xcconfig"

+ 1 - 0
macos/Flutter/Flutter-Release.xcconfig

@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
 #include "ephemeral/Flutter-Generated.xcconfig"

+ 41 - 0
pubspec.lock

@@ -57,11 +57,27 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.3.1"
+  ffi:
+    dependency: transitive
+    description:
+      name: ffi
+      sha256: "13a6ccf6a459a125b3fcdb6ec73bd5ff90822e071207c663bfd1f70062d51d18"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.2.1"
   flutter:
     dependency: "direct main"
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_document_picker:
+    dependency: "direct main"
+    description:
+      name: flutter_document_picker
+      sha256: "05fd2cdbcfd679fece8bfd99103e1effd5d3756ef742f263b9bd682288888fce"
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.1.0"
   flutter_lints:
     dependency: "direct dev"
     description:
@@ -115,6 +131,22 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.8.0"
+  nested:
+    dependency: transitive
+    description:
+      name: nested
+      sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.0.0"
+  open_file:
+    dependency: "direct main"
+    description:
+      name: open_file
+      sha256: "5cd0288033d613ee7908aa974b7ff00805ae55d253f64d311603cf2e30613ca8"
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.2.1"
   path:
     dependency: transitive
     description:
@@ -123,6 +155,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.8.2"
+  provider:
+    dependency: "direct main"
+    description:
+      name: provider
+      sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f
+      url: "https://pub.dev"
+    source: hosted
+    version: "6.0.5"
   sky_engine:
     dependency: transitive
     description: flutter
@@ -186,3 +226,4 @@ packages:
     version: "2.1.4"
 sdks:
   dart: ">=2.19.1 <3.0.0"
+  flutter: ">=1.16.0"

+ 3 - 0
pubspec.yaml

@@ -15,6 +15,9 @@ dependencies:
   # The following adds the Cupertino Icons font to your application.
   # Use with the CupertinoIcons class for iOS style icons.
   cupertino_icons: ^1.0.2
+  flutter_document_picker: ^5.1.0
+  provider: ^6.0.5
+  open_file: ^3.2.1
 
 dev_dependencies:
   flutter_test: