Skip to content
ComPDF
DemoFAQ
New Release

Open-Source PDF SDK & AI Document Processing

Get the full self-hosted SDK and AI document processing on GitHub. One-click deploy to quickly build your document workflows.

Guides

Annotation Replies

ComPDF Flutter SDK provides annotation reply APIs for adding, reading, updating, and removing replies on PDF annotations. You can also manage mark and review states for annotations and replies. These APIs are useful for review workflows, document collaboration, feedback tracking, and approval processes.

API Overview

Annotation reply APIs are available on CPDFDocument:

dart
Future<CPDFReplyAnnotation?> addAnnotationReply(
  CPDFAnnotation annotation, {
  required String content,
  String title = '',
});

Future<List<CPDFReplyAnnotation>> getAnnotationReplies(
  CPDFAnnotation annotation,
);

Future<bool> updateAnnotationReply(
  CPDFReplyAnnotation reply, {
  required String content,
  String? title,
});

Future<bool> removeAnnotationReply(CPDFReplyAnnotation reply);

Future<bool> removeAllAnnotationReplies(CPDFAnnotation annotation);

Future<bool> setAnnotationMarkState(
  CPDFAnnotation annotation,
  CPDFAnnotationMarkState state,
);

Future<CPDFAnnotationMarkState> getAnnotationMarkState(
  CPDFAnnotation annotation,
);

Future<bool> setAnnotationReviewState(
  CPDFAnnotation annotation,
  CPDFAnnotationReviewState state,
);

Future<CPDFAnnotationReviewState> getAnnotationReviewState(
  CPDFAnnotation annotation,
);

API description:

APIDescription
addAnnotationReplyAdds a plain reply to an annotation
getAnnotationRepliesGets all plain replies attached to an annotation
updateAnnotationReplyUpdates the content and optional title of a plain reply
removeAnnotationReplyRemoves one plain reply
removeAllAnnotationRepliesRemoves all plain replies from an annotation
setAnnotationMarkStateSets the mark state of an annotation or reply
getAnnotationMarkStateGets the mark state of an annotation or reply
setAnnotationReviewStateSets the review state of an annotation or reply
getAnnotationReviewStateGets the review state of an annotation or reply

Reply Model

Replies are represented by CPDFReplyAnnotation.

dart
class CPDFReplyAnnotation extends CPDFAnnotation {
  final DateTime? modifyDate;
}

Because CPDFReplyAnnotation extends CPDFAnnotation, it also includes the common annotation fields that are useful in a review workflow:

PropertyDescription
pageZero-based page index
titleReply author or title
contentReply content
createDateReply creation date, if available
modifyDateReply modification date, if available
markStateCurrent mark state
reviewStateCurrent review state

Use reply objects returned by addAnnotationReply or getAnnotationReplies when calling update, remove, or state APIs. Do not manually construct CPDFReplyAnnotation objects for these operations.

Mark and Review States

Mark states:

dart
enum CPDFAnnotationMarkState {
  marked,
  unmarked,
}

Review states:

dart
enum CPDFAnnotationReviewState {
  accepted,
  rejected,
  cancelled,
  completed,
  none,
  error,
}

Mark and review states can be applied to either a parent annotation or a reply annotation:

dart
await document.setAnnotationMarkState(
  annotation,
  CPDFAnnotationMarkState.marked,
);

await document.setAnnotationReviewState(
  annotation,
  CPDFAnnotationReviewState.accepted,
);

Get an Annotation

Most reply operations start from an existing annotation. The following example gets the first annotation on the first page.

dart
final CPDFPage page = document.pageAtIndex(0);
final List<CPDFAnnotation> annotations = await page.getAnnotations();

if (annotations.isEmpty) {
  return;
}

final CPDFAnnotation annotation = annotations.first;

Page indexes are zero-based.

Add a Reply

Use addAnnotationReply to add a plain reply to an annotation.

dart
final CPDFReplyAnnotation? reply = await document.addAnnotationReply(
  annotation,
  content: 'Please review this highlight.',
  title: 'Alex',
);

if (reply == null) {
  throw Exception('Add annotation reply failed.');
}

print('Reply content: ${reply.content}');

content is required. title is optional and is commonly used as the reply author name.

Read Replies

Use getAnnotationReplies to get all plain replies attached to an annotation.

dart
final List<CPDFReplyAnnotation> replies =
    await document.getAnnotationReplies(annotation);

for (final reply in replies) {
  print('Author: ${reply.title}');
  print('Content: ${reply.content}');
  print('Mark state: ${reply.markState.name}');
  print('Review state: ${reply.reviewState.name}');
}

The returned list contains plain replies only. Internal mark and review state replies are not exposed as separate reply items.

Update a Reply

Use updateAnnotationReply to update the content and optional title of an existing reply.

dart
final List<CPDFReplyAnnotation> replies =
    await document.getAnnotationReplies(annotation);

