Browse Source

ComPDFKit(flutter) - 1.Outline列表

liuxiaolong 1 year ago
parent
commit
633c9eb64f
30 changed files with 471 additions and 68 deletions
  1. 2 0
      android/build.gradle
  2. 1 1
      android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/common/pdfstyle/provider/CPDFDataProvider.java
  3. 8 3
      android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/common/pdfstyle/provider/CPDFDocumentDataProvider.java
  4. 1 2
      android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/common/pdfstyle/provider/CPDFViewCtrlDataProvider.java
  5. 1 1
      android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/common/pdfstyle/provider/ParamsUtil.java
  6. 96 0
      android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/common/pdfprovider/viewer/pdfoutline/CPDFOutlineDatas.java
  7. 2 3
      android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/common/views/cpdfviewctrl/CPDFViewCtrlFlutter.java
  8. 5 3
      android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/plugin/CPDFDocumentPlugin.java
  9. 1 2
      android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/plugin/CPDFViewCtrlPlugin.java
  10. BIN
      assets/images/1.5x/ic_arrow_down.png
  11. BIN
      assets/images/1.5x/ic_arrow_right.png
  12. BIN
      assets/images/2.0x/ic_arrow_down.png
  13. BIN
      assets/images/2.0x/ic_arrow_right.png
  14. BIN
      assets/images/3.0x/ic_arrow_down.png
  15. BIN
      assets/images/3.0x/ic_arrow_right.png
  16. BIN
      assets/images/ic_arrow_down.png
  17. BIN
      assets/images/ic_arrow_right.png
  18. 1 1
      example/lib/main.dart
  19. 1 0
      lib/common/util/Strings.dart
  20. 14 0
      lib/core/document/cpdf_document.dart
  21. 45 0
      lib/models/cpdf_outline_data.dart
  22. 4 0
      lib/theme/colors.dart
  23. 5 1
      lib/theme/themes.dart
  24. 17 11
      lib/widgets/common/views/cpdf_main_tool_bar.dart
  25. 2 6
      lib/widgets/common/views/cpdf_tool_bar.dart
  26. 59 22
      lib/widgets/viewer/pdfbota/cpdf_bota_page.dart
  27. 123 0
      lib/widgets/viewer/pdfbota/pdfoutline/cpdf_outline_item.dart
  28. 77 0
      lib/widgets/viewer/pdfbota/pdfoutline/cpdf_outline_page.dart
  29. 5 10
      lib/widgets/viewer/pdfbota/pdfthumbnail/cpdf_thumbnail_page.dart
  30. 1 2
      pubspec.yaml

+ 2 - 0
android/build.gradle

@@ -47,6 +47,8 @@ android {
         api 'com.compdf:compdfkit:1.9.1-SNAPSHOT'
         api 'com.compdf:compdfkit-ui:1.9.1-SNAPSHOT'
         api 'com.compdf:compdfkit-tools:1.9.1-SNAPSHOT'
+        implementation 'com.google.code.gson:gson:2.9.1'
+
     }
 
     testOptions {

+ 1 - 1
android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/common/pdfstyle/provider/CPDFDataProvider.java

@@ -7,7 +7,7 @@
  * This notice may not be removed from this file.
  */
 
-package com.compdfkit.flutter.compdfkit_flutter.common.pdfstyle.provider;
+package com.compdfkit.flutter.compdfkit_flutter.common.pdfprovider.provider;
 
 
 import io.flutter.plugin.common.MethodCall;

+ 8 - 3
android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/common/pdfstyle/provider/CPDFDocumentDataProvider.java

@@ -7,7 +7,7 @@
  * This notice may not be removed from this file.
  */
 
-package com.compdfkit.flutter.compdfkit_flutter.common.pdfstyle.provider;
+package com.compdfkit.flutter.compdfkit_flutter.common.pdfprovider.provider;
 
 import android.graphics.Bitmap;
 import android.graphics.RectF;
@@ -16,14 +16,14 @@ import android.graphics.drawable.Drawable;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.bumptech.glide.Glide;
 import com.bumptech.glide.load.engine.DiskCacheStrategy;
 import com.bumptech.glide.request.target.CustomTarget;
 import com.bumptech.glide.request.transition.Transition;
 import com.compdfkit.core.document.CPDFDocument;
-import com.compdfkit.flutter.compdfkit_flutter.common.views.cpdfviewctrl.CPDFViewCtrl;
+import com.compdfkit.flutter.compdfkit_flutter.common.pdfprovider.viewer.pdfoutline.CPDFOutlineDatas;
 import com.compdfkit.tools.common.utils.glide.CPDFWrapper;
 import com.compdfkit.tools.common.utils.glide.GlideApp;
+import com.compdfkit.tools.viewer.pdfoutline.data.COutlineDatas;
 
 import java.io.ByteArrayOutputStream;
 import java.util.HashMap;
@@ -85,6 +85,9 @@ public class CPDFDocumentDataProvider implements CPDFDataProvider {
                             }
                         });
                 break;
