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:
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:
| API | Description |
|---|---|
addAnnotationReply | Adds a plain reply to an annotation |
getAnnotationReplies | Gets all plain replies attached to an annotation |
updateAnnotationReply | Updates the content and optional title of a plain reply |
removeAnnotationReply | Removes one plain reply |
removeAllAnnotationReplies | Removes all plain replies from an annotation |
setAnnotationMarkState | Sets the mark state of an annotation or reply |
getAnnotationMarkState | Gets the mark state of an annotation or reply |
setAnnotationReviewState | Sets the review state of an annotation or reply |
getAnnotationReviewState | Gets the review state of an annotation or reply |
Reply Model
Replies are represented by CPDFReplyAnnotation.
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:
| Property | Description |
|---|---|
page | Zero-based page index |
title | Reply author or title |
content | Reply content |
createDate | Reply creation date, if available |
modifyDate | Reply modification date, if available |
markState | Current mark state |
reviewState | Current 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:
enum CPDFAnnotationMarkState {
marked,
unmarked,
}Review states:
enum CPDFAnnotationReviewState {
accepted,
rejected,
cancelled,
completed,
none,
error,
}Mark and review states can be applied to either a parent annotation or a reply annotation:
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
CPDFReplyAnnotationobjects 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.
getAnnotationRepliesreturns plain replies only. State replies are handled internally.removeAllAnnotationRepliesremoves 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.