PDF文档阅读器应用


欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net

适配的第三方库地址:

  • pdf_render: https://pub.dev/packages/pdf_render
  • file_selector: https://pub.dev/packages/file_selector
  • shared_preferences: https://pub.dev/packages/shared_preferences
  • screen: https://pub.dev/packages/screen

一、项目概述

运行效果图

image-20260412100927921

image-20260412100936961

image-20260412100947275

image-20260412100952584

1.1 应用简介

PDF文档阅读器是一款功能完善的文档阅读应用,支持PDF文档打开、阅读、书签管理、批注功能,提供夜间模式和阅读进度同步。应用以清新的蓝色为主色调,象征专业与高效。涵盖文档库、阅读器、书签管理、设置四大模块。用户可以打开本地PDF文件、浏览文档内容、添加书签和批注、调整阅读设置,享受舒适的阅读体验。

1.2 核心功能

功能模块功能描述实现方式
PDF渲染高质量PDF页面渲染显示pdf_render
文件选择从本地选择PDF文件打开file_selector
书签管理添加、删除、跳转书签本地存储
批注功能高亮、下划线、文字批注自定义绘制
夜间模式深色主题减少眼睛疲劳主题切换
阅读进度自动保存阅读位置shared_preferences
缩放控制放大缩小页面内容手势缩放
目录导航快速跳转到指定章节目录解析
屏幕亮度调节屏幕亮度screen
文档搜索搜索文档内容全文检索

1.3 视图模式定义

序号模式名称Emoji描述适用场景
1单页模式📄单页连续滚动普通阅读
2双页模式📖双页对开显示杂志书籍
3连续模式📜连续垂直滚动快速浏览

1.4 批注类型定义

序号批注名称Emoji颜色描述
1高亮🖍️黄色标记重要内容
2下划线📝绿色强调关键文字
3批注💬蓝色添加阅读笔记
4书签🔖红色标记阅读位置

1.5 技术栈

技术领域技术选型版本要求
开发框架Flutter>= 3.0.0
编程语言Dart>= 2.17.0
设计规范Material Design 3-
PDF渲染pdf_render>= 1.4.0
文件选择file_selector>= 1.0.0
数据存储shared_preferences>= 2.0.0
屏幕控制screen>= 0.0.5
目标平台鸿蒙OS / Web / AndroidAPI 21+

1.6 项目结构

lib/
└── main_pdf_reader.dart
    ├── PDFReaderApp                    # 应用入口
    ├── ViewMode                        # 视图模式枚举
    ├── AnnotationType                  # 批注类型枚举
    ├── PDFDocument                     # PDF文档模型
    ├── Bookmark                        # 书签模型
    ├── Annotation                      # 批注模型
    ├── ReadingProgress                 # 阅读进度模型
    ├── PDFReaderHomePage               # 主页面(底部导航)
    ├── _buildLibraryPage               # 文档库页面
    ├── _buildReaderPage                # 阅读器页面
    ├── _buildBookmarksPage             # 书签页面
    ├── _buildSettingsPage              # 设置页面
    └── PDFSearchDelegate               # 搜索代理

二、系统架构

2.1 整体架构图

Data Layer

Business Layer

Presentation Layer

主页面
PDFReaderHomePage

文档库

阅读器

书签管理

设置

文档列表

搜索功能

快速操作

PDF渲染

页面导航

批注工具

书签列表

书签跳转

显示设置

阅读设置

存储设置

PDF管理器
PDFManager

书签管理器
BookmarkManager

批注管理器
AnnotationManager

进度管理器
ProgressManager

PDFDocument
文档模型

Bookmark
书签模型

Annotation
批注模型

ReadingProgress
进度模型

2.2 类图设计

contains

contains

has

PDFReaderApp

+Widget build()

«enumeration»

ViewMode

+String label

+String emoji

+String description

+singlePage()

+twoPage()

+continuous()

«enumeration»

AnnotationType

+String label

+String emoji

+Color color

+highlight()

+underline()

+note()

+bookmark()

PDFDocument

+String id

+String name

+String path

+int totalPages

+int currentPage

+int fileSize

+DateTime lastOpened

+List<Bookmark> bookmarks

+List<Annotation> annotations

+double zoom

+double progress

+String fileSizeText

+copyWith()

Bookmark

+String id

+String documentId

+int pageNumber

+String title

+DateTime createdAt