+            case Method.GET_OUTLINE:
+                    result.success(CPDFOutlineDatas.getOutlineList(document, Integer.MAX_VALUE));
+                break;
             default:
                 break;
         }
@@ -99,5 +102,7 @@ public class CPDFDocumentDataProvider implements CPDFDataProvider {
         public static final String RENDER_PAGE_AT_INDEX = "renderPageAtIndex";
 
         public static final String OPEN_DOCUMENT = "openDocument";
+
+        public static final String GET_OUTLINE = "getOutline";
     }
 }

+ 1 - 2
android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/common/pdfstyle/provider/CPDFViewCtrlDataProvider.java

@@ -7,7 +7,7 @@
  * This notice may not be removed from this file.
  */
 
-package com.compdfkit.flutter.compdfkit_flutter.common.pdfstyle.provider;
+package com.compdfkit.flutter.compdfkit_flutter.common.pdfprovider.provider;
 
 
 import android.graphics.Color;
@@ -16,7 +16,6 @@ import com.compdfkit.flutter.compdfkit_flutter.common.utils.ColorUtils;
 import com.compdfkit.flutter.compdfkit_flutter.common.views.cpdfviewctrl.CPDFViewCtrl;
 import com.compdfkit.ui.reader.CPDFReaderView;
 
-import java.util.HashMap;
 import java.util.Map;
 
 import io.flutter.plugin.common.MethodCall;

+ 1 - 1
android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/common/pdfstyle/provider/ParamsUtil.java

@@ -7,7 +7,7 @@
  * This notice may not be removed from this file.
  */
 
-package com.compdfkit.flutter.compdfkit_flutter.common.pdfstyle.provider;
+package com.compdfkit.flutter.compdfkit_flutter.common.pdfprovider.provider;
 
 
 

+ 96 - 0
android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/common/pdfprovider/viewer/pdfoutline/CPDFOutlineDatas.java

