PDF Generation Template Editor
Open-source visual PDF generation engine with customizable templates and developer-friendly APIs.
Open-source visual PDF generation engine with customizable templates and developer-friendly APIs.
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.
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 |
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 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,
);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.
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.
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.
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.
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.');
}
}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.
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.
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}');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.
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}');
}CPDFReplyAnnotation objects returned by the SDK when updating or removing replies.getAnnotationReplies returns plain replies only. State replies are handled internally.removeAllAnnotationReplies removes plain replies and preserves mark/review state information.