Annotation

+String id

+String documentId

+int pageNumber

+AnnotationType type

+String content

+Offset position

+DateTime createdAt

ReadingProgress

+String documentId

+int currentPage

+double zoom

+DateTime lastRead

+toJson()

+fromJson()

2.3 页面导航流程

打开文件

点击文档

添加书签

添加批注

调整设置

翻页

应用启动

文档库

选择操作

选择PDF文件

加载PDF

阅读器页面

阅读PDF

用户操作

书签管理

批注工具

设置页面

返回文档库

2.4 阅读流程

存储服务阅读器文档库用户存储服务阅读器文档库用户alt[添加书签]alt[添加批注]loop[阅读过程]选择PDF文档打开文档加载阅读进度返回上次位置显示PDF页面翻页/缩放更新页面状态自动保存进度点击添加书签保存书签书签添加成功选择批注工具保存批注批注添加成功关闭文档保存最终进度返回文档列表

三、核心模块设计

3.1 数据模型设计

3.1.1 视图模式枚举 (ViewMode)
enum ViewMode {
  singlePage(label: '单页', emoji: '📄', description: '单页连续滚动'),
  twoPage(label: '双页', emoji: '📖', description: '双页对开显示'),
  continuous(label: '连续', emoji: '📜', description: '连续垂直滚动');

  final String label;
  final String emoji;
  final String description;
  const ViewMode({
    required this.label,
    required this.emoji,
    required this.description,
  });
}
3.1.2 批注类型枚举 (AnnotationType)
enum AnnotationType {
  highlight(label: '高亮', emoji: '🖍️', color: Colors.yellow),
  underline(label: '下划线', emoji: '📝', color: Colors.green),
  note(label: '批注', emoji: '💬', color: Colors.blue),
  bookmark(label: '书签', emoji: '🔖', color: Colors.red);

  final String label;
  final String emoji;
  final Color color;
  const AnnotationType({
    required this.label,
    required this.emoji,
    required this.color,
  });
}
3.1.3 PDF文档模型 (PDFDocument)
class PDFDocument {
  final String id;
  final String name;
  final String path;
  final int totalPages;
  final int currentPage;
  final int fileSize;
  final DateTime lastOpened;
  final List<Bookmark> bookmarks;
  final List<Annotation> annotations;
  final double zoom;

  const PDFDocument({
    required this.id,
    required this.name,
    required this.path,
    required this.totalPages,
    required this.currentPage,
    required this.fileSize,
    required this.lastOpened,
    required this.bookmarks,
    required this.annotations,
    required this.zoom,
  });

  double get progress => totalPages > 0 ? currentPage / totalPages : 0;

  String get fileSizeText {
    if (fileSize < 1024) return '$fileSize B';
    if (fileSize < 1024 * 1024) return '${(fileSize / 1024).toStringAsFixed(1)} KB';
    return '${(fileSize / (1024 * 1024)).toStringAsFixed(1)} MB';
  }
}
3.1.4 文档功能分布
50%20%15%10%5%文档功能使用分布示例阅读浏览书签管理批注功能夜间模式其他功能

3.2 页面结构设计

3.2.1 主页面布局

PDFReaderHomePage

IndexedStack

文档库页面

阅读器页面

书签页面

设置页面

NavigationBar

文档库 Tab

阅读 Tab

书签 Tab

设置 Tab

3.2.2 文档库页面结构

文档库页面

SliverAppBar

快速操作区

文档列表

打开PDF

阅读历史

收藏文档

云端同步

文档卡片

文档信息

阅读进度

操作按钮

3.2.3 阅读器页面结构

阅读器页面

AppBar

PDF视图区

页面导航栏

缩放按钮

文档标题

主题切换

视图模式

更多菜单

PDF页面渲染

翻页手势

缩放手势

首页按钮

上一页

页码滑块

下一页

末页按钮

3.2.4 设置页面结构

设置页面

SliverAppBar

显示设置

阅读设置

存储设置

关于信息

夜间模式

屏幕亮度

字体大小

默认视图

自动保存

翻页动画

全屏阅读

云端同步

清除缓存

存储位置

3.3 PDF渲染逻辑

翻页

缩放

切换视图

添加批注

打开PDF文件

加载PDF文档

解析PDF结构

获取页数信息

渲染当前页

显示页面内容

用户操作

加载新页面

调整缩放比例