@@ -0,0 +1,96 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ * <p>
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+
+package com.compdfkit.flutter.compdfkit_flutter.common.pdfprovider.viewer.pdfoutline;
+
+import com.compdfkit.core.document.CPDFDocument;
+import com.compdfkit.core.document.CPDFOutline;
+import com.compdfkit.tools.common.views.pdfview.CPDFViewCtrl;
+import com.compdfkit.tools.viewer.pdfoutline.bean.COutlineData;
+import com.google.gson.Gson;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class CPDFOutlineDatas {
+
+
+    /**
+     * Get the list of outlines for a PDF document
+     * @param document
+     * @see COutlineData
+     * @return ArrayList COutlineData
+     */
+    public static String getOutlineList(CPDFDocument document, int maxLevel) {
+        // Retrieve the outline root from the PDF document and convert it to a list of COutlineData objects
+        if (document == null) {
+            return "";
+        }
+        CPDFOutline outlineRoot = document.getOutlineRoot();
+        List<COutlineData> list = null;
+        if (outlineRoot != null) {
+            list =  convertToCOutlineDataList(outlineRoot.getChildList(), maxLevel);
+        } else {
+            list =  new ArrayList<>();
+        }
+        if (list == null || list.size() <= 0){
+            return "";
+        }
+        String json = new Gson().toJson(list);
+        return json;
+
+    }
+
+    /**
+     * Recursively convert an array of CPDFOutline objects to an ArrayList of COutlineData objects
+     * @param outlines outline list data
+     */
+    private static ArrayList<COutlineData> convertToCOutlineDataList(CPDFOutline[] outlines, int maxLevel) {
+        ArrayList<COutlineData> outlineDataList = new ArrayList<>();
+        for (CPDFOutline outline : outlines) {
+            // Convert the CPDFOutline object to a COutlineData object
+            COutlineData outlineData = convertToCOutlineData(outline, maxLevel);
+            // Only add the COutlineData object to the list if its level is 1
+            if (outlineData.getLevel() == 1) {
+                outlineDataList.add(outlineData);
+            }
+            // If the CPDFOutline object has child outlines, convert them to COutlineData objects and add them to the list
+            if (outline.getChildList() != null && outline.getChildList().length > 0) {
+                outlineDataList.addAll(convertToCOutlineDataList(outline.getChildList(), maxLevel));
+            }
+        }
+        return outlineDataList;
+    }
+
+    /**
+     * Convert a CPDFOutline object to a COutlineData object
+     */
+    private static COutlineData convertToCOutlineData(CPDFOutline outline, int maxLevel) {
+        COutlineData outlineData = new COutlineData();
+        outlineData.setLevel(outline.getLevel());
+        outlineData.setPageIndex(outline.getDestination().getPageIndex());
+        outlineData.setTitle(outline.getTitle());
+        outlineData.setChildOutline(new ArrayList<>());
+
+        // If the CPDFOutline object has child outlines, convert them to COutlineData objects and add them to the list
+        if (outline.getChildList() != null && outline.getChildList().length > 0) {
+            for (CPDFOutline child : outline.getChildList()) {
+                if (child.getLevel() <= maxLevel) {
+                    outlineData.getChildOutline().add(convertToCOutlineData(child, maxLevel));
+                }
+            }
+        }
+        return outlineData;
+    }
+
+}

+ 2 - 3
android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/common/views/cpdfviewctrl/CPDFViewCtrlFlutter.java

@@ -17,8 +17,7 @@ import android.view.View;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.compdfkit.flutter.compdfkit_flutter.common.pdfstyle.provider.CPDFDocumentDataProvider;
-import com.compdfkit.flutter.compdfkit_flutter.common.pdfstyle.provider.CPDFViewCtrlDataProvider;
+import com.compdfkit.flutter.compdfkit_flutter.common.pdfprovider.provider.CPDFViewCtrlDataProvider;
 import com.compdfkit.flutter.compdfkit_flutter.common.utils.task.CExtractAssetFileTask;
 import com.compdfkit.flutter.compdfkit_flutter.plugin.CPDFDocumentPlugin;
 import com.compdfkit.flutter.compdfkit_flutter.plugin.CPDFViewCtrlPlugin;
