Browse Source

PDFView Flutter - 高亮、下划线、删除线、波浪线注释功能接入

liuxiaolong 2 years ago
parent
commit
8e7615ab6b
30 changed files with 842 additions and 71 deletions
  1. 1 0
      .gitignore
  2. 5 0
      android/app/build.gradle
  3. 17 0
      android/app/src/main/kotlin/com/compdfkit/pdfviewer/flutter/helpers/AnnotType.kt
  4. 88 4
      android/app/src/main/kotlin/com/compdfkit/pdfviewer/flutter/helpers/CPDFReaderViewHelpers.kt
  5. 7 0
      android/app/src/main/kotlin/com/compdfkit/pdfviewer/flutter/helpers/Extensions.kt
  6. 36 3
      android/app/src/main/kotlin/com/compdfkit/pdfviewer/flutter/helpers/PluginUtils.kt
  7. 48 0
      android/app/src/main/kotlin/com/compdfkit/pdfviewer/flutter/helpers/SamplePDFContextMenuHelper.kt
  8. 3 1
      android/app/src/main/kotlin/com/compdfkit/pdfviewer/flutter/view/PDFReaderView.kt
  9. 61 0
      android/app/src/main/res/layout/layout_select_text_content_view.xml
  10. 13 0
      android/app/src/main/res/values/styles.xml
  11. 4 0
      assets/images/ic_reader_highlight.svg
  12. 4 0
      assets/images/ic_reader_highlight_select.svg
  13. 1 0
      assets/images/ic_reader_squiggly_underline.svg
  14. 6 0
      assets/images/ic_reader_squiggly_underline_select.svg
  15. 5 0
      assets/images/ic_reader_strikethrough.svg
  16. 5 0
      assets/images/ic_reader_strikethrough_select.svg
  17. 1 0
      assets/images/ic_reader_underline.svg
  18. 4 0
      assets/images/ic_reader_underline_select.svg
  19. 1 1
      lib/main.dart
  20. 21 2
      lib/widgets/config.dart
  21. 21 0
      lib/widgets/constains.dart
  22. 34 12
      lib/widgets/events.dart
  23. 124 0
      lib/widgets/function/annot_color_options_widget.dart
  24. 189 0
      lib/widgets/function/pdf_buttom_annot_fun_widget.dart
  25. 0 4
      lib/widgets/function/pdf_function_page.dart
  26. 9 0
      lib/widgets/models/annot_attribute_bean.dart
  27. 17 0
      lib/widgets/models/annot_bean.dart
  28. 49 43
      lib/widgets/page/pdf_page_widget.dart
  29. 65 1
      pubspec.lock
  30. 3 0
      pubspec.yaml

+ 1 - 0
.gitignore

@@ -42,3 +42,4 @@ app.*.map.json
 /android/app/debug
 /android/app/profile
 /android/app/release
+/local.properties

+ 5 - 0
android/app/build.gradle

@@ -58,6 +58,10 @@ android {
             signingConfig signingConfigs.debug
         }
     }
