You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:使用syncfusion_flutter_pdf实现PDF高亮标注的存储与读取

解决Syncfusion Flutter PDF高亮标注的持久化问题

要实现高亮标注的存储与读取,核心思路是将标注的关键信息序列化后保存到本地,下次打开PDF时再反序列化并恢复标注。以下是具体实现步骤和完整代码:

1. 添加依赖

在pubspec.yaml中添加本地存储依赖:

dependencies:
  flutter:
    sdk: flutter
  syncfusion_flutter_pdfviewer: ^latest_version
  shared_preferences: ^2.2.2

2. 定义可序列化的标注模型

创建SavedHighlight类存储高亮标注的关键属性(Syncfusion原生Annotation无法直接序列化):

class SavedHighlight {
  final int pageNumber;
  final List<PdfTextSpan> textSpans;
  final int colorValue;

  SavedHighlight({
    required this.pageNumber,
    required this.textSpans,
    required this.colorValue,
  });

  // 转换为JSON格式
  Map<String, dynamic> toJson() {
    return {
      'pageNumber': pageNumber,
      'textSpans': textSpans.map((span) => {
            'startIndex': span.startIndex,
            'endIndex': span.endIndex,
          }).toList(),
      'colorValue': colorValue,
    };
  }

  // 从JSON解析为对象
  factory SavedHighlight.fromJson(Map<String, dynamic> json) {
    final textSpans = (json['textSpans'] as List)
        .map((e) => PdfTextSpan(
              startIndex: e['startIndex'],
              endIndex: e['endIndex'],
            ))
        .toList();

    return SavedHighlight(
      pageNumber: json['pageNumber'],
      textSpans: textSpans,
      colorValue: json['colorValue'],
    );
  }
}

3. 实现保存与加载逻辑

修改原页面代码,加入存储和读取标注的方法:

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:developer' as dev;

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final GlobalKey<SfPdfViewerState> _pdfViewerKey = GlobalKey();
  late PdfViewerController _controller;
  late SharedPreferences _prefs;
  final String _annotationKey = 'pdf_highlights'; // 存储标注的键名

  @override
  void initState() {
    super.initState();
    _controller = PdfViewerController();
    _initPrefsAndLoadAnnotations();
  }

  // 初始化本地存储并加载标注
  Future<void> _initPrefsAndLoadAnnotations() async {
    _prefs = await SharedPreferences.getInstance();
    await _loadAnnotations();
  }

  // 加载保存的高亮标注
  Future<void> _loadAnnotations() async {
    final String? savedJson = _prefs.getString(_annotationKey);
    if (savedJson == null) return;

    final List<dynamic> jsonList = jsonDecode(savedJson);
    final List<SavedHighlight> savedHighlights = jsonList
        .map((json) => SavedHighlight.fromJson(json))
        .toList();

    // 将保存的高亮转换为Syncfusion标注并添加到控制器
    for (var highlight in savedHighlights) {
      final textMarkupAnnotation = TextMarkupAnnotation(
        textMarkupType: TextMarkupType.highlight,
        color: Color(highlight.colorValue),
        textSpans: highlight.textSpans,
        pageNumber: highlight.pageNumber,
      );
      _controller.addAnnotation(textMarkupAnnotation);
    }
  }

  // 保存当前所有高亮标注
  Future<void> _saveAnnotations() async {
    // 只筛选高亮类型的标注
    final List<Annotation> annotations = _controller.getAnnotations()
        .where((anno) => anno is TextMarkupAnnotation && (anno as TextMarkupAnnotation).textMarkupType == TextMarkupType.highlight)
        .toList();

    // 转换为可序列化的SavedHighlight列表
    final List<SavedHighlight> savedHighlights = annotations.map((anno) {
      final highlight = anno as TextMarkupAnnotation;
      return SavedHighlight(
        pageNumber: highlight.pageNumber,
        textSpans: highlight.textSpans,
        colorValue: highlight.color.value,
      );
    }).toList();

    // 转成JSON字符串保存到本地
    final String jsonStr = jsonEncode(savedHighlights);
    await _prefs.setString(_annotationKey, jsonStr);
    dev.log('标注已保存');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Syncfusion Flutter PDF Viewer'),
        actions: <Widget>[
          IconButton(
            icon: const Icon(
              Icons.bookmark,
              color: Colors.white,
              semanticLabel: 'Bookmark',
            ),
            onPressed: () {
              _pdfViewerKey.currentState?.openBookmarkView();
            },
          ),
          // 手动保存按钮(可选,配合自动保存使用)
          IconButton(
            icon: const Icon(Icons.save, color: Colors.white),
            onPressed: _saveAnnotations,
          ),
        ],
      ),
      body: SfPdfViewer.asset(
        'assets/name.pdf',
        controller: _controller,
        onAnnotationAdded: (Annotation annotation) {
          if (annotation is TextMarkupAnnotation && annotation.textMarkupType == TextMarkupType.highlight) {
            annotation.color = Colors.yellow;
            _saveAnnotations(); // 添加标注后自动保存
          }
        },
        onAnnotationEdited: (Annotation annotation) {
          if (annotation is TextMarkupAnnotation && annotation.textMarkupType == TextMarkupType.highlight) {
            annotation.color = Colors.blue;
            _saveAnnotations(); // 编辑标注后自动保存
          }
        },
        onAnnotationRemoved: (Annotation annotation) {
          if (annotation is TextMarkupAnnotation && annotation.textMarkupType == TextMarkupType.highlight) {
            _saveAnnotations(); // 删除标注后自动保存
          }
        },
        key: _pdfViewerKey,
      ),
    );
  }
}

关键说明

  • 序列化逻辑:提取TextMarkupAnnotation的页码、文本范围、颜色值核心属性,转换成可JSON序列化的格式存储。
  • 自动持久化:在标注添加、编辑、删除时自动触发保存,确保标注状态实时同步到本地;也可保留手动保存按钮作为补充。
  • 标注恢复:页面初始化时读取本地存储的标注,转换为Syncfusion原生标注对象并添加到控制器,实现打开PDF时自动恢复高亮。
  • 类型过滤:仅处理TextMarkupType.highlight类型的标注,避免混入下划线、删除线等其他注释类型。

内容的提问来源于stack exchange,提问作者coder lover

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 10:14:57