更新视图模式

绘制批注层

3.4 书签管理逻辑

点击添加书签

检查当前页

已有书签?

提示已存在

弹出输入框

输入书签标题

创建书签对象

保存到文档

更新UI显示

点击书签

跳转到目标页

更新当前页码

删除书签

确认删除

从列表移除

更新UI显示


四、UI设计规范

4.1 配色方案

应用以清新的蓝色为主色调,象征专业与高效:

颜色类型色值用途
主色#2196F3 (Blue)导航、主题元素
辅助色#64B5F6次要按钮
第三色#90CAF9高亮显示
背景色#FAFAFA页面背景
卡片背景#FFFFFF信息卡片
夜间背景#1A1A1A夜间模式背景
强调色#FF5722重要操作
成功色#4CAF50完成状态

4.2 批注类型配色

批注类型色值视觉效果
高亮#FFEB3B黄色高亮
下划线#4CAF50绿色下划线
批注#2196F3蓝色批注框
书签#F44336红色书签图标

4.3 字体规范

元素字号字重颜色
页面标题24pxBold主色
文档名称16pxBold#000000
页码显示14pxBold主色
提示文字12pxRegular#666666
进度文字12pxRegular#888888

4.4 组件规范

4.4.1 文档卡片
┌─────────────────────────────────────┐
│  ┌──────┐                           │
│  │ PDF  │  Flutter开发指南.pdf      │
│  │ 图标 │  128页 · 2.4 MB           │
│  └──────┘                           │
│  ═════════════●══════════════      │
│  阅读进度: 45/128页 (35%)     ⋮    │
└─────────────────────────────────────┘
4.4.2 阅读器界面
┌─────────────────────────────────────┐
│  ← Flutter开发指南.pdf    🌙 📋 ⋮  │
├─────────────────────────────────────┤
│                                     │
│  ┌─────────────────────────────┐   │
│  │  第 45 页 / 共 128 页        │   │
│  │                             │   │
│  │  ████████████████████       │   │
│  │  ████████                   │   │
│  │                             │   │
│  │  ████████████████████████   │   │
│  │  ████████████████████████   │   │
│  │                             │   │
│  │  ┌─────────────────────┐   │   │
│  │  │ PDF内容区域          │   │   │
│  │  │ (PDF渲染预览)        │   │   │
│  │  └─────────────────────┘   │   │
│  └─────────────────────────────┘   │
│                                     │
├─────────────────────────────────────┤
│  ◀◀  ◀  ═══════●════════  45/128  ▶  ▶▶  │
└─────────────────────────────────────┘
4.4.3 书签卡片
┌─────────────────────────────────────┐
│  🔖  Widget生命周期                 │
│      第 45 页            📄  🗑️    │
└─────────────────────────────────────┘
4.4.4 设置卡片
┌─────────────────────────────────────┐
│  显示设置                              │
│                                     │
│  🌙 夜间模式              [开关]     │
│  ─────────────────────────────      │
│  🔆 屏幕亮度                         │
│  ──────●────────────────  50%      │
│  ─────────────────────────────      │
│  📝 字体大小              中    ▶   │
└─────────────────────────────────────┘

五、核心功能实现

5.1 PDF文件选择实现

Future<void> _openPDFFile() async {
  try {
    const XTypeGroup typeGroup = XTypeGroup(
      label: 'PDF文档',
      extensions: ['pdf'],
    );
    final XFile? file = await openFile(acceptedTypeGroups: [typeGroup]);
    if (file != null) {
      final newDoc = PDFDocument(
        id: 'doc_${DateTime.now().millisecondsSinceEpoch}',
        name: file.name,
        path: file.path,
        totalPages: 100,
        currentPage: 1,
        fileSize: await file.length(),
        lastOpened: DateTime.now(),
        bookmarks: [],
        annotations: [],
        zoom: 1.0,
      );
      setState(() {
        _currentDocument = newDoc;
        _recentDocuments.insert(0, newDoc);
        _currentIndex = 1;
      });
    }
  } catch (e) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('打开文件失败: $e')),
    );
  }
}

5.2 书签管理实现