@@ -30,7 +29,7 @@ import io.flutter.plugin.platform.PlatformView;
 
 public class CPDFViewCtrlFlutter implements PlatformView {
 
-    public static final String QUICK_START_GUIDE = "PDF32000_2008.pdf";
+    public static final String QUICK_START_GUIDE = "Quick Start Guide.pdf";
 
     private CPDFViewCtrl pdfView;
 

+ 5 - 3
android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/plugin/CPDFDocumentPlugin.java

@@ -1,11 +1,13 @@
 package com.compdfkit.flutter.compdfkit_flutter.plugin;
 
+import android.util.Log;
+
 import androidx.annotation.NonNull;
 
 import com.compdfkit.core.document.CPDFDocument;
-import com.compdfkit.flutter.compdfkit_flutter.common.pdfstyle.provider.CPDFDataProvider;
-import com.compdfkit.flutter.compdfkit_flutter.common.pdfstyle.provider.CPDFDocumentDataProvider;
-import com.compdfkit.flutter.compdfkit_flutter.common.views.cpdfviewctrl.CPDFViewCtrl;
+import com.compdfkit.flutter.compdfkit_flutter.common.pdfprovider.provider.CPDFDataProvider;
+import com.compdfkit.flutter.compdfkit_flutter.common.pdfprovider.provider.CPDFDocumentDataProvider;
+import com.compdfkit.flutter.compdfkit_flutter.common.pdfprovider.viewer.pdfoutline.CPDFOutlineDatas;
 
 import io.flutter.plugin.common.BinaryMessenger;
 import io.flutter.plugin.common.MethodCall;

+ 1 - 2
android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/plugin/CPDFViewCtrlPlugin.java

@@ -12,8 +12,7 @@ package com.compdfkit.flutter.compdfkit_flutter.plugin;
 
 import androidx.annotation.NonNull;
 
-import com.compdfkit.flutter.compdfkit_flutter.common.pdfstyle.provider.CPDFViewCtrlDataProvider;
-import com.compdfkit.flutter.compdfkit_flutter.common.pdfstyle.provider.CPDFDataProvider;
+import com.compdfkit.flutter.compdfkit_flutter.common.pdfprovider.provider.CPDFViewCtrlDataProvider;
 import com.compdfkit.flutter.compdfkit_flutter.common.views.cpdfviewctrl.CPDFViewCtrl;
 
 

BIN
assets/images/1.5x/ic_arrow_down.png


BIN
assets/images/1.5x/ic_arrow_right.png


BIN
assets/images/2.0x/ic_arrow_down.png


BIN
assets/images/2.0x/ic_arrow_right.png


BIN
assets/images/3.0x/ic_arrow_down.png


BIN
assets/images/3.0x/ic_arrow_right.png


BIN
assets/images/ic_arrow_down.png


BIN
assets/images/ic_arrow_right.png


+ 1 - 1
example/lib/main.dart

@@ -45,6 +45,6 @@ class _MyAppState extends State<MyApp> {
 
   @override
   Widget build(BuildContext context) {
-    return const CPDFReaderViewPage(isDark: false);
+    return const CPDFReaderViewPage(isDark: true);
   }
 }

+ 1 - 0
lib/common/util/Strings.dart

@@ -11,4 +11,5 @@ class Strings {
   static const String Annotations = 'Annotations';
   static const String Bookmarks = 'Bookmarks';
   static const String Outlines = 'Outlines';
+  static const String NoOutlines = 'No Outlines';
 }

+ 14 - 0
lib/core/document/cpdf_document.dart

@@ -1,4 +1,7 @@
+import 'dart:convert';
+import 'dart:ffi';
 import 'dart:ui' as ui;
+import 'package:compdfkit_flutter/models/cpdf_outline_data.dart';
 import 'package:flutter/services.dart';
 
 import 'cpdf_document_error.dart';
@@ -49,4 +52,15 @@ class CPDFDocument {
         {'pageIndex': pageIndex, 'width': width, 'height': height});
     return imageData;
   }
+
+  Future<List<CPDFOutlineData>> getOutline() async {
+    String outlineJson = await _methodChannel.invokeMethod('getOutline');
+    List<dynamic> list = json.decode(outlineJson);
+    List<CPDFOutlineData> datas = List.empty(growable: true);
+    for (var value in list) {
+      datas.add(CPDFOutlineData.formJson(value));
+    }
+    return datas;
+  }
+
 }

+ 45 - 0
lib/models/cpdf_outline_data.dart

@@ -0,0 +1,45 @@
+///
+///  Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+///
+///  THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+///  AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+///  UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+///  This notice may not be removed from this file.
+
+class CPDFOutlineData {
+
+  int level = 1;
+
+  String title = '';
+
+  int pageIndex = 0;
+
+  bool expanded = false;
+
+  List<CPDFOutlineData>? childOutline;
+
+  CPDFOutlineData(
+      {this.level = 1, this.title = '', this.pageIndex = 0, this.childOutline});
+
+  CPDFOutlineData.formJson(Map<String, dynamic> json) {
+    level = json['level'];
+    title = json['title'];
+    pageIndex = json['pageIndex'];
+    childOutline = parse(json['childOutline']);
+  }
+
+  List<CPDFOutlineData> parse(List<dynamic> list) {
+    List<CPDFOutlineData> datas = List.empty(growable: true);
+    for (var value in list) {
+      datas.add(CPDFOutlineData.formJson(value));
+    }
+    return datas;
+  }
+
+  @override
+  String toString() {
+    return 'title: $title, level: $level, pageIndex:$pageIndex, childOutline:${childOutline.toString()} ';
+  }
+
+
+}

+ 4 - 0
lib/theme/colors.dart

@@ -29,4 +29,8 @@ class CPDFColors{
 
   static const titleTextColorLight = Color(0xFF43474D);
   static const titleTextColorDark = Colors.white;
+
+  static const readerSettingHeadBgColorLight = Color(0xFFF2F2F2);
+  static const readerSettingHeadBgColorDark = Color(0xFF303238);
+
 }

