diff --git a/AGENTS.md b/AGENTS.md index a7e41e9..1b6a118 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,12 +1,12 @@ # AGENTS -Always read `README.md` and `docs/meta-arch.md` when new chat created. +Always read [`README.md`](README.md) and [`meta-arch.md`](docs/meta-arch.md) when new chat created. Additionally read relevant files depends on task. * If want to modify use cases (files at `test/features/*.feature`) - * read `docs/FRs.md` + * read [`FRs.md`](docs/FRs.md) * If want to modify code (implement or test) of `View` of MVVM (UI widget) (files at `lib/ui/features/*/widgets/*`) - * read `docs/wireframe.md`, `docs/NFRs.md`, `test/features/*.feature` + * read [`wireframe.md`](docs/wireframe.md), [`NFRs.md`](docs/NFRs.md), `test/features/*.feature` * If want to modify code (implement or test) of non-View e.g. `Model`, `View Model`, services... - * read `test/features/*.feature`, `docs/NFRs.md` + * read `test/features/*.feature`, [`NFRs.md`](docs/NFRs.md) diff --git a/integration_test/export_flow_test.dart b/integration_test/export_flow_test.dart index 3896904..00a5d8d 100644 --- a/integration_test/export_flow_test.dart +++ b/integration_test/export_flow_test.dart @@ -126,7 +126,7 @@ void main() { final pdf = container.read(pdfProvider); container .read(pdfProvider.notifier) - .addPlacement(page: pdf.currentPage, rect: r, image: imageId); + .addPlacement(page: pdf.currentPage, rect: r, imageId: imageId); container.read(signatureProvider.notifier).clearActiveOverlay(); await tester.pumpAndSettle(); diff --git a/lib/data/model/model.dart b/lib/data/model/model.dart index 256bc2b..df11209 100644 --- a/lib/data/model/model.dart +++ b/lib/data/model/model.dart @@ -1,6 +1,35 @@ import 'dart:typed_data'; import 'package:flutter/widgets.dart'; +/// Represents a single signature placement on a page combining both the +/// geometric rectangle (UI coordinate space) and the identifier of the +/// image/signature asset assigned to that placement. +class SignaturePlacement { + final Rect rect; + + /// Rotation in degrees to apply when rendering/exporting this placement. + final double rotationDeg; + + /// Identifier of the image (e.g., filename / asset id) assigned to this placement. + /// Nullable to allow a placement reserved before an image is chosen. + final String? imageId; + const SignaturePlacement({ + required this.rect, + this.imageId, + this.rotationDeg = 0.0, + }); + + SignaturePlacement copyWith({ + Rect? rect, + String? imageId, + double? rotationDeg, + }) => SignaturePlacement( + rect: rect ?? this.rect, + imageId: imageId ?? this.imageId, + rotationDeg: rotationDeg ?? this.rotationDeg, + ); +} + class PdfState { final bool loaded; final int pageCount; @@ -8,10 +37,8 @@ class PdfState { final String? pickedPdfPath; final Uint8List? pickedPdfBytes; final int? signedPage; - // Multiple signature placements per page, stored as UI-space rects (e.g., 400x560) - final Map> placementsByPage; - // For each placement, store the assigned image identifier (e.g., filename) in the same index order. - final Map> placementImageByPage; + // Multiple signature placements per page, each combines geometry and optional image id. + final Map> placementsByPage; // UI state: selected placement index on the current page (if any) final int? selectedPlacementIndex; const PdfState({ @@ -22,7 +49,6 @@ class PdfState { this.pickedPdfBytes, this.signedPage, this.placementsByPage = const {}, - this.placementImageByPage = const {}, this.selectedPlacementIndex, }); factory PdfState.initial() => const PdfState( @@ -32,7 +58,6 @@ class PdfState { pickedPdfBytes: null, signedPage: null, placementsByPage: {}, - placementImageByPage: {}, selectedPlacementIndex: null, ); PdfState copyWith({ @@ -42,8 +67,7 @@ class PdfState { String? pickedPdfPath, Uint8List? pickedPdfBytes, int? signedPage, - Map>? placementsByPage, - Map>? placementImageByPage, + Map>? placementsByPage, int? selectedPlacementIndex, }) => PdfState( loaded: loaded ?? this.loaded, @@ -53,7 +77,6 @@ class PdfState { pickedPdfBytes: pickedPdfBytes ?? this.pickedPdfBytes, signedPage: signedPage ?? this.signedPage, placementsByPage: placementsByPage ?? this.placementsByPage, - placementImageByPage: placementImageByPage ?? this.placementImageByPage, selectedPlacementIndex: selectedPlacementIndex ?? this.selectedPlacementIndex, ); diff --git a/lib/data/services/export_service.dart b/lib/data/services/export_service.dart index 9370300..0ace6a8 100644 --- a/lib/data/services/export_service.dart +++ b/lib/data/services/export_service.dart @@ -6,6 +6,7 @@ import 'package:pdf/widgets.dart' as pw; import 'package:pdf/pdf.dart' as pdf; import 'package:printing/printing.dart' as printing; import 'package:image/image.dart' as img; +import '../model/model.dart'; // NOTE: // - This exporter uses a raster snapshot of the UI (RepaintBoundary) and embeds it into a new PDF. @@ -32,8 +33,7 @@ class ExportService { required Rect? signatureRectUi, required Size uiPageSize, required Uint8List? signatureImageBytes, - Map>? placementsByPage, - Map>? placementImageByPage, + Map>? placementsByPage, Map? libraryBytes, double targetDpi = 144.0, }) async { @@ -55,7 +55,6 @@ class ExportService { uiPageSize: uiPageSize, signatureImageBytes: signatureImageBytes, placementsByPage: placementsByPage, - placementImageByPage: placementImageByPage, libraryBytes: libraryBytes, targetDpi: targetDpi, ); @@ -76,8 +75,7 @@ class ExportService { required Rect? signatureRectUi, required Size uiPageSize, required Uint8List? signatureImageBytes, - Map>? placementsByPage, - Map>? placementImageByPage, + Map>? placementsByPage, Map? libraryBytes, double targetDpi = 144.0, }) async { @@ -104,12 +102,8 @@ class ExportService { (placementsByPage != null && placementsByPage.isNotEmpty); final pagePlacements = hasMulti - ? (placementsByPage[pageIndex] ?? const []) - : const []; - final pageImageIds = - hasMulti - ? (placementImageByPage?[pageIndex] ?? const []) - : const []; + ? (placementsByPage[pageIndex] ?? const []) + : const []; final shouldStampSingle = !hasMulti && signedPage != null && @@ -147,18 +141,18 @@ class ExportService { // Multi-placement stamping: per-placement image from libraryBytes if (hasMulti && pagePlacements.isNotEmpty) { for (var i = 0; i < pagePlacements.length; i++) { - final r = pagePlacements[i]; + final placement = pagePlacements[i]; + final r = placement.rect; final left = r.left / uiPageSize.width * widthPts; final top = r.top / uiPageSize.height * heightPts; final w = r.width / uiPageSize.width * widthPts; final h = r.height / uiPageSize.height * heightPts; Uint8List? bytes; - if (i < pageImageIds.length) { - final id = pageImageIds[i]; + final id = placement.imageId; + if (id != null) { bytes = libraryBytes?[id]; } - bytes ??= - signatureImageBytes; // fallback to single image if provided + bytes ??= signatureImageBytes; // fallback if (bytes != null && bytes.isNotEmpty) { pw.MemoryImage? imgObj; try { @@ -176,7 +170,13 @@ class ExportService { height: h, child: pw.FittedBox( fit: pw.BoxFit.contain, - child: pw.Image(imgObj), + child: pw.Transform.rotate( + angle: + placement.rotationDeg * + 3.1415926535 / + 180.0, + child: pw.Image(imgObj), + ), ), ), ), @@ -222,11 +222,9 @@ class ExportService { final hasMulti = (placementsByPage != null && placementsByPage.isNotEmpty); final pagePlacements = - hasMulti ? (placementsByPage[1] ?? const []) : const []; - final pageImageIds = hasMulti - ? (placementImageByPage?[1] ?? const []) - : const []; + ? (placementsByPage[1] ?? const []) + : const []; final shouldStampSingle = !hasMulti && signedPage != null && @@ -270,18 +268,18 @@ class ExportService { // Multi-placement stamping on fallback page if (hasMulti && pagePlacements.isNotEmpty) { for (var i = 0; i < pagePlacements.length; i++) { - final r = pagePlacements[i]; + final placement = pagePlacements[i]; + final r = placement.rect; final left = r.left / uiPageSize.width * widthPts; final top = r.top / uiPageSize.height * heightPts; final w = r.width / uiPageSize.width * widthPts; final h = r.height / uiPageSize.height * heightPts; Uint8List? bytes; - if (i < pageImageIds.length) { - final id = pageImageIds[i]; + final id = placement.imageId; + if (id != null) { bytes = libraryBytes?[id]; } - bytes ??= - signatureImageBytes; // fallback to single image if provided + bytes ??= signatureImageBytes; // fallback if (bytes != null && bytes.isNotEmpty) { pw.MemoryImage? imgObj; try { @@ -313,7 +311,11 @@ class ExportService { height: h, child: pw.FittedBox( fit: pw.BoxFit.contain, - child: pw.Image(imgObj), + child: pw.Transform.rotate( + angle: + placement.rotationDeg * 3.1415926535 / 180.0, + child: pw.Image(imgObj), + ), ), ), ), diff --git a/lib/ui/features/pdf/view_model/pdf_controller.dart b/lib/ui/features/pdf/view_model/pdf_controller.dart index 5fa3f53..ece2a5d 100644 --- a/lib/ui/features/pdf/view_model/pdf_controller.dart +++ b/lib/ui/features/pdf/view_model/pdf_controller.dart @@ -17,7 +17,6 @@ class PdfController extends StateNotifier { pickedPdfPath: null, signedPage: null, placementsByPage: {}, - placementImageByPage: {}, selectedPlacementIndex: null, ); } @@ -35,7 +34,6 @@ class PdfController extends StateNotifier { pickedPdfBytes: bytes, signedPage: null, placementsByPage: {}, - placementImageByPage: {}, selectedPlacementIndex: null, ); } @@ -67,49 +65,54 @@ class PdfController extends StateNotifier { void addPlacement({ required int page, required Rect rect, - String image = 'default.png', + String? imageId = 'default.png', + double rotationDeg = 0.0, }) { if (!state.loaded) return; final p = page.clamp(1, state.pageCount); - final map = Map>.from(state.placementsByPage); - final list = List.from(map[p] ?? const []); - list.add(rect); - map[p] = list; - // Sync image mapping list - final imgMap = Map>.from(state.placementImageByPage); - final imgList = List.from(imgMap[p] ?? const []); - imgList.add(image); - imgMap[p] = imgList; - state = state.copyWith( - placementsByPage: map, - placementImageByPage: imgMap, - selectedPlacementIndex: null, + final map = Map>.from(state.placementsByPage); + final list = List.from(map[p] ?? const []); + list.add( + SignaturePlacement( + rect: rect, + imageId: imageId, + rotationDeg: rotationDeg, + ), ); + map[p] = list; + state = state.copyWith(placementsByPage: map, selectedPlacementIndex: null); + } + + void updatePlacementRotation({ + required int page, + required int index, + required double rotationDeg, + }) { + if (!state.loaded) return; + final p = page.clamp(1, state.pageCount); + final map = Map>.from(state.placementsByPage); + final list = List.from(map[p] ?? const []); + if (index >= 0 && index < list.length) { + list[index] = list[index].copyWith(rotationDeg: rotationDeg); + map[p] = list; + state = state.copyWith(placementsByPage: map); + } } void removePlacement({required int page, required int index}) { if (!state.loaded) return; final p = page.clamp(1, state.pageCount); - final map = Map>.from(state.placementsByPage); - final list = List.from(map[p] ?? const []); + final map = Map>.from(state.placementsByPage); + final list = List.from(map[p] ?? const []); if (index >= 0 && index < list.length) { list.removeAt(index); - // Sync image mapping - final imgMap = Map>.from(state.placementImageByPage); - final imgList = List.from(imgMap[p] ?? const []); - if (index >= 0 && index < imgList.length) { - imgList.removeAt(index); - } if (list.isEmpty) { map.remove(p); - imgMap.remove(p); } else { map[p] = list; - imgMap[p] = imgList; } state = state.copyWith( placementsByPage: map, - placementImageByPage: imgMap, selectedPlacementIndex: null, ); } @@ -123,17 +126,20 @@ class PdfController extends StateNotifier { }) { if (!state.loaded) return; final p = page.clamp(1, state.pageCount); - final map = Map>.from(state.placementsByPage); - final list = List.from(map[p] ?? const []); + final map = Map>.from(state.placementsByPage); + final list = List.from(map[p] ?? const []); if (index >= 0 && index < list.length) { - list[index] = rect; + final existing = list[index]; + list[index] = existing.copyWith(rect: rect); map[p] = list; state = state.copyWith(placementsByPage: map); } } - List placementsOn(int page) { - return List.from(state.placementsByPage[page] ?? const []); + List placementsOn(int page) { + return List.from( + state.placementsByPage[page] ?? const [], + ); } void selectPlacement(int? index) { @@ -161,9 +167,9 @@ class PdfController extends StateNotifier { // Convenience to get image name for a placement String? imageOfPlacement({required int page, required int index}) { - final list = state.placementImageByPage[page] ?? const []; + final list = state.placementsByPage[page] ?? const []; if (index < 0 || index >= list.length) return null; - return list[index]; + return list[index].imageId; } } diff --git a/lib/ui/features/pdf/widgets/pdf_page_overlays.dart b/lib/ui/features/pdf/widgets/pdf_page_overlays.dart index 16587ff..c68e188 100644 --- a/lib/ui/features/pdf/widgets/pdf_page_overlays.dart +++ b/lib/ui/features/pdf/widgets/pdf_page_overlays.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../signature/view_model/signature_controller.dart'; +import '../../../../data/model/model.dart'; import '../view_model/pdf_controller.dart'; import 'signature_overlay.dart'; @@ -30,12 +31,13 @@ class PdfPageOverlays extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final pdf = ref.watch(pdfProvider); final sig = ref.watch(signatureProvider); - final placed = pdf.placementsByPage[pageNumber] ?? const []; + final placed = + pdf.placementsByPage[pageNumber] ?? const []; final widgets = []; for (int i = 0; i < placed.length; i++) { // Stored as UI-space rects (SignatureController.pageSize). - final uiRect = placed[i]; + final uiRect = placed[i].rect; widgets.add( SignatureOverlay( pageSize: pageSize, diff --git a/lib/ui/features/pdf/widgets/pdf_screen.dart b/lib/ui/features/pdf/widgets/pdf_screen.dart index e316ba2..2e62436 100644 --- a/lib/ui/features/pdf/widgets/pdf_screen.dart +++ b/lib/ui/features/pdf/widgets/pdf_screen.dart @@ -174,7 +174,6 @@ class _PdfSignatureHomePageState extends ConsumerState { uiPageSize: SignatureController.pageSize, signatureImageBytes: rotated, placementsByPage: pdf.placementsByPage, - placementImageByPage: pdf.placementImageByPage, libraryBytes: { for (final a in ref.read(signatureLibraryProvider)) a.id: a.bytes, }, @@ -211,7 +210,6 @@ class _PdfSignatureHomePageState extends ConsumerState { uiPageSize: SignatureController.pageSize, signatureImageBytes: rotated, placementsByPage: pdf.placementsByPage, - placementImageByPage: pdf.placementImageByPage, libraryBytes: { for (final a in ref.read(signatureLibraryProvider)) a.id: a.bytes, }, @@ -242,7 +240,6 @@ class _PdfSignatureHomePageState extends ConsumerState { uiPageSize: SignatureController.pageSize, signatureImageBytes: rotated, placementsByPage: pdf.placementsByPage, - placementImageByPage: pdf.placementImageByPage, libraryBytes: { for (final a in ref.read(signatureLibraryProvider)) a.id: a.bytes, diff --git a/lib/ui/features/pdf/widgets/signature_overlay.dart b/lib/ui/features/pdf/widgets/signature_overlay.dart index 81363af..3c82e76 100644 --- a/lib/ui/features/pdf/widgets/signature_overlay.dart +++ b/lib/ui/features/pdf/widgets/signature_overlay.dart @@ -253,11 +253,12 @@ class _SignatureImage extends ConsumerWidget { final processed = ref.watch(processedSignatureImageProvider); bytes = processed ?? sig.imageBytes; } else if (placedIndex != null) { - // Use the image assigned to this placement - final imgId = ref - .read(pdfProvider) - .placementImageByPage[pageNumber] - ?.elementAt(placedIndex!); + final placementList = ref.read(pdfProvider).placementsByPage[pageNumber]; + final placement = + (placementList != null && placedIndex! < placementList.length) + ? placementList[placedIndex!] + : null; + final imgId = placement?.imageId; if (imgId != null) { final lib = ref.watch(signatureLibraryProvider); for (final a in lib) { @@ -267,7 +268,6 @@ class _SignatureImage extends ConsumerWidget { } } } - // Fallback to current processed bytes ??= ref.read(processedSignatureImageProvider) ?? sig.imageBytes; } @@ -281,9 +281,19 @@ class _SignatureImage extends ConsumerWidget { return Center(child: Text(label)); } + // Use live rotation for interactive overlay; stored rotation for placed + double rotationDeg = 0.0; + if (interactive) { + rotationDeg = sig.rotation; + } else if (placedIndex != null) { + final placementList = ref.read(pdfProvider).placementsByPage[pageNumber]; + if (placementList != null && placedIndex! < placementList.length) { + rotationDeg = placementList[placedIndex!].rotationDeg; + } + } return RotatedSignatureImage( bytes: bytes, - rotationDeg: interactive ? sig.rotation : 0.0, + rotationDeg: rotationDeg, enableAngleAwareScale: interactive, fit: BoxFit.contain, wrapInRepaintBoundary: true, diff --git a/lib/ui/features/signature/view_model/signature_controller.dart b/lib/ui/features/signature/view_model/signature_controller.dart index b6c5997..4454c0f 100644 --- a/lib/ui/features/signature/view_model/signature_controller.dart +++ b/lib/ui/features/signature/view_model/signature_controller.dart @@ -21,17 +21,13 @@ class SignatureController extends StateNotifier { @visibleForTesting void placeDefaultRect() { final w = 120.0, h = 60.0; - state = state.copyWith( - rect: Rect.fromCenter( - center: Offset( - (pageSize.width / 2) * (Random().nextDouble() * 1.5 + 1), - (pageSize.height / 2) * (Random().nextDouble() * 1.5 + 1), - ), - width: w, - height: h, - ), - editingEnabled: true, - ); + final rand = Random(); + // Generate a center within 10%..90% of each axis to reduce off-screen risk + final cx = pageSize.width * (0.1 + rand.nextDouble() * 0.8); + final cy = pageSize.height * (0.1 + rand.nextDouble() * 0.8); + Rect r = Rect.fromCenter(center: Offset(cx, cy), width: w, height: h); + r = _clampRectToPage(r); + state = state.copyWith(rect: r, editingEnabled: true); } void loadSample() { @@ -181,37 +177,20 @@ class SignatureController extends StateNotifier { if (!pdf.loaded) return null; // Bind the processed image at placement time (so placed preview matches adjustments). // If processed bytes exist, always create a new asset for this placement. - String id = ''; - // Compose final bytes for placement: apply adjustments (processed) then rotation. - Uint8List? srcBytes = ref.read(processedSignatureImageProvider); - srcBytes ??= state.imageBytes; - // If still null, fall back to asset reference only. - if (srcBytes != null && srcBytes.isNotEmpty) { - final rot = state.rotation % 360; - Uint8List finalBytes = srcBytes; - if (rot != 0) { - try { - final decoded = img.decodeImage(srcBytes); - if (decoded != null) { - var out = img.copyRotate( - decoded, - angle: rot, - interpolation: img.Interpolation.linear, - ); - finalBytes = Uint8List.fromList(img.encodePng(out, level: 6)); - } - } catch (_) {} - } - id = ref - .read(signatureLibraryProvider.notifier) - .add(finalBytes, name: 'image'); - } else { - id = state.assetId ?? 'default.png'; - } + // Prefer reusing an existing library asset id when the active overlay is + // based on a library item. If there is no library asset, do NOT create + // a new library card here — keep the placement's image id empty so the + // UI and exporter will fall back to using the processed/current bytes. + String id = state.assetId ?? ''; // Store as UI-space rect (consistent with export and rendering paths) ref .read(pdfProvider.notifier) - .addPlacement(page: pdf.currentPage, rect: r, image: id); + .addPlacement( + page: pdf.currentPage, + rect: r, + imageId: id, + rotationDeg: state.rotation, + ); // Newly placed index is the last one on the page final idx = (ref.read(pdfProvider).placementsByPage[pdf.currentPage]?.length ?? 1) - @@ -227,39 +206,23 @@ class SignatureController extends StateNotifier { // Test/helper variant: confirm using a ProviderContainer instead of WidgetRef. // Useful in widget tests where obtaining a WidgetRef is not straightforward. + @visibleForTesting Rect? confirmCurrentSignatureWithContainer(ProviderContainer container) { final r = state.rect; if (r == null) return null; final pdf = container.read(pdfProvider); if (!pdf.loaded) return null; - String id = ''; - Uint8List? srcBytes = container.read(processedSignatureImageProvider); - srcBytes ??= state.imageBytes; - if (srcBytes != null && srcBytes.isNotEmpty) { - final rot = state.rotation % 360; - Uint8List finalBytes = srcBytes; - if (rot != 0) { - try { - final decoded = img.decodeImage(srcBytes); - if (decoded != null) { - var out = img.copyRotate( - decoded, - angle: rot, - interpolation: img.Interpolation.linear, - ); - finalBytes = Uint8List.fromList(img.encodePng(out, level: 6)); - } - } catch (_) {} - } - id = container - .read(signatureLibraryProvider.notifier) - .add(finalBytes, name: 'image'); - } else { - id = state.assetId ?? 'default.png'; - } + // Reuse existing library id if present; otherwise leave empty so the + // placement will reference the current bytes via fallback paths. + String id = state.assetId ?? ''; container .read(pdfProvider.notifier) - .addPlacement(page: pdf.currentPage, rect: r, image: id); + .addPlacement( + page: pdf.currentPage, + rect: r, + imageId: id, + rotationDeg: state.rotation, + ); final idx = (container .read(pdfProvider) diff --git a/test/features/step/a_signature_is_placed_on_page.dart b/test/features/step/a_signature_is_placed_on_page.dart index b919cae..7f7b0bb 100644 --- a/test/features/step/a_signature_is_placed_on_page.dart +++ b/test/features/step/a_signature_is_placed_on_page.dart @@ -21,5 +21,5 @@ Future aSignatureIsPlacedOnPage(WidgetTester tester, num page) async { final Rect r = container.read(signatureProvider).rect!; container .read(pdfProvider.notifier) - .addPlacement(page: page.toInt(), rect: r, image: 'default.png'); + .addPlacement(page: page.toInt(), rect: r, imageId: 'default.png'); } diff --git a/test/features/step/adjusting_one_instance_does_not_affect_the_others.dart b/test/features/step/adjusting_one_instance_does_not_affect_the_others.dart index 7edfe7c..0171449 100644 --- a/test/features/step/adjusting_one_instance_does_not_affect_the_others.dart +++ b/test/features/step/adjusting_one_instance_does_not_affect_the_others.dart @@ -10,9 +10,11 @@ Future adjustingOneInstanceDoesNotAffectTheOthers( final container = TestWorld.container ?? ProviderContainer(); final before = container.read(pdfProvider.notifier).placementsOn(2); expect(before.length, greaterThanOrEqualTo(2)); - final modified = before[0].translate(5, 0).inflate(3); + final modified = before[0].rect.translate(5, 0).inflate(3); container.read(pdfProvider.notifier).removePlacement(page: 2, index: 0); - container.read(pdfProvider.notifier).addPlacement(page: 2, rect: modified); + container + .read(pdfProvider.notifier) + .addPlacement(page: 2, rect: modified, imageId: before[0].imageId); final after = container.read(pdfProvider.notifier).placementsOn(2); - expect(after.any((r) => r == before[1]), isTrue); + expect(after.any((p) => p.rect == before[1].rect), isTrue); } diff --git a/test/features/step/dragging_or_resizing_one_does_not_change_the_other.dart b/test/features/step/dragging_or_resizing_one_does_not_change_the_other.dart index 0cd019a..3285d78 100644 --- a/test/features/step/dragging_or_resizing_one_does_not_change_the_other.dart +++ b/test/features/step/dragging_or_resizing_one_does_not_change_the_other.dart @@ -11,11 +11,18 @@ Future draggingOrResizingOneDoesNotChangeTheOther( final container = TestWorld.container ?? ProviderContainer(); final list = container.read(pdfProvider.notifier).placementsOn(1); expect(list.length, greaterThanOrEqualTo(2)); - final before = List.from(list.take(2)); + final before = List.from(list.take(2).map((p) => p.rect)); // Simulate changing the first only final changed = before[0].inflate(5); container.read(pdfProvider.notifier).removePlacement(page: 1, index: 0); - container.read(pdfProvider.notifier).addPlacement(page: 1, rect: changed); + container + .read(pdfProvider.notifier) + .addPlacement( + page: 1, + rect: changed, + imageId: list[1].imageId, + rotationDeg: list[1].rotationDeg, + ); final after = container.read(pdfProvider.notifier).placementsOn(1); - expect(after.any((r) => r == before[1]), isTrue); + expect(after.any((p) => p.rect == before[1]), isTrue); } diff --git a/test/features/step/each_signature_can_be_dragged_and_resized_independently.dart b/test/features/step/each_signature_can_be_dragged_and_resized_independently.dart index ddd9d4a..882d2c6 100644 --- a/test/features/step/each_signature_can_be_dragged_and_resized_independently.dart +++ b/test/features/step/each_signature_can_be_dragged_and_resized_independently.dart @@ -11,9 +11,9 @@ Future eachSignatureCanBeDraggedAndResizedIndependently( final list = container.read(pdfProvider.notifier).placementsOn(1); expect(list.length, greaterThanOrEqualTo(2)); // Independence is modeled by distinct rects; ensure not equal and both within page - expect(list[0], isNot(equals(list[1]))); - for (final r in list.take(2)) { - expect(r.left, greaterThanOrEqualTo(0)); - expect(r.top, greaterThanOrEqualTo(0)); + expect(list[0].rect, isNot(equals(list[1].rect))); + for (final p in list.take(2)) { + expect(p.rect.left, greaterThanOrEqualTo(0)); + expect(p.rect.top, greaterThanOrEqualTo(0)); } } diff --git a/test/features/step/the_user_navigates_to_page_and_places_another_signature.dart b/test/features/step/the_user_navigates_to_page_and_places_another_signature.dart index a846815..6a9eefa 100644 --- a/test/features/step/the_user_navigates_to_page_and_places_another_signature.dart +++ b/test/features/step/the_user_navigates_to_page_and_places_another_signature.dart @@ -31,5 +31,5 @@ Future theUserNavigatesToPageAndPlacesAnotherSignature( final Rect r = container.read(signatureProvider).rect!; container .read(pdfProvider.notifier) - .addPlacement(page: page.toInt(), rect: r, image: 'default.png'); + .addPlacement(page: page.toInt(), rect: r, imageId: 'default.png'); } diff --git a/test/features/step/the_user_places_a_signature_from_picture_on_page.dart b/test/features/step/the_user_places_a_signature_from_picture_on_page.dart index df4e749..a43ff9d 100644 --- a/test/features/step/the_user_places_a_signature_from_picture_on_page.dart +++ b/test/features/step/the_user_places_a_signature_from_picture_on_page.dart @@ -54,5 +54,5 @@ Future theUserPlacesASignatureFromPictureOnPage( ((TestWorld.placeFromPictureCallCount <= 1) ? 1 : 3); container .read(pdfProvider.notifier) - .addPlacement(page: page, rect: r, image: name); + .addPlacement(page: page, rect: r, imageId: name); } diff --git a/test/features/step/the_user_places_a_signature_on_page.dart b/test/features/step/the_user_places_a_signature_on_page.dart index 576326a..b980692 100644 --- a/test/features/step/the_user_places_a_signature_on_page.dart +++ b/test/features/step/the_user_places_a_signature_on_page.dart @@ -30,5 +30,5 @@ Future theUserPlacesASignatureOnPage( final Rect r = container.read(signatureProvider).rect!; container .read(pdfProvider.notifier) - .addPlacement(page: page.toInt(), rect: r, image: 'default.png'); + .addPlacement(page: page.toInt(), rect: r, imageId: 'default.png'); } diff --git a/test/widget/regression_signature_tests.dart b/test/widget/regression_signature_tests.dart index 5509cbe..30f33a8 100644 --- a/test/widget/regression_signature_tests.dart +++ b/test/widget/regression_signature_tests.dart @@ -119,7 +119,7 @@ void main() { final processed = container3.read(processedSignatureImageProvider); expect(processed, isNotNull); final pdf = container3.read(pdfProvider); - final imgId = pdf.placementImageByPage[pdf.currentPage]?.first; + final imgId = pdf.placementsByPage[pdf.currentPage]?.first.imageId; expect(imgId, isNotNull); final lib = container3.read(signatureLibraryProvider); final match = lib.firstWhere((a) => a.id == imgId);