if (replies.isNotEmpty) {
  final bool success = await document.updateAnnotationReply(
    replies.first,
    content: 'Updated reply content.',
    title: 'Alex',
  );

  if (!success) {
    throw Exception('Update annotation reply failed.');
  }
}

The reply parameter should be a reply object returned by getAnnotationReplies or addAnnotationReply.

Remove a Reply

Use removeAnnotationReply to remove one plain reply.

dart
final List<CPDFReplyAnnotation> replies =
    await document.getAnnotationReplies(annotation);

if (replies.isNotEmpty) {
  final bool success = await document.removeAnnotationReply(replies.first);

  if (!success) {
    throw Exception('Remove annotation reply failed.');
  }
}

Remove All Replies

Use removeAllAnnotationReplies to remove all plain replies from an annotation.

dart
final bool success = await document.removeAllAnnotationReplies(annotation);

if (!success) {
  throw Exception('Remove all annotation replies failed.');
}

This API removes plain replies only. Mark and review state information is preserved.

Set Reply Mark and Review States

You can set mark or review state on a reply object.

dart
final List<CPDFReplyAnnotation> replies =
    await document.getAnnotationReplies(annotation);

if (replies.isNotEmpty) {
  final CPDFReplyAnnotation reply = replies.first;

  await document.setAnnotationMarkState(
    reply,
    CPDFAnnotationMarkState.marked,
  );

  await document.setAnnotationReviewState(
    reply,
    CPDFAnnotationReviewState.completed,
  );
}

After updating a reply state, call getAnnotationReplies again if you need the latest reply data.

Set Parent Annotation States

You can also set mark and review states on the parent annotation itself.

dart
await document.setAnnotationMarkState(
  annotation,
  CPDFAnnotationMarkState.marked,
);

await document.setAnnotationReviewState(
  annotation,
  CPDFAnnotationReviewState.accepted,
);

final CPDFAnnotationMarkState markState =
    await document.getAnnotationMarkState(annotation);

final CPDFAnnotationReviewState reviewState =
    await document.getAnnotationReviewState(annotation);

print('Mark: ${markState.name}');
print('Review: ${reviewState.name}');

Use in CPDFReaderWidget

When you use CPDFReaderWidget, call the same APIs through controller.document.

dart
Future<void> addReplyToFirstAnnotation(
  CPDFReaderWidgetController controller,
) async {
  final int pageIndex = await controller.getCurrentPageIndex();
  final CPDFPage page = controller.document.pageAtIndex(pageIndex);
  final List<CPDFAnnotation> annotations = await page.getAnnotations();

  if (annotations.isEmpty) {
    return;
  }

  await controller.document.addAnnotationReply(
    annotations.first,
    content: 'Reviewed in the reader.',
    title: 'Reviewer',
  );
}

This is useful for custom review panels, annotation lists, and collaboration toolbars.

Complete Example

The following example opens a document, adds a reply to the first annotation on the first page, updates its review state, and reads the reply list again.

dart
import 'package:compdfkit_flutter/annotation/cpdf_annotation.dart';
import 'package:compdfkit_flutter/annotation/cpdf_annotation_reply.dart';
import 'package:compdfkit_flutter/annotation/cpdf_annotation_state.dart';
import 'package:compdfkit_flutter/document/cpdf_document.dart';
import 'package:compdfkit_flutter/page/cpdf_page.dart';

Future<void> reviewFirstAnnotation(String filePath) async {
  final CPDFDocument document = await CPDFDocument.createInstance();
  await document.open(filePath);

  final CPDFPage page = document.pageAtIndex(0);
  final List<CPDFAnnotation> annotations = await page.getAnnotations();

  if (annotations.isEmpty) {
    return;
  }

  final CPDFAnnotation annotation = annotations.first;

  final CPDFReplyAnnotation? reply = await document.addAnnotationReply(
    annotation,
    content: 'Please verify this annotation.',
    title: 'Reviewer',
  );

  if (reply == null) {
    throw Exception('Add annotation reply failed.');
  }

  await document.setAnnotationReviewState(
    reply,
    CPDFAnnotationReviewState.completed,
  );

  final List<CPDFReplyAnnotation> replies =
      await document.getAnnotationReplies(annotation);

  print('Reply count: ${replies.length}');
}

Notes

  • Use CPDFReplyAnnotation objects returned by the SDK when updating or removing replies.
  • Do not rely on internal annotation identifiers in application logic. Pass the annotation or reply object returned by the SDK back to the related API.
  • getAnnotationReplies returns plain replies only. State replies are handled internally.
  • removeAllAnnotationReplies removes plain replies and preserves mark/review state information.
  • After setting mark or review state, read the annotation or reply again if your UI needs the latest model values.
  • Annotation and reply APIs use zero-based page indexes.
  • Save the document after making changes if you need to persist them to disk.