+ 5 - 1
lib/theme/themes.dart

@@ -12,13 +12,15 @@ import 'colors.dart';
 final comPDFKitLightTheme = ThemeData(
     splashColor: const Color(0x334982E6),
     highlightColor: const Color(0x334982E6),
+    dividerColor: const Color(0x1A000000),
     colorScheme: const ColorScheme.light(
         primary: Color(0xFF1460F3),
         onPrimary: Colors.black,
         primaryContainer: Color(0xFF1460F3),
         secondary: Color(0xFF1460F3),
         onSecondary: Colors.white,
-        background: Color(0xFFFAFCFF),
+        background: Colors.white,
+        secondaryContainer: Color(0xFFF2F2F2),
         onBackground: Color(0xFF43474D)),
     tabBarTheme: const TabBarTheme(
       dividerColor: Color(0xFF1460F3),
@@ -33,6 +35,7 @@ final comPDFKitLightTheme = ThemeData(
 final comPDFKitDarkTheme = ThemeData(
     highlightColor: const Color(0x334982E6),
     splashColor: const Color(0x334982E6),
+    dividerColor: const Color(0x1AFFFFFF),
     colorScheme: const ColorScheme.dark(
       primary: Color(0xFF6499FF),
       onPrimary: Colors.white,
@@ -40,6 +43,7 @@ final comPDFKitDarkTheme = ThemeData(
       secondary: Color(0xFF6499FF),
       onSecondary: Color(0xFF606773),
       background: Color(0xFF414347),
+      secondaryContainer: Color(0xFF414347),
     ),
     tabBarTheme: const TabBarTheme(
       dividerColor: Color(0xFF6499FF),

+ 17 - 11
lib/widgets/common/views/cpdf_main_tool_bar.dart

@@ -62,23 +62,29 @@ class CPDFMainToolbar extends StatelessWidget implements PreferredSizeWidget {
       : super(key: key) {
     actions = [
       CPDFActions.thumbnail(onPressed: (context) async {
-        int? pageIndex = await Navigator.push(context, MaterialPageRoute(builder: (_) {
-          return CPDFBotaPage(
-            ctrl: ctrl,
-            isDark: isDark,
-            types: const [CPDFBotaType.thumbnails],
-          );
-        }));
-        if (pageIndex != null) {
-          ctrl.setDisplayPageIndex(pageIndex);
-        }
+        pushToBota(context, const [CPDFBotaType.thumbnails, CPDFBotaType.outline]);
       }),
       CPDFActions.search(),
-      CPDFActions.bota(),
+      CPDFActions.bota(onPressed: (context)  {
+        pushToBota(context, const [CPDFBotaType.outline]);
+      }),
       CPDFActions.more()
     ];
   }
 
+  void pushToBota(BuildContext context, List<CPDFBotaType> types) async {
+    int? pageIndex = await Navigator.push(context, MaterialPageRoute(builder: (_) {
+      return CPDFBotaPage(
+        ctrl: ctrl,
+        isDark: isDark,
+        types: types,
+      );
+    }));
+    if (pageIndex != null) {
+      ctrl.setDisplayPageIndex(pageIndex);
+    }
+  }
+
   @override
   Widget build(BuildContext context) {
     return Theme(

+ 2 - 6
lib/widgets/common/views/cpdf_tool_bar.dart

@@ -8,7 +8,6 @@ import 'package:flutter/material.dart';
 ///  This notice may not be removed from this file.
 
 class CPDFToolbar extends AppBar {
-  final String titleText;
 
   final VoidCallback? onPressed;
 
@@ -16,7 +15,7 @@ class CPDFToolbar extends AppBar {
 
   CPDFToolbar(
       {super.key,
-      this.titleText = '',
+      super.title,
       super.actions,
       super.backgroundColor,
       this.leadingIcon,
@@ -31,9 +30,6 @@ class CPDFToolbar extends AppBar {
                 )
               : null,
           elevation: 2,
-          title: Text(titleText,
-              style: const TextStyle(
-                  fontFamily: 'sans-serif-medium', fontSize: 22)),
-          titleSpacing: titleText.isEmpty ? 15 : 0,
+          titleSpacing: title == null ? 15 : 0,
         );
 }

+ 59 - 22
lib/widgets/viewer/pdfbota/cpdf_bota_page.dart

@@ -2,10 +2,10 @@ import 'package:compdfkit_flutter/common/util/Strings.dart';
 import 'package:compdfkit_flutter/core/cpdf_view_ctrl.dart';
 import 'package:compdfkit_flutter/theme/colors.dart';
 import 'package:compdfkit_flutter/theme/themes.dart';
+import 'package:compdfkit_flutter/widgets/viewer/pdfbota/pdfoutline/cpdf_outline_page.dart';
 import 'package:compdfkit_flutter/widgets/viewer/pdfbota/pdfthumbnail/cpdf_thumbnail_page.dart';
 import 'package:flutter/material.dart';
 
-import '../../../core/document/cpdf_document.dart';
 import '../../common/views/cpdf_tool_bar.dart';
 
 ///  Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
@@ -34,7 +34,31 @@ class CPDFBotaPage extends StatefulWidget {
   State<CPDFBotaPage> createState() => _CPDFBotaPageState();
 }
 
-class _CPDFBotaPageState extends State<CPDFBotaPage> {
+class _CPDFBotaPageState extends State<CPDFBotaPage>
+    with SingleTickerProviderStateMixin {
+  late TabController _tabController;
+
+  final ValueNotifier<String> _title = ValueNotifier('');
+
+  @override
+  void initState() {
+    super.initState();
+    _title.value = titles()[0];
+    _tabController = TabController(length: widget.types.length, vsync: this);
+    _tabController.addListener(() {
+      if (!_tabController.indexIsChanging) {
+        _title.value = titles()[_tabController.index];
+      }
+    });
+  }
+
+  @override
+  void dispose() {
+    _title.dispose();
+    _tabController.dispose();
+    super.dispose();
+  }
+
   @override
   Widget build(BuildContext context) {
     return Theme(
@@ -43,25 +67,34 @@ class _CPDFBotaPageState extends State<CPDFBotaPage> {
             : comPDFKitLightTheme.copyWith(
                 colorScheme: comPDFKitLightTheme.colorScheme
                     .copyWith(primary: CPDFColors.backgroundLight)),
-        child: DefaultTabController(
-            length: widget.types.length,
-            child: Scaffold(
-              appBar: CPDFToolbar(
-                bottom:
-                    widget.types.length > 1 ? TabBar(tabs: _tabItems()) : null,
-                leadingIcon: IconButton(
-                    onPressed: () {
-                      Navigator.pop(context);
-                    },
-                    icon: Icon(Icons.arrow_back,
-                        color: Theme.of(context).colorScheme.onBackground)),
-                titleText: Strings.Thumbnails,
-              ),
-              body: TabBarView(children: _pages()),
-            )));
+        child: Scaffold(
+          appBar: CPDFToolbar(
+            bottom: widget.types.length > 1
+                ? TabBar(controller: _tabController, tabs: _tabItems())
+                : null,
+            leadingIcon: IconButton(
+                onPressed: () {
+                  Navigator.pop(context);
+                },
+                icon: Icon(Icons.arrow_back,
+                    color: Theme.of(context).colorScheme.onBackground)),
+            title: ValueListenableBuilder(
+              builder: (BuildContext context, String title, Widget? child) {
+                return Text(title,
+                    style: const TextStyle(
+                        fontFamily: 'sans-serif-medium', fontSize: 22));
+              },
+              valueListenable: _title,
+            ),
+          ),
+          body: TabBarView(
+            controller: _tabController,
+            children: _pages(),
+          ),
+        ));
   }
 
-  List<Widget> _tabItems() {
+  List<String> titles() {
     List<String> titles = widget.types.map((e) {
       switch (e) {
         case CPDFBotaType.annotations:
@@ -74,7 +107,11 @@ class _CPDFBotaPageState extends State<CPDFBotaPage> {
           return Strings.Thumbnails;
       }
     }).toList();
-    return titles.map((e) => Tab(text: e)).toList();
+    return titles;
+  }
+
+  List<Widget> _tabItems() {
+    return titles().map((e) => Tab(text: e)).toList();
   }
 
   List<Widget> _pages() {
@@ -89,8 +126,8 @@ class _CPDFBotaPageState extends State<CPDFBotaPage> {
         //   return Strings.Annotations;
         // case CPDFBotaType.bookmarks:
         //   return Strings.Bookmarks;
-        // case CPDFBotaType.outline:
-        //   return Strings.Outlines;
+        case CPDFBotaType.outline:
+          return CPDFOutlinePage(document: widget.ctrl.document);
         default:
           return Text(e.name);
       }

+ 123 - 0
lib/widgets/viewer/pdfbota/pdfoutline/cpdf_outline_item.dart

@@ -0,0 +1,123 @@
+///  Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+///
+///  THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+///  AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+///  UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+///  This notice may not be removed from this file.
+
+import 'package:compdfkit_flutter/models/cpdf_outline_data.dart';
+import 'package:flutter/material.dart';
+
+import '../../../../theme/colors.dart';
+
+typedef OutlineExpandedCallback = Function(bool isExpanded);
+
+class CPDFOutlineItem extends StatefulWidget {
+  final CPDFOutlineData outlineData;
+
+  final ValueChanged<int>? onDisplayPageIndex;
+
+  final OutlineExpandedCallback? onExpanded;
+
+  const CPDFOutlineItem(
+      {Key? key,
+      required this.outlineData,
+      this.onDisplayPageIndex,
+      this.onExpanded})
+      : super(key: key);
+
+  @override
+  State<CPDFOutlineItem> createState() => _CPDFOutlineItemState();
+}
+
+class _CPDFOutlineItemState extends State<CPDFOutlineItem> {
+  bool _expanded = false;
+
+  @override
+  void initState() {
+    super.initState();
+    _expanded = widget.outlineData.expanded;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    CPDFOutlineData data = widget.outlineData;
+    _expanded = widget.outlineData.expanded;
+    return Ink(
+      color: _backgroundColor(context, data.level == 1),
+      child: InkWell(
+        child: Column(
+          children: [
+            Row(
+              children: [
+                Padding(
+                    padding: EdgeInsets.only(left: (data.level - 1) * 8),
+                    child: AnimatedOpacity(
+                      opacity: data.childOutline != null &&
+                              data.childOutline!.isNotEmpty
+                          ? 1.0
+                          : 0.0,
+                      duration: const Duration(milliseconds: 100),
+                      child: IconButton(
+                        splashRadius: 20,
+                        icon: ImageIcon(AssetImage(
+                            _expanded
+                                ? 'assets/images/ic_arrow_down.png'
+                                : 'assets/images/ic_arrow_right.png',
+                            package: 'compdfkit_flutter')),
+                        onPressed: () {
+                          if (data.childOutline != null &&
+                              data.childOutline!.isNotEmpty) {
+                            if (widget.onExpanded != null) {
+                              widget.onExpanded!(_expanded);
+                            }
+                            _expanded = !_expanded;
+                            widget.outlineData.expanded = _expanded;
+                          }
+                        },
+                      ),
+                    )),
+                Expanded(
+                    child: Text(
+                  widget.outlineData.title,
+                  style: Theme.of(context).textTheme.titleSmall,
+                  maxLines: 1,
+                  overflow: TextOverflow.ellipsis,
+                )),
+                Padding(
+                  padding: const EdgeInsets.only(right: 16),
+                  child: Text(
+                    (widget.outlineData.pageIndex + 1).toString(),
+                    style: const TextStyle(fontSize: 12),
+                  ),
+                )
+              ],
+            ),
+            const Divider(height: 0.5)
+          ],
+        ),
+        onTap: () {
+          if (widget.onDisplayPageIndex != null) {
+            widget.onDisplayPageIndex!(data.pageIndex);
+          }
+        },
+      ),
+    );
+  }
+
+  Color _backgroundColor(BuildContext context, bool root) {
+    if (root) {
+      if (Theme.of(context).brightness == Brightness.dark) {
+        return CPDFColors.readerSettingHeadBgColorDark;
+      } else {
+        return CPDFColors.readerSettingHeadBgColorLight;
+      }
+    } else {
+      if (Theme.of(context).brightness == Brightness.dark) {
+        return const Color(0xFF414347);
+      } else {
+        return Colors.white;
+      }
+    }
+  }
+}

+ 77 - 0
lib/widgets/viewer/pdfbota/pdfoutline/cpdf_outline_page.dart

@@ -0,0 +1,77 @@
+///
+///  Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+///
+///  THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+///  AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+///  UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+///  This notice may not be removed from this file.
+
+import 'package:compdfkit_flutter/core/document/cpdf_document.dart';
+import 'package:compdfkit_flutter/models/cpdf_outline_data.dart';
+import 'package:flutter/material.dart';
+
+import 'cpdf_outline_item.dart';
+
+class CPDFOutlinePage extends StatefulWidget {
+  final CPDFDocument document;
+
+  const CPDFOutlinePage({Key? key, required this.document}) : super(key: key);
+
+  @override
+  State<CPDFOutlinePage> createState() => _CPDFOutlinePageState();
+}
+
+class _CPDFOutlinePageState extends State<CPDFOutlinePage>
+     {
+  List<CPDFOutlineData> _outlineList = List.empty(growable: true);
+
+  final globalKey = GlobalKey<AnimatedListState>();
+
+  @override
+  void initState() {
+    super.initState();
+    widget.document.getOutline().then((value) {
+      setState(() {
+        _outlineList = value;
+      });
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return ListView.builder(
+        itemCount: _outlineList.length,
+        itemBuilder: (context, index) {
+          return CPDFOutlineItem(
+            outlineData: _outlineList[index],
+            onDisplayPageIndex: (pageIndex) {
+              Navigator.pop(context, pageIndex);
+            },
+            onExpanded: (expanded) {
+              setState(() {
+                if (expanded) {
+                  loopCollapseItem(_outlineList[index]);
+                } else {
+                  _outlineList[index].expanded = true;
+                  _outlineList.insertAll(
+                      index + 1, _outlineList[index].childOutline ?? []);
+                }
+              });
+            },
+          );
+        });
+  }
+
+  @override
+  bool get wantKeepAlive => true;
+
+  void loopCollapseItem(CPDFOutlineData data) {
+    data.expanded = false;
+    data.childOutline?.forEach((element) {
+      if (element.childOutline != null && element.childOutline!.isNotEmpty) {
+        loopCollapseItem(element);
+      }
+      _outlineList.remove(element);
+    });
+  }
+}

+ 5 - 10
lib/widgets/viewer/pdfbota/pdfthumbnail/cpdf_thumbnail_page.dart

@@ -22,7 +22,7 @@ class CPDFThumbnailPage extends StatefulWidget {
   State<CPDFThumbnailPage> createState() => _CPDFThumbnailPageState();
 }
 
-class _CPDFThumbnailPageState extends State<CPDFThumbnailPage> {
+class _CPDFThumbnailPageState extends State<CPDFThumbnailPage> with AutomaticKeepAliveClientMixin{
   final ScrollController _scrollController = ScrollController();
 
   final GlobalKey globalKey = GlobalKey();
@@ -30,9 +30,6 @@ class _CPDFThumbnailPageState extends State<CPDFThumbnailPage> {
   @override
   void initState() {
     super.initState();
-    // WidgetsBinding.instance.addPostFrameCallback((_) {
-    //   scrollToItem(widget.currentPageIndex);
-    // });
   }
 
   @override
@@ -141,7 +138,7 @@ class _CPDFThumbnailPageState extends State<CPDFThumbnailPage> {
     );
   }
 
-  Widget _pageIndicator(int index, bool selected){
+  Widget _pageIndicator(int index, bool selected) {
     return Container(
         margin: const EdgeInsets.only(top: 8),
         decoration: BoxDecoration(
@@ -163,10 +160,8 @@ class _CPDFThumbnailPageState extends State<CPDFThumbnailPage> {
 
   // Function to scroll to a specific item in the GridView
   void scrollToItem(int index) {
-    // _scrollController.animateTo(
-    //   offset,
-    //   duration: const Duration(milliseconds: 500),
-    //   curve: Curves.fastOutSlowIn,
-    // );
   }
+
+  @override
+  bool get wantKeepAlive => true;
 }

+ 1 - 2
pubspec.yaml

@@ -30,5 +30,4 @@ flutter:
         pluginClass: CompdfkitFlutterPlugin
 
   assets:
-    - assets/images/
-    - testFiles/
+    - assets/images/