void _addBookmark() {
  if (_currentDocument == null) return;
  final page = _currentDocument!.currentPage;
  final hasBookmark = _currentDocument!.bookmarks.any((b) => b.pageNumber == page);
  if (hasBookmark) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('当前页面已有书签')),
    );
    return;
  }
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('添加书签'),
      content: TextField(
        controller: _annotationController,
        decoration: const InputDecoration(
          labelText: '书签标题',
          hintText: '请输入书签标题',
        ),
      ),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('取消'),
        ),
        ElevatedButton(
          onPressed: () {
            final bookmark = Bookmark(
              id: 'bm_${DateTime.now().millisecondsSinceEpoch}',
              documentId: _currentDocument!.id,
              pageNumber: page,
              title: _annotationController.text.isEmpty
                  ? '第$page页'
                  : _annotationController.text,
              createdAt: DateTime.now(),
            );
            setState(() {
              _currentDocument = _currentDocument!.copyWith(
                bookmarks: [..._currentDocument!.bookmarks, bookmark],
              );
            });
            _annotationController.clear();
            Navigator.pop(context);
          },
          child: const Text('确定'),
        ),
      ],
    ),
  );
}

5.3 批注功能实现

void _addAnnotation(AnnotationType type) {
  if (_currentDocument == null) return;
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: Text('${type.emoji} 添加${type.label}'),
      content: TextField(
        controller: _annotationController,
        decoration: const InputDecoration(
          labelText: '批注内容',
          hintText: '请输入批注内容',
        ),
        maxLines: 3,
      ),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('取消'),
        ),
        ElevatedButton(
          onPressed: () {
            final annotation = Annotation(
              id: 'an_${DateTime.now().millisecondsSinceEpoch}',
              documentId: _currentDocument!.id,
              pageNumber: _currentDocument!.currentPage,
              type: type,
              content: _annotationController.text,
              position: const Offset(100, 200),
              createdAt: DateTime.now(),
            );
            setState(() {
              _currentDocument = _currentDocument!.copyWith(
                annotations: [..._currentDocument!.annotations, annotation],
              );
            });
            _annotationController.clear();
            Navigator.pop(context);
          },
          child: const Text('确定'),
        ),
      ],
    ),
  );
}

5.4 夜间模式实现

void _toggleDarkMode() {
  setState(() {
    _isDarkMode = !_isDarkMode;
  });
  _saveSettings();
}

Future<void> _saveSettings() async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setBool('darkMode', _isDarkMode);
  await prefs.setDouble('brightness', _brightness);
}

Future<void> _loadSettings() async {
  final prefs = await SharedPreferences.getInstance();
  setState(() {
    _isDarkMode = prefs.getBool('darkMode') ?? false;
    _brightness = prefs.getDouble('brightness') ?? 0.5;
  });
}

5.5 页面导航实现

void _goToPage(int page) {
  if (_currentDocument == null) return;
  if (page < 1 || page > _currentDocument!.totalPages) return;
  setState(() {
    _currentDocument = _currentDocument!.copyWith(currentPage: page);
  });
}

Widget _buildPageNavigationBar() {
  return Container(
    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
    child: Row(
      children: [
        IconButton(
          icon: const Icon(Icons.first_page),
          onPressed: () => _goToPage(1),
        ),
        IconButton(
          icon: const Icon(Icons.chevron_left),
          onPressed: () => _goToPage(_currentDocument!.currentPage - 1),
        ),
        Expanded(
          child: Slider(
            value: _currentDocument!.currentPage.toDouble(),
            min: 1,
            max: _currentDocument!.totalPages.toDouble(),
            onChanged: (value) => _goToPage(value.round()),
          ),
        ),
        Container(
          padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
          child: Text(
            '${_currentDocument!.currentPage} / ${_currentDocument!.totalPages}',
          ),
        ),
        IconButton(
          icon: const Icon(Icons.chevron_right),
          onPressed: () => _goToPage(_currentDocument!.currentPage + 1),
        ),
        IconButton(
          icon: const Icon(Icons.last_page),
          onPressed: () => _goToPage(_currentDocument!.totalPages),
        ),
      ],
    ),
  );
}

六、交互设计

6.1 阅读流程

设置阅读器文档库用户设置阅读器文档库用户alt[添加书签]alt[切换主题]loop[阅读过程]打开应用显示文档列表选择文档打开PDF显示PDF内容翻页/缩放更新显示点击书签按钮显示添加对话框输入标题确认书签添加成功点击主题按钮切换夜间模式更新主题显示返回文档库保存阅读进度返回列表

6.2 书签管理流程

阅读PDF