+    buildFeatures {
+        viewBinding = true
+        dataBinding = true
+    }
 }
 
 flutter {
@@ -71,4 +75,5 @@ dependencies {
     api 'com.google.android.material:material:1.8.0'
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
     implementation 'androidx.multidex:multidex:2.0.1'
+    implementation 'com.google.android.flexbox:flexbox:3.0.0'
 }

+ 17 - 0
android/app/src/main/kotlin/com/compdfkit/pdfviewer/flutter/helpers/AnnotType.kt

@@ -0,0 +1,17 @@
+package com.compdfkit.pdfviewer.flutter.helpers
+
+/**
+ * @classname:
+ * @author: LiuXiaoLong
+ * @date: 2023/3/13
+ * description:
+ */
+enum class AnnotType {
+
+    highlight,
+    strikeout,
+    underline,
+    squiggly
+
+
+}

+ 88 - 4
android/app/src/main/kotlin/com/compdfkit/pdfviewer/flutter/helpers/CPDFReaderViewHelpers.kt

@@ -13,15 +13,15 @@ import com.compdfkit.ui.reader.CPDFReaderView
 object CPDFReaderViewHelpers {
 
 
-    fun openDocument(context: Context, documentPath : String?, success : (CPDFDocument) -> Unit, isEncrypt : ()-> Unit, errorResult : (CPDFDocument.PDFDocumentError)-> Unit){
+    fun openDocument(context: Context, documentPath: String?, success: (CPDFDocument) -> Unit, isEncrypt: () -> Unit, errorResult: (CPDFDocument.PDFDocumentError) -> Unit) {
 
         var document = CPDFDocument(context)
         val error = document.open(documentPath)
-        when(error){
-            CPDFDocument.PDFDocumentError.PDFDocumentErrorSuccess->{
+        when (error) {
+            CPDFDocument.PDFDocumentError.PDFDocumentErrorSuccess -> {
                 success.invoke(document)
             }
-            CPDFDocument.PDFDocumentError.PDFDocumentErrorPassword->{
+            CPDFDocument.PDFDocumentError.PDFDocumentErrorPassword -> {
                 isEncrypt.invoke()
             }
             else -> {
@@ -31,4 +31,88 @@ object CPDFReaderViewHelpers {
 
     }
 
+
+
+
+    fun setAnnotAttribute(cpdfReaderView: CPDFReaderView, map: Map<String, Map<String, Any>>) {
+        for (entry in map) {
+            val color = entry.value.get("color") as? Long
+            val alpha = entry.value.getInt("alpha")
+            when (AnnotType.valueOf(entry.key)) {
+                AnnotType.squiggly -> {
+                    cpdfReaderView.readerAttribute.annotAttribute.squigglyAttr.also {
+                        if (color != null) {
+                            it.color = color.toInt()
+                        }
+                        if (alpha != null) {
+                            it.alpha = alpha
+                        }
+                        it.onstore()
+                    }
+                }
+                AnnotType.highlight -> {
+                    cpdfReaderView.readerAttribute.annotAttribute.highlightAttr.also {
+                        if (color != null) {
+                            it.color = color.toInt()
+                        }
+                        if (alpha != null) {
+                            it.alpha = alpha
+                        }
+                        it.onstore()
+                    }
+                }
+                AnnotType.strikeout -> {
+                    cpdfReaderView.readerAttribute.annotAttribute.strikeoutAttr.also {
+                        if (color != null) {
+                            it.color = color.toInt()
+                        }
+                        if (alpha != null) {
+                            it.alpha = alpha
+                        }
+                        it.onstore()
+                    }
+                }
+                AnnotType.underline -> {
+                    cpdfReaderView.readerAttribute.annotAttribute.underlineAttr.also {
+                        if (color != null) {
+                            it.color = color.toInt()
+                        }
+                        if (alpha != null) {
+                            it.alpha = alpha
+                        }
+                        it.onstore()
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * get CPDFReaderView annotAttribute color and alpha value
+     */
+    fun getReaderAnnotAttribute(cpdfReaderView: CPDFReaderView, annotType: AnnotType): Pair<Int, Int> {
+        val annotAttribute = cpdfReaderView.readerAttribute.annotAttribute
+
+        return when (annotType) {
+            AnnotType.highlight -> {
+                val highlightAttr = annotAttribute.highlightAttr
+                highlightAttr.color to highlightAttr.alpha
+            }
+            AnnotType.strikeout -> {
+                val strikeoutAttr = annotAttribute.strikeoutAttr
+                strikeoutAttr.color to strikeoutAttr.alpha
+            }
+            AnnotType.underline -> {
+                val underlineAttr = annotAttribute.underlineAttr
+                underlineAttr.color to underlineAttr.alpha
+            }
+            AnnotType.squiggly -> {
+                val squigglyAttr = annotAttribute.squigglyAttr
+                squigglyAttr.color to squigglyAttr.alpha
+            }
+        }
+
+    }
+
 }

+ 7 - 0
android/app/src/main/kotlin/com/compdfkit/pdfviewer/flutter/helpers/Extensions.kt

@@ -16,3 +16,10 @@ fun Map<String, Any>?.getString(key: String): String? {
     return this?.get(key) as? String
 }
 
+fun Map<String, Any>?.getMap(key : String) :Map<String, Map<String, Any>>?{
+    return this?.get(key) as? Map<String, Map<String, Any>>
+}
+
+fun Map<String, Any>?.getInt(key : String) : Int?{
+    return this?.get(key) as? Int
+}

+ 36 - 3
android/app/src/main/kotlin/com/compdfkit/pdfviewer/flutter/helpers/PluginUtils.kt

@@ -1,5 +1,6 @@
 package com.compdfkit.pdfviewer.flutter.helpers
 
+import android.util.Log
 import com.compdfkit.pdfviewer.flutter.view.PDFReaderView
 import io.flutter.plugin.common.MethodCall
 import io.flutter.plugin.common.MethodChannel
@@ -24,19 +25,21 @@ object PluginUtils {
     const val EVENT_ON_SCROLL_END = "onScrollEnd"
     const val EVENT_ON_SCROLLING = "onScrolling"
     const val EVENT_ON_RECORD_LAST_JUMP_PAGE_NUM = "onRecordLastJumpPageNum"
-
+    const val EVENT_ANNOT_ATTR_COLOR = "annotAttrColor"
+    const val EVENT_ANNOT_ATTR_ALPHA = "annotAttrAlpha"
 
     const val KEY_SCROLL_DIRECTION = "scrollDirection"
     const val KEY_IS_DOUBLE_PAGE = "isDoublePage"
     const val KEY_IS_CONTINUE_MODE = "isContinueMode"
     const val KEY_IS_COVER_PAGE_MODE = "isCoverPageMode"
     const val KEY_IS_CROP_PAGE_MODE = "isCropPageMode"
+    const val KEY_ANNOT_TYPE = "annotType"
+    const val KEY_ANNOT_ATTRIBUTE = "annotAttribute"
 
     const val SCROLL_DIRECTION_VERTICAL = "vertical"
     const val SCROLL_DIRECTION_HORIZONTAL = "horizontal"
 
-    const val PAGE_MODE_IS_DOUBLE_PAGE = "isDoublePage"
-
+    ///MethodChannel invokeMethod method name
     const val FUNCTION_SET_SCROLL_DIRECTION = "setScrollDirection"
     const val FUNCTION_GET_SCROLL_DIRECTION = "getScrollDirection"
     const val FUNCTION_SET_PAGE_MODE = "setPageMode"
@@ -47,6 +50,9 @@ object PluginUtils {
     const val FUNCTION_GET_IS_COVER_PAGE_MODE = "isCoverPageMode"
     const val FUNCTION_GET_IS_CROP_PAGE_MODE = "isCropPageMode"
     const val FUNCTION_SET_IS_CROP_PAGE_MODE = "setIsCropPageMode"
+    const val FUNCTION_GET_ANNOT_ATTRIBUTE = "getAnnotAttribute"
+    const val FUNCTION_SET_ANNOT_ATTRIBUTE = "setAnnotAttribute"
+
 
 
     fun initPDFReaderViewConfig(pdfReaderView: PDFReaderView, configurationMap: HashMap<String, Any>? = null) {
@@ -66,6 +72,10 @@ object PluginUtils {
         configurationMap.getBoolean(KEY_IS_CROP_PAGE_MODE)?.let {
             pdfReaderView.pdfReaderView.isCropMode = it
         }
+        configurationMap.getMap(KEY_ANNOT_ATTRIBUTE)?.let {
+            CPDFReaderViewHelpers.setAnnotAttribute(pdfReaderView.pdfReaderView, it)
+        }
+
 
     }
 
@@ -118,6 +128,29 @@ object PluginUtils {
                     result.success(pdfReaderView.pdfReaderView.isCropMode)
                 }
             }
+            FUNCTION_GET_ANNOT_ATTRIBUTE ->{
+                (configuration?.get(KEY_ANNOT_TYPE) as? String)?.let {
+                    val annotAttribute = CPDFReaderViewHelpers.getReaderAnnotAttribute(pdfReaderView.pdfReaderView, AnnotType.valueOf(it))
+                    Log.e("Flutter", "method get Attr, annotType:${it}, color:${annotAttribute.first}, alpha:${annotAttribute.second}")
+                    result.success(mapOf(
+                        EVENT_ANNOT_ATTR_COLOR to annotAttribute.first,
+                        EVENT_ANNOT_ATTR_ALPHA to annotAttribute.second
+                    ))
+                }
+            }
+            FUNCTION_SET_ANNOT_ATTRIBUTE ->{
+                configuration.getString(KEY_ANNOT_TYPE)?.let {
+                    val annotAttr = configuration?.get(KEY_ANNOT_ATTRIBUTE) as? Map<String, Any> ?: hashMapOf()
+                    CPDFReaderViewHelpers.setAnnotAttribute(pdfReaderView.pdfReaderView, mapOf(it to annotAttr))
+                    //return current annot type color and colorAlpha
+                    val annotAttribute = CPDFReaderViewHelpers.getReaderAnnotAttribute(pdfReaderView.pdfReaderView, AnnotType.valueOf(it))
+                    result.success(mapOf(
+                        EVENT_ANNOT_ATTR_COLOR to annotAttribute.first,
+                        EVENT_ANNOT_ATTR_ALPHA to annotAttribute.second
+                    ))
+                }
+
+            }
             else -> {}
         }
     }

+ 48 - 0
android/app/src/main/kotlin/com/compdfkit/pdfviewer/flutter/helpers/SamplePDFContextMenuHelper.kt

@@ -0,0 +1,48 @@
+package com.compdfkit.pdfviewer.flutter.helpers
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.View
+import com.compdfkit.pdfviewer.flutter.databinding.LayoutSelectTextContentViewBinding
+import com.compdfkit.ui.contextmenu.CPDFContextMenuShowHelper
+import com.compdfkit.ui.reader.CPDFPageView
+import com.compdfkit.ui.reader.CPDFReaderView
+
+/**
+ * @classname:
+ * @author: LiuXiaoLong
+ * @date: 2023/2/16
+ * description:
+ */
+class SamplePDFContextMenuHelper(readerView: CPDFReaderView) : CPDFContextMenuShowHelper(readerView) {
+
+    @SuppressLint("MissingInflatedId")
+    override fun getSelectTextContentView(p0: CPDFPageView, p1: LayoutInflater): View {
+        val binding = LayoutSelectTextContentViewBinding.inflate(p1)
+        binding.tvCopy.setOnClickListener {
+            p0.operateSelections(CPDFPageView.SelectFuncType.COPY)
+        }
+        binding.tvHighlight.setOnClickListener {
+            p0.operateSelections(CPDFPageView.SelectFuncType.HIGHLIGHT)
+        }
+        binding.tvUnderline.setOnClickListener {
+            p0.operateSelections(CPDFPageView.SelectFuncType.UNDERLINE)
+        }
+        binding.tvStrikeout.setOnClickListener {
+            p0.operateSelections(CPDFPageView.SelectFuncType.STRIKEOUT)
+        }
+        binding.tvSquiggly.setOnClickListener {
+            p0.operateSelections(CPDFPageView.SelectFuncType.SQUIGGLY)
+        }
+        binding.tvEdit.setOnClickListener {
+            p0.operateSelections(CPDFPageView.SelectFuncType.EDIT)
+        }
+        binding.tvRedaction.setOnClickListener {
+            p0.operateSelections(CPDFPageView.SelectFuncType.REDACTION)
+        }
+
+        return binding.root
+    }
+
+
+}

+ 3 - 1
android/app/src/main/kotlin/com/compdfkit/pdfviewer/flutter/view/PDFReaderView.kt

@@ -10,6 +10,7 @@ import com.compdfkit.pdfviewer.flutter.helpers.PluginUtils.EVENT_ON_RECORD_LAST_
 import com.compdfkit.pdfviewer.flutter.helpers.PluginUtils.EVENT_ON_SCROLLING
 import com.compdfkit.pdfviewer.flutter.helpers.PluginUtils.EVENT_ON_SCROLL_END
 import com.compdfkit.pdfviewer.flutter.helpers.PluginUtils.EVENT_ON_TAP_MAIN_DOC_AREA
+import com.compdfkit.pdfviewer.flutter.helpers.SamplePDFContextMenuHelper
 import com.compdfkit.ui.reader.IReaderViewCallback
 import io.flutter.plugin.common.EventChannel
 
@@ -26,8 +27,9 @@ class PDFReaderView(context: Context?) : RelativeLayout(context), IReaderViewCal
 
     init {
         pdfReaderView.setReaderViewCallback(this)
+        pdfReaderView.contextMenuShowListener = SamplePDFContextMenuHelper(pdfReaderView)
         addView(pdfReaderView)
-
+        pdfReaderView.readerAttribute.annotAttribute.strikeoutAttr
     }
 
 

+ 61 - 0
android/app/src/main/res/layout/layout_select_text_content_view.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+    <GridLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:columnCount="4"
+        android:rowCount="2"
+        >
+
+        <androidx.appcompat.widget.AppCompatButton
+            android:id="@+id/tv_edit"
+            style="@style/SelectTextContentViewMenuButtonStyle"
+            android:text="Edit"
+            />
+
+        <androidx.appcompat.widget.AppCompatButton
+            android:id="@+id/tv_highlight"
+            style="@style/SelectTextContentViewMenuButtonStyle"
+            android:text="Highlight"
+            />
+
+        <androidx.appcompat.widget.AppCompatButton
+            android:id="@+id/tv_underline"
+            style="@style/SelectTextContentViewMenuButtonStyle"
+            android:text="Underline"
+            />
+
+        <androidx.appcompat.widget.AppCompatButton
+            android:id="@+id/tv_strikeout"
+            style="@style/SelectTextContentViewMenuButtonStyle"
+            android:text="strikeout"
+            />
+
+        <androidx.appcompat.widget.AppCompatButton
+            android:id="@+id/tv_squiggly"
+            style="@style/SelectTextContentViewMenuButtonStyle"
+            android:text="Squiggly"
+            />
+        <androidx.appcompat.widget.AppCompatButton
+            android:id="@+id/tv_redaction"
+            style="@style/SelectTextContentViewMenuButtonStyle"
+            android:text="Redaction"
+            />
+
+        <androidx.appcompat.widget.AppCompatButton
+            android:id="@+id/tv_copy"
+            style="@style/SelectTextContentViewMenuButtonStyle"
+            android:text="Copy"
+            />
+
+
+
+
+    </GridLayout>
+
+
+</androidx.cardview.widget.CardView>

+ 13 - 0
android/app/src/main/res/values/styles.xml

@@ -15,4 +15,17 @@
     <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
         <item name="android:windowBackground">?android:colorBackground</item>
     </style>
+
+    <style name="SelectTextContentViewMenuButtonStyle">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">match_parent</item>
+        <item name="android:gravity">center_horizontal</item>
+        <item name="android:paddingStart">12dp</item>
+        <item name="android:paddingEnd">12dp</item>
+        <item name="android:paddingTop">4dp</item>
+        <item name="android:paddingBottom">4dp</item>
+        <item name="android:layout_gravity">fill</item>
+        <item name="android:textSize">14sp</item>
+    </style>
+
 </resources>

+ 4 - 0
assets/images/ic_reader_highlight.svg

@@ -0,0 +1,4 @@
+<svg id="vector" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="#EFF4FD" d="M24,0H0V24H24V0ZM22,4H4L2,20H20L22,4Z" fill-rule="evenodd" id="path_0" />
+    <path fill="#273C62" d="M17.6078,17.5L12.6964,5.2214H11.3036L6.3922,17.5H5V18.5H9V17.5H8.0078L9.1077,14.75H14.8923L15.9922,17.5H15V18.5H19V17.5H17.6078ZM14.4923,13.75L12,7.519L9.5077,13.75H14.4923Z" fill-rule="evenodd" id="path_1" />
+</svg>

+ 4 - 0
assets/images/ic_reader_highlight_select.svg

@@ -0,0 +1,4 @@
+<svg id="vector" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="#D5E3FE" d="M24,0H0V24H24V0ZM22,4H4L2,20H20L22,4Z" fill-rule="evenodd" id="path_0" />
+    <path fill="#273C62" d="M17.6078,17.5L12.6964,5.2214H11.3036L6.3922,17.5H5V18.5H9V17.5H8.0078L9.1077,14.75H14.8923L15.9922,17.5H15V18.5H19V17.5H17.6078ZM14.4923,13.75L12,7.519L9.5077,13.75H14.4923Z" fill-rule="evenodd" id="path_1" />
+</svg>

File diff suppressed because it is too large
+ 1 - 0
assets/images/ic_reader_squiggly_underline.svg


File diff suppressed because it is too large
+ 6 - 0
assets/images/ic_reader_squiggly_underline_select.svg


+ 5 - 0
assets/images/ic_reader_strikethrough.svg

@@ -0,0 +1,5 @@
+<svg id="vector" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="#EFF4FD" d="M24,0H0V24H24V0ZM22,12.5H2V14H22V12.5Z" fill-rule="evenodd" id="path_0" />
+    <path fill="#273C62" d="M15.2143,17.3928H16.3494L14.9923,14H16.6078L17.9649,17.3928H19.5V18.3928H15.2143V17.3928ZM16.0078,12.5H14.3923L12,6.519L9.6077,12.5H7.9922L11.3036,4.2214H12.6964L16.0078,12.5ZM7.3922,14L6.0351,17.3928H4.5V18.3928H8.7857V17.3928H7.6506L9.0077,14H7.3922Z"
+        fill-rule="evenodd" id="path_1" />
+</svg>

+ 5 - 0
assets/images/ic_reader_strikethrough_select.svg

@@ -0,0 +1,5 @@
+<svg id="vector" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="#D5E3FE" d="M24,0H0V24H24V0ZM22,12.5H2V14H22V12.5Z" fill-rule="evenodd" id="path_0" />
+    <path fill="#273C62" d="M15.2143,17.3928H16.3494L14.9923,14H16.6078L17.9649,17.3928H19.5V18.3928H15.2143V17.3928ZM16.0078,12.5H14.3923L12,6.519L9.6077,12.5H7.9922L11.3036,4.2214H12.6964L16.0078,12.5ZM7.3922,14L6.0351,17.3928H4.5V18.3928H8.7857V17.3928H7.6506L9.0077,14H7.3922Z"
+        fill-rule="evenodd" id="path_1" />
+</svg>

+ 1 - 0
assets/images/ic_reader_underline.svg

@@ -0,0 +1 @@
+<svg id="vector" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#EFF4FD" d="M24,0H0V24H24V0ZM22,19H2V20.5H22V19Z" fill-rule="evenodd" id="path_0"/><path fill="#273C62" d="M15,16H15.9922L14.8923,13.25H9.1077L8.0078,16H9V17H5V16H6.3922L11.3036,3.7214H12.6964L17.6078,16H19V17H15V16ZM9.5077,12.25H14.4923L12,6.019L9.5077,12.25Z" fill-rule="evenodd" id="path_1"/></svg>

+ 4 - 0
assets/images/ic_reader_underline_select.svg

@@ -0,0 +1,4 @@
+<svg id="vector" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <path fill="#D5E3FE" d="M24,0H0V24H24V0ZM22,19H2V20.5H22V19Z" fill-rule="evenodd" id="path_0" />
+    <path fill="#273C62" d="M15,16H15.9922L14.8923,13.25H9.1077L8.0078,16H9V17H5V16H6.3922L11.3036,3.7214H12.6964L17.6078,16H19V17H15V16ZM9.5077,12.25H14.4923L12,6.019L9.5077,12.25Z" fill-rule="evenodd" id="path_1" />
+</svg>

+ 1 - 1
lib/main.dart

@@ -51,7 +51,7 @@ class _MyAppState extends State<MyApp> {
                           showDocument(context);
                         },
                         child: const Text('Simple PDF Viewer'),
-                      )))
+                      ))),
             ],
           ),
         ));

+ 21 - 2
lib/widgets/config.dart

@@ -1,14 +1,33 @@
 
 
 
+import 'package:flutter/material.dart';
 import 'package:kmpdfkit_demo/widgets/constains.dart';
 
 
 ///default configuration options
-Map<String, dynamic> applyNormalConfiguration() => const {
+Map<String, dynamic> applyNormalConfiguration() => {
   'scrollDirection' : ScrollDirection.vertical,
   'isDoublePage' : false,
   'isContinueMode' : true,
   'isCoverPageMode' : false,
-  'isCropPageMode' : false
+  'isCropPageMode' : false,
+  'annotAttribute' : {
+    'highlight' : {
+      'color' : Colors.red.value,
+      'alpha' : 255
+    },
+    'strikeout' : {
+      'color' : Colors.green.value,
+      'alpha' : 255
+    },
+    'underline' : {
+      'color' : Colors.blue.value,
+      'alpha' : 255
+    },
+    'squiggly' : {
+      'color' : Colors.purple.value,
+      'alpha' : 255
+    }
+  }
 };

+ 21 - 0
lib/widgets/constains.dart

@@ -19,6 +19,8 @@ class Functions {
   static const setCoverPageMode = 'setCoverPageMode';
   static const isCropPageMode = 'isCropPageMode';
   static const setIsCropPageMode = 'setIsCropPageMode';
+  static const getAnnotAttribute = 'getAnnotAttribute';
+  static const setAnnotAttribute = 'setAnnotAttribute';
 
 }
 
@@ -36,6 +38,11 @@ class EventParameters {
   static const isContinueMode = 'isContinueMode';
   static const isCoverPageMode = 'isCoverPageMode';
   static const isCropPageMode = 'isCropPageMode';
+  static const annotType = 'annotType';
+  static const annotAttribute = 'annotAttribute';
+  static const annotAttrColor = 'annotAttrColor';
+  static const annotAttrAlpha = 'annotAttrAlpha';
+
 }
 
 /// CPDFReaderView scroll direction
@@ -43,3 +50,17 @@ class ScrollDirection {
   static const vertical = 'vertical';
   static const horizontal = 'horizontal';
 }
+
+
+enum AnnotType {
+
+  highlight,
+  strikeout,
+  underline,
+  squiggly,
+
+}
+
+
+
+

+ 34 - 12
lib/widgets/events.dart

@@ -1,8 +1,11 @@
 import 'dart:ffi';
 
+import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:kmpdfkit_demo/widgets/constains.dart';
 
+import 'models/annot_attribute_bean.dart';
+
 const _methodChannel = MethodChannel(ChannelNames.EVENT_CPDF_READER_VIEW);
 
 //Listen to PDFReaderView events.
@@ -143,17 +146,16 @@ Future<bool> isContinueMode() async {
   return await _methodChannel.invokeMethod(Functions.getPageContinue);
 }
 
-
 ///Set page scrolling mode
 ///true: continue Mode
 ///```dart
 ///     bool isContinueMode = await setIsContinueMode(isContinueMode);
 /// ```
 Future<bool> setIsContinueMode(bool isContinueMode) async {
-  return await _methodChannel.invokeMethod(Functions.setPageContinue, {EventParameters.isContinueMode : isContinueMode});
+  return await _methodChannel.invokeMethod(Functions.setPageContinue,
+      {EventParameters.isContinueMode: isContinueMode});
 }
 
-
 ///Get whether 'ComPDFReaderView' is in cover display mode
 ///```dart
 ///     bool isCoverPageMode = await isCoverPageMode();
@@ -162,17 +164,16 @@ Future<bool> isCoverPageMode() async {
   return await _methodChannel.invokeMethod(Functions.isCoverPageMode);
 }
 
-
 ///set is cover display mode
 ///true: is cover page mode
 ///```dart
 ///     bool isCoverPageMode = await setIsCoverPageMode(isCoverPageMode);
 /// ```
 Future<bool> setIsCoverPageMode(bool isCoverPageMode) async {
-  return await _methodChannel.invokeMethod(Functions.setCoverPageMode, {EventParameters.isCoverPageMode : isCoverPageMode});
+  return await _methodChannel.invokeMethod(Functions.setCoverPageMode,
+      {EventParameters.isCoverPageMode: isCoverPageMode});
 }
 
-
 ///Get 'ComPDFReaderView' whether to crop the display mode
 ///```dart
 ///     bool isCropMode = await isCropPageMode();
@@ -181,22 +182,43 @@ Future<bool> isCropPageMode() async {
   return await _methodChannel.invokeMethod(Functions.isCropPageMode);
 }
 
-
 ///set is crop display mode
 ///true: is crop page mode
 ///```dart
 ///     bool isCropMode = await setIsCropPageMode(isCropPageMode);
 /// ```
 Future<bool> setIsCropPageMode(bool isCropPageMode) async {
-  return await _methodChannel.invokeMethod(Functions.setIsCropPageMode, {EventParameters.isCropPageMode : isCropPageMode});
+  return await _methodChannel.invokeMethod(Functions.setIsCropPageMode,
+      {EventParameters.isCropPageMode: isCropPageMode});
 }
 
 
+///Get annotation attributes based on annotation type
+///```dart
+///     AnnotAttributeBean highlight = await getAnnotAttribute(AnnotType.highlight);
+/// ```
+/// return annotation attributes color and alpha
+Future<AnnotAttributeBean> getAnnotAttribute(AnnotType annotType) async {
+  Map<dynamic, dynamic> annotAttrMap = await _methodChannel.invokeMethod(Functions.getAnnotAttribute,{
+    EventParameters.annotType : annotType.name
+  });
+  int color = annotAttrMap[EventParameters.annotAttrColor]?? Colors.red.value;
+  int alpha = annotAttrMap[EventParameters.annotAttrAlpha] ?? 255;
+  return AnnotAttributeBean(color, alpha);
+}
 
 
+Future<AnnotAttributeBean> setAnnotAttribute({required AnnotType annotType, int? color, int? alpha}) async {
+  Map<dynamic, dynamic> annotAttrMap = await _methodChannel.invokeMethod(Functions.setAnnotAttribute,{
+    EventParameters.annotType : annotType.name,
+    EventParameters.annotAttribute : {
+      'color' : color,
+      'alpha' : alpha
+    }
+  });
 
-
-
-
-
+  int attrColor = annotAttrMap[EventParameters.annotAttrColor]?? Colors.red.value;
+  int attrAlpha = annotAttrMap[EventParameters.annotAttrAlpha] ?? 255;
+  return AnnotAttributeBean(attrColor, attrAlpha);
+}
 

+ 124 - 0
lib/widgets/function/annot_color_options_widget.dart

@@ -0,0 +1,124 @@
+import 'package:flutter/material.dart';
+
+var annotColorList = [
+  Colors.red,
+  Colors.purple,
+  Colors.pink,
+  Colors.blue,
+  Colors.green,
+  Colors.yellow,
+  Colors.orange,
+  Colors.redAccent,
+  Colors.deepPurple,
+  Colors.blueGrey,
+  Colors.white,
+  Colors.black38
+];
+
+typedef AnnotColorOptionsCallback = void Function(int? color, int? alpha);
+
+class AnnotColorOptionsWidget extends StatefulWidget {
+  int color;
+  int alpha = 255;
+  final AnnotColorOptionsCallback colorOptionsCallback;
+
+  AnnotColorOptionsWidget(
+      {Key? key,
+      required this.color,
+      required this.alpha,
+      required this.colorOptionsCallback})
+      : super(key: key);
+
+  @override
+  State<AnnotColorOptionsWidget> createState() =>
+      _AnnotColorOptionsWidgetState();
+}
+
+class _AnnotColorOptionsWidgetState extends State<AnnotColorOptionsWidget> {
+  late double colorAlpha = 0.0;
+  late int selectColor;
+
+  @override
+  void initState() {
+    super.initState();
+    colorAlpha = widget.alpha.toDouble();
+    selectColor = widget.color;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Column(
+      mainAxisSize: MainAxisSize.min,
+      children: [
+        SizedBox(
+            height: 80,
+            child: ListView.builder(
+                scrollDirection: Axis.horizontal,
+                itemCount: annotColorList.length,
+                primary: true,
+                shrinkWrap: true,
+                itemBuilder: (BuildContext context, int index) {
+                  return Padding(
+                      padding: const EdgeInsets.all(8),
+                      child: Column(
+                        crossAxisAlignment: CrossAxisAlignment.center,
+                        children: [
+                          if (Color(selectColor).value ==
+                              annotColorList[index].value) ...[
+                            const Icon(
+                              Icons.arrow_drop_down_sharp,
+                              size: 24,
+                            )
+                          ] else ...[
+                            const SizedBox(
+                              width: 24,
+                              height: 24,
+                            )
+                          ],
+                          InkWell(
+                              onTap: () {
+                                widget.colorOptionsCallback(annotColorList[index].value, null);
+                                setState(() {
+                                  selectColor = annotColorList[index].value;
+                                });
+                              },
+                              child: Container(
+                                width: 30,
+                                height: 30,
+                                decoration: BoxDecoration(
+                                    color: annotColorList[index],
+                                    shape: BoxShape.circle),
+                              ))
+                        ],
+                      ));
+                })),
+        Padding(
+            padding: EdgeInsets.symmetric(horizontal: 8),
+            child: Row(
+              crossAxisAlignment: CrossAxisAlignment.center,
+              children: [
+                const Icon(Icons.invert_colors),
+                Expanded(
+                    child: Slider(
+                  min: 0.0,
+                  max: 255,
+                  value: colorAlpha,
+                  onChanged: (data) {
+                    setState(() {
+                      colorAlpha = data;
+                    });
+                  },
+                  onChangeEnd: (value) {
+                    widget.colorOptionsCallback(colorAlpha.round(), null);
+                  },
+                )),
+                Container(
+                  width: 50,
+                  child: Text('${((colorAlpha / 255.0) * 100).round()}%'),
+                )
+              ],
+            ))
+      ],
+    );
+  }
+}

+ 189 - 0
lib/widgets/function/pdf_buttom_annot_fun_widget.dart

@@ -0,0 +1,189 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:kmpdfkit_demo/widgets/constains.dart';
+import 'package:kmpdfkit_demo/widgets/events.dart';
+import 'package:kmpdfkit_demo/widgets/function/annot_color_options_widget.dart';
+import 'package:kmpdfkit_demo/widgets/models/annot_attribute_bean.dart';
+import 'package:kmpdfkit_demo/widgets/models/annot_bean.dart';
+
+class PDFButtonAnnotFunWidget extends StatefulWidget {
+  const PDFButtonAnnotFunWidget({Key? key}) : super(key: key);
+
+  @override
+  State<PDFButtonAnnotFunWidget> createState() =>
+      _PDFButtonAnnotFunWidgetState();
+}
+
+class _PDFButtonAnnotFunWidgetState extends State<PDFButtonAnnotFunWidget> {
+  AnnotType selectAnnot = AnnotType.highlight;
+
+  var annotFunList = [
+    AnnotBean(
+        annotType: AnnotType.highlight,
+        normalImagePath: 'assets/images/ic_reader_highlight.svg',
+        selectImagePath: 'assets/images/ic_reader_highlight_select.svg',
+        annotColor: Colors.red.value,
+        annotColorAlpha: 255),
+    AnnotBean(
+        annotType: AnnotType.underline,
+        normalImagePath: 'assets/images/ic_reader_underline.svg',
+        selectImagePath: 'assets/images/ic_reader_underline_select.svg',
+        annotColor: Colors.blue.value,
+        annotColorAlpha: 255),
+    AnnotBean(
+        annotType: AnnotType.strikeout,
+        normalImagePath: 'assets/images/ic_reader_strikethrough.svg',
+        selectImagePath: 'assets/images/ic_reader_strikethrough_select.svg',
+        annotColor: Colors.green.value,
+        annotColorAlpha: 255),
+    AnnotBean(
+        annotType: AnnotType.squiggly,
+        normalImagePath: 'assets/images/ic_reader_squiggly_underline.svg',
+        selectImagePath:
+            'assets/images/ic_reader_squiggly_underline_select.svg',
+        annotColor: Colors.pink.value,
+        annotColorAlpha: 255),
+  ];
+
+  @override
+  void initState() {
+    super.initState();
+    initAnnotAttr();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return annotList(selectAnnot, (annotBean) {
+      selectAnnotType(annotBean.annotType);
+    }, (annotBean) {
+      selectAnnotType(annotBean.annotType);
+      _showColorOptionsModalBottomSheet(context, annotBean);
+    });
+  }
+
+  void selectAnnotType(AnnotType type) {
+    setState(() {
+      selectAnnot = type;
+    });
+  }
+
+  Widget annotList(
+      AnnotType selectAnnotType,
+      Function(AnnotBean annotBean) onTap,
+      Function(AnnotBean annotBean) onLongPress) {
+    return Container(
+        color: const Color(0xFFEFF4FD),
+        height: 60,
+        alignment: Alignment.centerLeft,
+        child: ListView.builder(
+            itemCount: annotFunList.length,
+            scrollDirection: Axis.horizontal,
+            primary: true,
+            shrinkWrap: true,
+            itemBuilder: (context, index) {
+              AnnotBean bean = annotFunList[index];
+              return Container(
+                width: 60,
+                padding: const EdgeInsets.all(16),
+                height: 60,
+                color: selectAnnotType == bean.annotType
+                    ? const Color(0xFFD5E3FE)
+                    : Colors.transparent,
+                child: InkWell(
+                  onTap: () {
+                    onTap(bean);
+                  },
+                  onLongPress: () {
+                    onLongPress(bean);
+                  },
+                  child: Stack(
+                    children: [
+                      Positioned(
+                        top: 0,
+                        bottom: 0,
+                        left: 0,
+                        right: 0,
+                        child: Container(
+                          margin: const EdgeInsets.all(2),
+                          color: Color(bean.annotColor),
+                          width: double.infinity,
+                          height: double.infinity,
+                        ),
+                      ),
+                      Positioned(
+                          top: 0,
+                          bottom: 0,
+                          left: 0,
+                          right: 0,
+                          child: SvgPicture.asset(
+                            selectAnnotType == bean.annotType
+                                ? bean.selectImagePath
+                                : bean.normalImagePath,
+                            semanticsLabel: bean.annotType.name,
+                          ))
+                    ],
+                  ),
+                ),
+              );
+            }));
+  }
+
+  void _showColorOptionsModalBottomSheet(context, AnnotBean annotBean) {
+    showModalBottomSheet<int>(
+        context: context,
+        isScrollControlled: false,
+        builder: (BuildContext context) {
+          return AnnotColorOptionsWidget(
+            color: annotBean.annotColor,
+            alpha: annotBean.annotColorAlpha,
+            colorOptionsCallback: (int? color, int? alpha) async {
+              AnnotAttributeBean attr = await setAnnotAttribute(
+                  annotType: annotBean.annotType, color: color, alpha: alpha);
+              AnnotBean bean = annotFunList.firstWhere(
+                  (element) => element.annotType == annotBean.annotType);
+              setState(() {
+                if (color != null) {
+                  bean.annotColor = attr.color;
+                }
+                if (alpha != null) {
+                  bean.annotColorAlpha = attr.alpha;
+                }
+              });
+            },
+          );
+        });
+  }
+
+  void initAnnotAttr() async {
+    AnnotAttributeBean highlight = await getAnnotAttribute(AnnotType.highlight);
+    AnnotAttributeBean underline = await getAnnotAttribute(AnnotType.underline);
+    AnnotAttributeBean strikeout = await getAnnotAttribute(AnnotType.strikeout);
+    AnnotAttributeBean squiggly = await getAnnotAttribute(AnnotType.squiggly);
+    setState(() {
+      annotFunList.forEach((element) {
+        switch (element.annotType) {
+          case AnnotType.highlight:
+            element
+              ..annotColor = highlight.color
+              ..annotColorAlpha = highlight.alpha;
+            break;
+          case AnnotType.underline:
+            element
+              ..annotColor = underline.color
+              ..annotColorAlpha = underline.alpha;
+            break;
+          case AnnotType.strikeout:
+            element
+              ..annotColor = strikeout.color
+              ..annotColorAlpha = strikeout.alpha;
+            break;
+          case AnnotType.squiggly:
+            element
+              ..annotColor = squiggly.color
+              ..annotColorAlpha = squiggly.alpha;
+            break;
+        }
+      });
+    });
+  }
+}

+ 0 - 4
lib/widgets/function/pdf_function_page.dart

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

+ 9 - 0
lib/widgets/models/annot_attribute_bean.dart

@@ -0,0 +1,9 @@
+
+class AnnotAttributeBean{
+
+  int color;
+  int alpha;
+
+  AnnotAttributeBean(this.color, this.alpha);
+
+}

+ 17 - 0
lib/widgets/models/annot_bean.dart

@@ -0,0 +1,17 @@
+import 'package:flutter/material.dart';
+import 'package:kmpdfkit_demo/widgets/constains.dart';
+
+class AnnotBean {
+  AnnotType annotType;
+  String normalImagePath;
+  String selectImagePath;
+  int annotColor;
+  int annotColorAlpha;
+
+  AnnotBean(
+      {required this.annotType,
+      required this.normalImagePath,
+      required this.selectImagePath,
+      required this.annotColor,
+      required this.annotColorAlpha});
+}

+ 49 - 43
lib/widgets/page/pdf_page_widget.dart

@@ -4,6 +4,7 @@ import 'package:kmpdfkit_demo/widgets/config.dart';
 import 'package:kmpdfkit_demo/widgets/events.dart';
 import 'package:kmpdfkit_demo/widgets/page_routes.dart';
 
+import '../function/pdf_buttom_annot_fun_widget.dart';
 
 class PDFPageWidget extends StatefulWidget {
   final String? documentPath;
@@ -16,56 +17,61 @@ class PDFPageWidget extends StatefulWidget {
 
 class _PDFPageWidgetState extends State<PDFPageWidget> {
   bool _fullScreen = false;
+  bool isInit = false;
 
   @override
   Widget build(BuildContext context) {
-    return Scaffold(body: Stack(
-      children: [
-        SafeArea(
-            child: ComPDFKitWidget(
-              documentPath: widget.documentPath,
-              configuration: applyNormalConfiguration(),
-              onComPDFKitWidgetCreate: () {
-                setReaderViewCallbackListener(
-                    onTapMainDocArea: () {
-                      setState(() {
-                        _fullScreen = !_fullScreen;
-                      });
-                    }
-                );
-              },
-            )),
-        Builder(builder: (context) {
-          return Positioned(
-              top: 0,
+    return Scaffold(
+      body: Stack(
+        children: [
+          SafeArea(
+              child: ComPDFKitWidget(
+            documentPath: widget.documentPath,
+            configuration: applyNormalConfiguration(),
+            onComPDFKitWidgetCreate: () {
+              setState(() {
+                isInit = true;
+              });
+              setReaderViewCallbackListener(onTapMainDocArea: () {
+                setState(() {
+                  _fullScreen = !_fullScreen;
+                });
+              });
+            },
+          )),
+          Builder(builder: (context) {
+            return Positioned(
+                top: 0,
+                left: 0,
+                right: 0,
+                child: AnimatedOpacity(
+                    opacity: _fullScreen ? 0.0 : 1.0,
+                    duration: const Duration(milliseconds: 200),
+                    child: AppBar(
+                      actions: [
+                        IconButton(
+                            onPressed: () {
+                              Navigator.pushNamed(context, PageRoutes.settings);
+                            },
+                            icon: const Icon(Icons.more_vert))
+                      ],
+                    )));
+          }),
+          Positioned(
+              bottom: 0,
               left: 0,
               right: 0,
               child: AnimatedOpacity(
                   opacity: _fullScreen ? 0.0 : 1.0,
                   duration: const Duration(milliseconds: 200),
-                  child: AppBar(
-                    actions: [
-                      IconButton(
-                          onPressed: () {
-                            Navigator.pushNamed(
-                                context, PageRoutes.settings);
-                          },
-                          icon: const Icon(Icons.more_vert))
-                    ],
-                  )));
-        }),
-        Positioned(
-            bottom: 0,
-            left: 0,
-            right: 0,
-            child: AnimatedOpacity(
-                opacity: _fullScreen ? 0.0 : 1.0,
-                duration: const Duration(milliseconds: 200),
-                child: Container(
-                  height: 60,
-                  color: Colors.blueGrey,
-                ))),
-      ],
-    ),);
+                  child: isInit
+                      ? const PDFButtonAnnotFunWidget()
+                      : Container(
+                          color: const Color(0xFFEFF4FD),
+                          width: double.infinity,
+                          height: 60))),
+        ],
+      ),
+    );
   }
 }

+ 65 - 1
pubspec.lock

@@ -1,6 +1,14 @@
 # Generated by pub
 # See https://dart.dev/tools/pub/glossary#lockfile
 packages:
+  args:
+    dependency: transitive
+    description:
+      name: args
+      sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.4.0"
   async:
     dependency: transitive
     description:
@@ -94,6 +102,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.0.9"
+  flutter_svg:
+    dependency: "direct main"
+    description:
+      name: flutter_svg
+      sha256: "017cb37b3e9bee44cc800e35469f295d537484602f02ce6ceb5876236cc7e1fe"
+      url: "https://pub.dev"
+    source: hosted
+    version: "2.0.3"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -160,6 +176,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.8.2"
+  path_parsing:
+    dependency: transitive
+    description:
+      name: path_parsing
+      sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.0.1"
   permission_handler:
     dependency: "direct main"
     description:
@@ -200,6 +224,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "0.1.2"
+  petitparser:
+    dependency: transitive
+    description:
+      name: petitparser
+      sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4"
+      url: "https://pub.dev"
+    source: hosted
+    version: "5.1.0"
   plugin_platform_interface:
     dependency: transitive
     description:
@@ -269,6 +301,30 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "0.4.16"
+  vector_graphics:
+    dependency: transitive
+    description:
+      name: vector_graphics
+      sha256: "2f317d969a9f1eb59d1890643107da749698b7c08c4b0532fc95c8a7130d2803"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.3"
+  vector_graphics_codec:
+    dependency: transitive
+    description:
+      name: vector_graphics_codec
+      sha256: "13cccfda2dd61232a19dfb769b7a907e2ab23aabfebb9053c81e29c6c11b1766"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.3"
+  vector_graphics_compiler:
+    dependency: transitive
+    description:
+      name: vector_graphics_compiler
+      sha256: "524c9889a1327401124fe068840a8867f0d57987c1219a2a696ade629ec2bec3"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.1.3"
   vector_math:
     dependency: transitive
     description:
@@ -285,6 +341,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "3.1.3"
+  xml:
+    dependency: transitive
+    description:
+      name: xml
+      sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5"
+      url: "https://pub.dev"
+    source: hosted
+    version: "6.2.2"
 sdks:
   dart: ">=2.19.1 <3.0.0"
-  flutter: ">=3.0.0"
+  flutter: ">=3.7.0-0"

+ 3 - 0
pubspec.yaml

@@ -39,6 +39,7 @@ dependencies:
   provider: ^6.0.5
   # open_file: ^3.2.1
   permission_handler: ^10.2.0
+  flutter_svg: ^2.0.3
 
 
 dev_dependencies:
@@ -62,6 +63,8 @@ flutter:
   # included with your application, so that you can use the icons in
   # the material Icons class.
   uses-material-design: true
+  assets:
+    - assets/images/