点击添加书签

当前页有书签?

提示已存在

弹出输入框

输入书签标题

点击确定

保存书签

显示成功提示

点击书签图标

跳转到目标页

继续阅读

长按书签

显示删除选项

确认删除

书签已删除

6.3 设置调整流程

点击显示设置

点击阅读设置

点击存储设置

切换开关

滑动滑块

点击选项

选择模式

切换开关

切换开关

点击同步

点击清除

设置页面

显示设置

阅读设置

存储设置

夜间模式

亮度调节

字体设置

视图模式

自动保存

翻页动画

云端同步

清除缓存


七、扩展功能规划

7.1 后续版本规划

2024-01-072024-01-142024-01-212024-01-282024-02-042024-02-112024-02-182024-02-252024-03-032024-03-102024-03-17基础UI框架 PDF渲染功能 书签管理 批注功能 夜间模式 阅读进度同步 全文搜索 目录导航 云端同步 V1.0 基础版本V1.1 增强版本V1.2 进阶版本PDF文档阅读器开发计划

7.2 功能扩展建议

7.2.1 高级批注功能

批注功能:

  • 手写批注支持
  • 语音批注记录
  • 批注导出分享
  • 批注搜索定位
7.2.2 文档管理功能

文档管理:

  • 文件夹分类管理
  • 标签系统
  • 文档收藏夹
  • 最近阅读列表
7.2.3 云端同步功能

云端功能:

  • 多设备同步
  • 阅读进度云备份
  • 书签批注同步
  • 文档云端存储

八、注意事项

8.1 开发注意事项

  1. PDF性能:大文件PDF需要分页加载,避免内存溢出

  2. 渲染优化:使用缓存机制优化页面渲染性能

  3. 存储管理:定期清理临时文件,避免占用过多存储空间

  4. 权限处理:正确申请文件读取权限

  5. 兼容性:适配不同版本的PDF格式

8.2 常见问题

问题原因解决方案
PDF打开失败文件格式不支持提示用户文件格式错误
渲染卡顿文件过大使用分页加载和缓存
书签丢失数据未保存确保及时保存到本地
夜间模式不生效主题未切换检查主题设置逻辑
进度不同步存储失败添加保存确认机制

8.3 使用技巧

📖 PDF阅读器使用技巧 📖

阅读技巧

  • 双击页面快速缩放
  • 左右滑动翻页
  • 点击边缘区域翻页
  • 长按文字选择复制

书签管理

  • 重要页面及时添加书签
  • 使用有意义的书签标题
  • 定期整理书签列表
  • 利用书签快速定位

阅读设置

  • 夜间阅读开启夜间模式
  • 调整亮度保护眼睛
  • 选择适合的视图模式
  • 开启自动保存进度

九、运行说明

9.1 环境要求

环境版本要求
Flutter SDK>= 3.0.0
Dart SDK>= 2.17.0
鸿蒙OSAPI 21+
AndroidAPI 21+
Web浏览器Chrome 90+

9.2 依赖配置

pubspec.yaml 中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.5.3
  file_selector: ^1.0.3
  pdf_render: ^1.4.12
  screen: ^0.0.5

9.3 运行命令

# 查看可用设备
flutter devices

# 运行到Web服务器
flutter run -d web-server -t lib/main_pdf_reader.dart --web-port 8145

# 运行到鸿蒙设备
flutter run -d 127.0.0.1:5555 lib/main_pdf_reader.dart

# 代码分析
flutter analyze lib/main_pdf_reader.dart

十、总结

PDF文档阅读器应用是一款功能完善的文档阅读工具,支持PDF文档打开、阅读、书签管理、批注功能,提供夜间模式和阅读进度同步。应用采用 Material Design 3 设计规范,以清新的蓝色为主色调,象征专业与高效。

核心功能涵盖PDF渲染、文件选择、书签管理、批注功能、夜间模式、阅读进度、缩放控制、目录导航、屏幕亮度、文档搜索十大模块。用户可以打开本地PDF文件、浏览文档内容、添加书签和批注、调整阅读设置,享受舒适的阅读体验。

应用支持单页模式、双页模式、连续模式三种视图模式,以及高亮、下划线、批注、书签四种批注类型,满足不同用户的阅读需求。通过本应用,希望能够为用户提供专业、高效的PDF阅读体验。

PDF文档阅读器——专业高效的阅读体验


Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