feat: move `model.dart` to `/lib/domain/models/`

This commit is contained in:
insleker 2025-09-10 13:17:31 +08:00
parent c1b7824cbd
commit 948999fe8e
102 changed files with 692 additions and 642 deletions

View File

@ -7,7 +7,52 @@
## Package structure
The repo structure follows official [Package structure](https://docs.flutter.dev/app-architecture/case-study#package-structure) with slight modifications.
The repo structure follows official [Package structure](https://docs.flutter.dev/app-architecture/case-study#package-structure).
```
lib
├─┬─ ui
│ ├─┬─ core
│ │ ├─┬─ ui
│ │ │ └─── <shared widgets>
│ │ └─── themes
│ └─┬─ <FEATURE NAME>
│ ├─┬─ view_model
│ │ └─── <view_model class>.dart
│ └─┬─ widgets
│ ├── <feature name>_screen.dart
│ └── <other widgets>
├─┬─ domain
│ └─┬─ models
│ └─── <model name>.dart
├─┬─ data
│ ├─┬─ repositories
│ │ └─── <repository class>.dart
│ ├─┬─ services
│ │ └─── <service class>.dart
│ └─┬─ model
│ └─── <api model class>.dart
├─── config
├─── utils
├─── routing
├─── main_staging.dart
├─── main_development.dart
└─── main.dart
// The test folder contains unit and widget tests
test
├─── data
├─── domain
├─── ui
└─── utils
// The testing folder contains mocks other classes need to execute tests
testing
├─── fakes
└─── models
```
But with slight modifications.
* put each `<FEATURE NAME>/`s in `features/` sub-directory under `ui/`.
* `test/features/` contains BDD unit tests for each feature. It focuses on pure logic, therefore will not access `View` but `ViewModel` and `Model`.
@ -21,15 +66,15 @@ Some rule of thumb:
### terminology
* signature asset
* `signature asset`
* image file of a signature, stored in the device or cloud storage
* can drawing from canvas
* signature card
* `signature card`
* template of signature placement
* It will include modifications such as brightness, contrast, background removal, rotation of the signature asset.
* signature placement
* `signature placement`
* placed modified signature asset from signature card on a specific position on a specific page of a specific PDF document
* document
* `document`
* PDF document to be signed
## key dependencies

View File

@ -7,7 +7,7 @@ import 'package:image/image.dart' as img;
import 'package:pdf_signature/data/services/export_service.dart';
import 'package:pdf_signature/data/services/export_providers.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'package:pdf_signature/data/repositories/signature_repository.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
@ -32,8 +32,8 @@ void main() {
await tester.pumpWidget(
ProviderScope(
overrides: [
pdfProvider.overrideWith(
(ref) => PdfController()..openPicked(path: 'test.pdf'),
documentRepositoryProvider.overrideWith(
(ref) => DocumentStateNotifier()..openPicked(path: 'test.pdf'),
),
signatureProvider.overrideWith(
(ref) => SignatureController()..placeDefaultRect(),
@ -85,11 +85,11 @@ void main() {
await tester.pumpWidget(
ProviderScope(
overrides: [
pdfProvider.overrideWith(
(ref) => PdfController()..openPicked(path: 'test.pdf'),
documentRepositoryProvider.overrideWith(
(ref) => DocumentStateNotifier()..openPicked(path: 'test.pdf'),
),
signatureLibraryProvider.overrideWith((ref) {
final c = SignatureLibraryController();
signatureAssetRepositoryProvider.overrideWith((ref) {
final c = SignatureAssetRepository();
c.add(sigBytes, name: 'image');
return c;
}),
@ -121,11 +121,11 @@ void main() {
final container = ProviderScope.containerOf(ctx);
final sigState = container.read(signatureProvider);
final r = sigState.rect!;
final lib = container.read(signatureLibraryProvider);
final lib = container.read(signatureAssetRepositoryProvider);
final asset = lib.isNotEmpty ? lib.first : null;
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(page: pdf.currentPage, rect: r, asset: asset);
container.read(signatureProvider.notifier).clearActiveOverlay();
await tester.pumpAndSettle();

View File

@ -98,7 +98,7 @@ class _RootHomeSwitcher extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final pdf = ref.watch(pdfProvider);
final pdf = ref.watch(documentRepositoryProvider);
if (!pdf.loaded) {
return const WelcomeScreen();
}

View File

@ -1,206 +0,0 @@
import 'dart:typed_data';
import 'package:flutter/widgets.dart';
/// A simple library of signature images available to the user in the sidebar.
class SignatureAsset {
final String id; // unique id
final Uint8List bytes;
// List<List<Offset>>? strokes;
final String? name; // optional display name (e.g., filename)
const SignatureAsset({required this.id, required this.bytes, this.name});
}
class GraphicAdjust {
final double contrast;
final double brightness;
final bool bgRemoval;
const GraphicAdjust({
this.contrast = 1.0,
this.brightness = 0.0,
this.bgRemoval = false,
});
GraphicAdjust copyWith({
double? contrast,
double? brightness,
bool? bgRemoval,
}) => GraphicAdjust(
contrast: contrast ?? this.contrast,
brightness: brightness ?? this.brightness,
bgRemoval: bgRemoval ?? this.bgRemoval,
);
}
/**
* signature card is template of signature placement
*/
class SignatureCard {
final double rotationDeg;
final SignatureAsset asset;
final GraphicAdjust graphicAdjust;
const SignatureCard({
required this.rotationDeg,
required this.asset,
this.graphicAdjust = const GraphicAdjust(),
});
SignatureCard copyWith({
double? rotationDeg,
SignatureAsset? asset,
GraphicAdjust? graphicAdjust,
}) => SignatureCard(
rotationDeg: rotationDeg ?? this.rotationDeg,
asset: asset ?? this.asset,
graphicAdjust: graphicAdjust ?? this.graphicAdjust,
);
}
/// Represents a single signature placement on a page combining both the
/// geometric rectangle (UI coordinate space) and the signature asset
/// assigned to that placement.
class SignaturePlacement {
// The bounding box of this placement in UI coordinate space, implies scaling and position.
final Rect rect;
/// Rotation in degrees to apply when rendering/exporting this placement.
final double rotationDeg;
final GraphicAdjust graphicAdjust;
final SignatureAsset asset;
const SignaturePlacement({
required this.rect,
required this.asset,
this.rotationDeg = 0.0,
this.graphicAdjust = const GraphicAdjust(),
});
SignaturePlacement copyWith({
Rect? rect,
SignatureAsset? asset,
double? rotationDeg,
GraphicAdjust? graphicAdjust,
}) => SignaturePlacement(
rect: rect ?? this.rect,
asset: asset ?? this.asset,
rotationDeg: rotationDeg ?? this.rotationDeg,
graphicAdjust: graphicAdjust ?? this.graphicAdjust,
);
}
class PdfState {
final bool loaded;
final int pageCount;
final int currentPage;
final String? pickedPdfPath;
final Uint8List? pickedPdfBytes;
final int? signedPage;
// Multiple signature placements per page, each combines geometry and asset.
final Map<int, List<SignaturePlacement>> placementsByPage;
// UI state: selected placement index on the current page (if any)
final int? selectedPlacementIndex;
const PdfState({
required this.loaded,
required this.pageCount,
required this.currentPage,
this.pickedPdfPath,
this.pickedPdfBytes,
this.signedPage,
this.placementsByPage = const {},
this.selectedPlacementIndex,
});
factory PdfState.initial() => const PdfState(
loaded: false,
pageCount: 0,
currentPage: 1,
pickedPdfBytes: null,
signedPage: null,
placementsByPage: {},
selectedPlacementIndex: null,
);
PdfState copyWith({
bool? loaded,
int? pageCount,
int? currentPage,
String? pickedPdfPath,
Uint8List? pickedPdfBytes,
int? signedPage,
Map<int, List<SignaturePlacement>>? placementsByPage,
int? selectedPlacementIndex,
}) => PdfState(
loaded: loaded ?? this.loaded,
pageCount: pageCount ?? this.pageCount,
currentPage: currentPage ?? this.currentPage,
pickedPdfPath: pickedPdfPath ?? this.pickedPdfPath,
pickedPdfBytes: pickedPdfBytes ?? this.pickedPdfBytes,
signedPage: signedPage ?? this.signedPage,
placementsByPage: placementsByPage ?? this.placementsByPage,
selectedPlacementIndex:
selectedPlacementIndex ?? this.selectedPlacementIndex,
);
}
class SignatureState {
final Rect? rect;
final bool aspectLocked;
final bool bgRemoval;
final double contrast;
final double brightness;
// Rotation in degrees applied to the signature image when rendering/exporting
final double rotation;
final List<List<Offset>> strokes;
final Uint8List? imageBytes;
// The signature asset the current overlay is based on (from library)
final SignatureAsset? asset;
// When true, the active signature overlay is movable/resizable and should not be exported.
// When false, the overlay is confirmed (unmovable) and eligible for export.
final bool editingEnabled;
const SignatureState({
required this.rect,
required this.aspectLocked,
required this.bgRemoval,
required this.contrast,
required this.brightness,
this.rotation = 0.0,
required this.strokes,
this.imageBytes,
this.asset,
this.editingEnabled = false,
});
factory SignatureState.initial() => const SignatureState(
rect: null,
aspectLocked: false,
bgRemoval: false,
contrast: 1.0,
brightness: 0.0,
rotation: 0.0,
strokes: [],
imageBytes: null,
asset: null,
editingEnabled: false,
);
SignatureState copyWith({
Rect? rect,
bool? aspectLocked,
bool? bgRemoval,
double? contrast,
double? brightness,
double? rotation,
List<List<Offset>>? strokes,
Uint8List? imageBytes,
SignatureAsset? asset,
bool? editingEnabled,
}) => SignatureState(
rect: rect ?? this.rect,
aspectLocked: aspectLocked ?? this.aspectLocked,
bgRemoval: bgRemoval ?? this.bgRemoval,
contrast: contrast ?? this.contrast,
brightness: brightness ?? this.brightness,
rotation: rotation ?? this.rotation,
strokes: strokes ?? this.strokes,
imageBytes: imageBytes ?? this.imageBytes,
asset: asset ?? this.asset,
editingEnabled: editingEnabled ?? this.editingEnabled,
);
}

View File

@ -2,57 +2,39 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../data/model/model.dart';
import '../../domain/models/model.dart';
class PdfController extends StateNotifier<PdfState> {
PdfController() : super(PdfState.initial());
static const int samplePageCount = 5;
class DocumentStateNotifier extends StateNotifier<Document> {
DocumentStateNotifier() : super(Document.initial());
@visibleForTesting
void openSample() {
state = state.copyWith(
loaded: true,
pageCount: samplePageCount,
pageCount: 5,
currentPage: 1,
pickedPdfPath: null,
signedPage: null,
placementsByPage: {},
selectedPlacementIndex: null,
);
}
void openPicked({
required String path,
int pageCount = samplePageCount,
required int pageCount,
Uint8List? bytes,
}) {
state = state.copyWith(
loaded: true,
pageCount: pageCount,
currentPage: 1,
pickedPdfPath: path,
pickedPdfBytes: bytes,
signedPage: null,
placementsByPage: {},
selectedPlacementIndex: null,
);
}
void jumpTo(int page) {
if (!state.loaded) return;
final clamped = page.clamp(1, state.pageCount);
state = state.copyWith(currentPage: clamped, selectedPlacementIndex: null);
}
// Set or clear the page that will receive the signature overlay.
void setSignedPage(int? page) {
if (!state.loaded) return;
if (page == null) {
state = state.copyWith(signedPage: null, selectedPlacementIndex: null);
} else {
final clamped = page.clamp(1, state.pageCount);
state = state.copyWith(signedPage: clamped, selectedPlacementIndex: null);
}
state = state.copyWith(currentPage: clamped);
}
void setPageCount(int count) {
@ -80,7 +62,7 @@ class PdfController extends StateNotifier<PdfState> {
),
);
map[p] = list;
state = state.copyWith(placementsByPage: map, selectedPlacementIndex: null);
state = state.copyWith(placementsByPage: map);
}
void updatePlacementRotation({
@ -111,10 +93,7 @@ class PdfController extends StateNotifier<PdfState> {
} else {
map[p] = list;
}
state = state.copyWith(
placementsByPage: map,
selectedPlacementIndex: null,
);
state = state.copyWith(placementsByPage: map);
}
}
@ -142,27 +121,6 @@ class PdfController extends StateNotifier<PdfState> {
);
}
void selectPlacement(int? index) {
if (!state.loaded) return;
// Only allow valid index on current page; otherwise clear
if (index == null) {
state = state.copyWith(selectedPlacementIndex: null);
return;
}
final list = state.placementsByPage[state.currentPage] ?? const [];
if (index >= 0 && index < list.length) {
state = state.copyWith(selectedPlacementIndex: index);
} else {
state = state.copyWith(selectedPlacementIndex: null);
}
}
void deleteSelectedPlacement() {
final idx = state.selectedPlacementIndex;
if (idx == null) return;
removePlacement(page: state.currentPage, index: idx);
}
// NOTE: Programmatic reassignment of images has been removed.
// Convenience to get asset for a placement
@ -173,6 +131,7 @@ class PdfController extends StateNotifier<PdfState> {
}
}
final pdfProvider = StateNotifierProvider<PdfController, PdfState>(
(ref) => PdfController(),
final documentRepositoryProvider =
StateNotifierProvider<DocumentStateNotifier, Document>(
(ref) => DocumentStateNotifier(),
);

View File

@ -1,9 +1,10 @@
import 'dart:typed_data';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
class SignatureLibraryController extends StateNotifier<List<SignatureAsset>> {
SignatureLibraryController() : super(const []);
///
class SignatureAssetRepository extends StateNotifier<List<SignatureAsset>> {
SignatureAssetRepository() : super(const []);
String add(Uint8List bytes, {String? name}) {
// Always add a new asset (allow duplicates). This lets users create multiple cards
@ -27,7 +28,7 @@ class SignatureLibraryController extends StateNotifier<List<SignatureAsset>> {
}
}
final signatureLibraryProvider =
StateNotifierProvider<SignatureLibraryController, List<SignatureAsset>>(
(ref) => SignatureLibraryController(),
final signatureAssetRepositoryProvider =
StateNotifierProvider<SignatureAssetRepository, List<SignatureAsset>>(
(ref) => SignatureAssetRepository(),
);

View File

@ -6,15 +6,18 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:image/image.dart' as img;
import 'package:pdf_signature/l10n/app_localizations.dart';
import '../../../../data/model/model.dart';
import '../../domain/models/model.dart';
import 'pdf_repository.dart';
class SignatureController extends StateNotifier<SignatureState> {
SignatureController() : super(SignatureState.initial());
class SignatureController extends StateNotifier<SignatureCard> {
final Ref ref;
SignatureController(this.ref) : super(SignatureCard.initial());
static const Size pageSize = Size(400, 560);
void resetForNewPage() {
state = SignatureState.initial();
state = SignatureCard.initial();
ref.read(currentRectProvider.notifier).setRect(null);
ref.read(editingEnabledProvider.notifier).set(false);
}
@visibleForTesting
@ -26,19 +29,22 @@ class SignatureController extends StateNotifier<SignatureState> {
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);
ref.read(currentRectProvider.notifier).setRect(r);
ref.read(editingEnabledProvider.notifier).set(true);
}
void loadSample() {
final w = 120.0, h = 60.0;
state = state.copyWith(
rect: Rect.fromCenter(
ref
.read(currentRectProvider.notifier)
.setRect(
Rect.fromCenter(
center: Offset(pageSize.width / 2, pageSize.height * 0.75),
width: w,
height: h,
),
editingEnabled: true,
);
ref.read(editingEnabledProvider.notifier).set(true);
}
void setInvalidSelected(BuildContext context) {
@ -56,17 +62,19 @@ class SignatureController extends StateNotifier<SignatureState> {
}
void drag(Offset delta) {
if (state.rect == null || !state.editingEnabled) return;
final moved = state.rect!.shift(delta);
state = state.copyWith(rect: _clampRectToPage(moved));
final currentRect = ref.read(currentRectProvider);
if (currentRect == null || !ref.read(editingEnabledProvider)) return;
final moved = currentRect.shift(delta);
ref.read(currentRectProvider.notifier).setRect(_clampRectToPage(moved));
}
void resize(Offset delta) {
if (state.rect == null || !state.editingEnabled) return;
final r = state.rect!;
final currentRect = ref.read(currentRectProvider);
if (currentRect == null || !ref.read(editingEnabledProvider)) return;
final r = currentRect;
double newW = r.width + delta.dx;
double newH = r.height + delta.dy;
if (state.aspectLocked) {
if (ref.read(aspectLockedProvider)) {
final aspect = r.width / r.height;
// Keep ratio based on the dominant proportional delta
final dxRel = (delta.dx / r.width).abs();
@ -90,7 +98,7 @@ class SignatureController extends StateNotifier<SignatureState> {
newH *= minScale;
Rect resized = Rect.fromLTWH(r.left, r.top, newW, newH);
resized = _clampRectPositionToPage(resized);
state = state.copyWith(rect: resized);
ref.read(currentRectProvider.notifier).setRect(resized);
return;
}
// Unlocked aspect: clamp each dimension independently
@ -98,7 +106,7 @@ class SignatureController extends StateNotifier<SignatureState> {
newH = newH.clamp(20.0, pageSize.height);
Rect resized = Rect.fromLTWH(r.left, r.top, newW, newH);
resized = _clampRectToPage(resized);
state = state.copyWith(rect: resized);
ref.read(currentRectProvider.notifier).setRect(resized);
}
Rect _clampRectToPage(Rect r) {
@ -116,89 +124,98 @@ class SignatureController extends StateNotifier<SignatureState> {
return Rect.fromLTWH(left, top, r.width, r.height);
}
void toggleAspect(bool v) => state = state.copyWith(aspectLocked: v);
void setBgRemoval(bool v) => state = state.copyWith(bgRemoval: v);
void setContrast(double v) => state = state.copyWith(contrast: v);
void setBrightness(double v) => state = state.copyWith(brightness: v);
void setRotation(double deg) => state = state.copyWith(rotation: deg);
void setStrokes(List<List<Offset>> strokes) =>
state = state.copyWith(strokes: strokes);
void ensureRectForStrokes() {
void toggleAspect(bool v) => ref.read(aspectLockedProvider.notifier).set(v);
void setBgRemoval(bool v) =>
state = state.copyWith(
rect:
state.rect ??
graphicAdjust: state.graphicAdjust.copyWith(bgRemoval: v),
);
void setContrast(double v) =>
state = state.copyWith(
graphicAdjust: state.graphicAdjust.copyWith(contrast: v),
);
void setBrightness(double v) =>
state = state.copyWith(
graphicAdjust: state.graphicAdjust.copyWith(brightness: v),
);
void setRotation(double deg) => state = state.copyWith(rotationDeg: deg);
void ensureRectForStrokes() {
if (ref.read(currentRectProvider) == null) {
ref
.read(currentRectProvider.notifier)
.setRect(
Rect.fromCenter(
center: Offset(pageSize.width / 2, pageSize.height * 0.75),
width: 140,
height: 70,
),
editingEnabled: true,
);
ref.read(editingEnabledProvider.notifier).set(true);
}
}
void setImageBytes(Uint8List bytes) {
state = state.copyWith(imageBytes: bytes, asset: null);
if (state.rect == null) {
final newAsset = SignatureAsset(id: 'drawn', bytes: bytes);
state = state.copyWith(asset: newAsset);
if (ref.read(currentRectProvider) == null) {
placeDefaultRect();
}
// Mark as draft/editable when user just loaded image
state = state.copyWith(editingEnabled: true);
ref.read(editingEnabledProvider.notifier).set(true);
}
// Select image from the shared signature library
void setImageFromLibrary({required SignatureAsset asset}) {
state = state.copyWith(asset: asset);
if (state.rect == null) {
if (ref.read(currentRectProvider) == null) {
placeDefaultRect();
}
state = state.copyWith(editingEnabled: true);
ref.read(editingEnabledProvider.notifier).set(true);
}
void clearImage() {
state = state.copyWith(imageBytes: null, rect: null, editingEnabled: false);
state = SignatureCard.initial();
ref.read(currentRectProvider.notifier).setRect(null);
ref.read(editingEnabledProvider.notifier).set(false);
}
void placeAtCenter(Offset center, {double width = 120, double height = 60}) {
Rect r = Rect.fromCenter(center: center, width: width, height: height);
r = _clampRectToPage(r);
state = state.copyWith(rect: r, editingEnabled: true);
ref.read(currentRectProvider.notifier).setRect(r);
ref.read(editingEnabledProvider.notifier).set(true);
}
// Confirm current signature: freeze editing and place it on the PDF as an immutable overlay.
// Stores the placement rect in UI-space (SignatureController.pageSize units).
// Returns the Rect placed, or null if no rect to confirm.
Rect? confirmCurrentSignature(WidgetRef ref) {
final r = state.rect;
final r = ref.read(currentRectProvider);
if (r == null) return null;
// Place onto the current page
final pdf = ref.read(pdfProvider);
final pdf = ref.read(documentRepositoryProvider);
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.
// Prefer reusing an existing library asset 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 asset empty so the
// UI and exporter will fall back to using the processed/current bytes.
// Store as UI-space rect (consistent with export and rendering paths)
ref
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: pdf.currentPage,
rect: r,
asset: state.asset,
rotationDeg: state.rotation,
rotationDeg: state.rotationDeg,
);
// Newly placed index is the last one on the page
final idx =
(ref.read(pdfProvider).placementsByPage[pdf.currentPage]?.length ?? 1) -
(ref
.read(documentRepositoryProvider)
.placementsByPage[pdf.currentPage]
?.length ??
1) -
1;
// Auto-select the newly placed item so the red box appears
if (idx >= 0) {
ref.read(pdfProvider.notifier).selectPlacement(idx);
ref.read(documentRepositoryProvider.notifier).selectPlacement(idx);
}
// Freeze editing: keep rect for preview but disable interaction
state = state.copyWith(editingEnabled: false);
ref.read(editingEnabledProvider.notifier).set(false);
return r;
}
@ -206,76 +223,89 @@ class SignatureController extends StateNotifier<SignatureState> {
// Useful in widget tests where obtaining a WidgetRef is not straightforward.
@visibleForTesting
Rect? confirmCurrentSignatureWithContainer(ProviderContainer container) {
final r = state.rect;
final r = container.read(currentRectProvider);
if (r == null) return null;
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
if (!pdf.loaded) return null;
// Reuse existing library asset if present; otherwise leave empty so the
// placement will reference the current bytes via fallback paths.
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: pdf.currentPage,
rect: r,
asset: state.asset,
rotationDeg: state.rotation,
rotationDeg: state.rotationDeg,
);
final idx =
(container
.read(pdfProvider)
.read(documentRepositoryProvider)
.placementsByPage[pdf.currentPage]
?.length ??
1) -
1;
// Auto-select the newly placed item so the red box appears
if (idx >= 0) {
container.read(pdfProvider.notifier).selectPlacement(idx);
container.read(documentRepositoryProvider.notifier).selectPlacement(idx);
}
// Freeze editing: keep rect for preview but disable interaction
state = state.copyWith(editingEnabled: false);
container.read(editingEnabledProvider.notifier).set(false);
return r;
}
// Remove the active overlay (draft or confirmed preview) but keep image settings intact
void clearActiveOverlay() {
state = state.copyWith(rect: null, editingEnabled: false);
ref.read(currentRectProvider.notifier).setRect(null);
ref.read(editingEnabledProvider.notifier).set(false);
}
}
final signatureProvider =
StateNotifierProvider<SignatureController, SignatureState>(
(ref) => SignatureController(),
final signatureCardProvider =
StateNotifierProvider<SignatureController, SignatureCard>(
(ref) => SignatureController(ref),
);
final currentRectProvider = StateNotifierProvider<RectNotifier, Rect?>(
(ref) => RectNotifier(),
);
class RectNotifier extends StateNotifier<Rect?> {
RectNotifier() : super(null);
void setRect(Rect? r) => state = r;
}
final editingEnabledProvider = StateNotifierProvider<BoolNotifier, bool>(
(ref) => BoolNotifier(false),
);
class BoolNotifier extends StateNotifier<bool> {
BoolNotifier(bool initial) : super(initial);
void set(bool v) => state = v;
}
final aspectLockedProvider = StateNotifierProvider<BoolNotifier, bool>(
(ref) => BoolNotifier(false),
);
/// Derived provider that returns processed signature image bytes according to
/// current adjustment settings (contrast/brightness) and background removal.
/// Returns null if no image is loaded. The output is a PNG to preserve alpha.
final processedSignatureImageProvider = Provider<Uint8List?>((ref) {
// Watch only the fields that affect pixel processing to avoid recompute on rotation.
final SignatureAsset? asset = ref.watch(
signatureProvider.select((s) => s.asset),
);
final Uint8List? directBytes = ref.watch(
signatureProvider.select((s) => s.imageBytes),
final SignatureAsset asset = ref.watch(
signatureCardProvider.select((s) => s.asset),
);
final double contrast = ref.watch(
signatureProvider.select((s) => s.contrast),
signatureCardProvider.select((s) => s.graphicAdjust.contrast),
);
final double brightness = ref.watch(
signatureProvider.select((s) => s.brightness),
signatureCardProvider.select((s) => s.graphicAdjust.brightness),
);
final bool bgRemoval = ref.watch(
signatureProvider.select((s) => s.bgRemoval),
signatureCardProvider.select((s) => s.graphicAdjust.bgRemoval),
);
// If active overlay is based on a library asset, pull its bytes
Uint8List? bytes;
if (asset != null) {
bytes = asset.bytes;
} else {
bytes = directBytes;
}
if (bytes == null || bytes.isEmpty) return null;
Uint8List? bytes = asset.bytes;
if (bytes.isEmpty) return null;
// Decode (supports PNG/JPEG, etc.)
final decoded = img.decodeImage(bytes);

View File

@ -6,7 +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';
import '../../domain/models/model.dart';
// NOTE:
// - This exporter uses a raster snapshot of the UI (RepaintBoundary) and embeds it into a new PDF.

View File

@ -0,0 +1,39 @@
import 'dart:typed_data';
import 'signature_placement.dart';
/// PDF document to be signed
class Document {
final bool loaded;
final int pageCount;
final int currentPage;
final Uint8List? pickedPdfBytes;
// Multiple signature placements per page, each combines geometry and asset.
final Map<int, List<SignaturePlacement>> placementsByPage;
const Document({
required this.loaded,
required this.pageCount,
required this.currentPage,
this.pickedPdfBytes,
this.placementsByPage = const {},
});
factory Document.initial() => const Document(
loaded: false,
pageCount: 0,
currentPage: 1,
pickedPdfBytes: null,
placementsByPage: {},
);
Document copyWith({
bool? loaded,
int? pageCount,
int? currentPage,
Uint8List? pickedPdfBytes,
Map<int, List<SignaturePlacement>>? placementsByPage,
}) => Document(
loaded: loaded ?? this.loaded,
pageCount: pageCount ?? this.pageCount,
currentPage: currentPage ?? this.currentPage,
pickedPdfBytes: pickedPdfBytes ?? this.pickedPdfBytes,
placementsByPage: placementsByPage ?? this.placementsByPage,
);
}

View File

@ -0,0 +1,21 @@
class GraphicAdjust {
final double contrast;
final double brightness;
final bool bgRemoval;
const GraphicAdjust({
this.contrast = 1.0,
this.brightness = 0.0,
this.bgRemoval = false,
});
GraphicAdjust copyWith({
double? contrast,
double? brightness,
bool? bgRemoval,
}) => GraphicAdjust(
contrast: contrast ?? this.contrast,
brightness: brightness ?? this.brightness,
bgRemoval: bgRemoval ?? this.bgRemoval,
);
}

View File

@ -0,0 +1,5 @@
export 'signature_asset.dart';
export 'graphic_adjust.dart';
export 'signature_card.dart';
export 'signature_placement.dart';
export 'document.dart';

View File

@ -0,0 +1,10 @@
import 'dart:typed_data';
/// SignatureAsset store image file of a signature, stored in the device or cloud storage
class SignatureAsset {
final String id; // unique id
final Uint8List bytes;
// List<List<Offset>>? strokes;
final String? name; // optional display name (e.g., filename)
const SignatureAsset({required this.id, required this.bytes, this.name});
}

View File

@ -0,0 +1,35 @@
import 'dart:typed_data';
import 'signature_asset.dart';
import 'graphic_adjust.dart';
/**
* signature card is template of signature placement
* Use the [SignatureCardRepository] to obtain a full [SignatureCard]
*/
class SignatureCard {
final double rotationDeg;
final SignatureAsset asset;
final GraphicAdjust graphicAdjust;
const SignatureCard({
required this.rotationDeg,
required this.asset,
this.graphicAdjust = const GraphicAdjust(),
});
SignatureCard copyWith({
double? rotationDeg,
SignatureAsset? asset,
GraphicAdjust? graphicAdjust,
}) => SignatureCard(
rotationDeg: rotationDeg ?? this.rotationDeg,
asset: asset ?? this.asset,
graphicAdjust: graphicAdjust ?? this.graphicAdjust,
);
factory SignatureCard.initial() => SignatureCard(
rotationDeg: 0.0,
asset: SignatureAsset(id: '', bytes: Uint8List(0)),
graphicAdjust: const GraphicAdjust(),
);
}

View File

@ -0,0 +1,35 @@
import 'dart:ui';
import 'signature_asset.dart';
import 'graphic_adjust.dart';
/// Represents a single signature placement on a page combining both the
/// geometric rectangle (UI coordinate space) and the signature asset
/// assigned to that placement.
class SignaturePlacement {
// The bounding box of this placement in UI coordinate space, implies scaling and position.
final Rect rect;
/// Rotation in degrees to apply when rendering/exporting this placement.
final double rotationDeg;
final GraphicAdjust graphicAdjust;
final SignatureAsset asset;
const SignaturePlacement({
required this.rect,
required this.asset,
this.rotationDeg = 0.0,
this.graphicAdjust = const GraphicAdjust(),
});
SignaturePlacement copyWith({
Rect? rect,
SignatureAsset? asset,
double? rotationDeg,
GraphicAdjust? graphicAdjust,
}) => SignaturePlacement(
rect: rect ?? this.rect,
asset: asset ?? this.asset,
rotationDeg: rotationDeg ?? this.rotationDeg,
graphicAdjust: graphicAdjust ?? this.graphicAdjust,
);
}

View File

@ -2,13 +2,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/l10n/app_localizations.dart';
import '../../../../data/model/model.dart';
import '../../../../domain/models/model.dart';
import 'package:pdf_signature/data/repositories/signature_repository.dart';
class AdjustmentsPanel extends ConsumerWidget {
const AdjustmentsPanel({super.key, required this.sig});
final SignatureState sig;
final SignatureCard sig;
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -22,19 +22,20 @@ class AdjustmentsPanel extends ConsumerWidget {
children: [
Checkbox(
key: const Key('chk_aspect_lock'),
value: sig.aspectLocked,
value: ref.watch(aspectLockedProvider),
onChanged:
(v) => ref
.read(signatureProvider.notifier)
.read(signatureCardProvider.notifier)
.toggleAspect(v ?? false),
),
Text(AppLocalizations.of(context).lockAspectRatio),
const SizedBox(width: 16),
Switch(
key: const Key('swt_bg_removal'),
value: sig.bgRemoval,
value: sig.graphicAdjust.bgRemoval,
onChanged:
(v) => ref.read(signatureProvider.notifier).setBgRemoval(v),
(v) =>
ref.read(signatureCardProvider.notifier).setBgRemoval(v),
),
Text(AppLocalizations.of(context).backgroundRemoval),
],
@ -47,15 +48,16 @@ class AdjustmentsPanel extends ConsumerWidget {
Text(AppLocalizations.of(context).contrast),
Align(
alignment: Alignment.centerRight,
child: Text(sig.contrast.toStringAsFixed(2)),
child: Text(sig.graphicAdjust.contrast.toStringAsFixed(2)),
),
Slider(
key: const Key('sld_contrast'),
min: 0.0,
max: 2.0,
value: sig.contrast,
value: sig.graphicAdjust.contrast,
onChanged:
(v) => ref.read(signatureProvider.notifier).setContrast(v),
(v) =>
ref.read(signatureCardProvider.notifier).setContrast(v),
),
],
),
@ -66,15 +68,16 @@ class AdjustmentsPanel extends ConsumerWidget {
Text(AppLocalizations.of(context).brightness),
Align(
alignment: Alignment.centerRight,
child: Text(sig.brightness.toStringAsFixed(2)),
child: Text(sig.graphicAdjust.brightness.toStringAsFixed(2)),
),
Slider(
key: const Key('sld_brightness'),
min: -1.0,
max: 1.0,
value: sig.brightness,
value: sig.graphicAdjust.brightness,
onChanged:
(v) => ref.read(signatureProvider.notifier).setBrightness(v),
(v) =>
ref.read(signatureCardProvider.notifier).setBrightness(v),
),
],
),

View File

@ -51,7 +51,7 @@ class _PdfPageAreaState extends ConsumerState<PdfPageArea> {
// is instructed to align to the provider's current page once ready.
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
final pdf = ref.read(pdfProvider);
final pdf = ref.read(documentRepositoryProvider);
if (pdf.pickedPdfPath != null && pdf.loaded) {
_scrollToPage(pdf.currentPage);
}
@ -68,7 +68,7 @@ class _PdfPageAreaState extends ConsumerState<PdfPageArea> {
void _scrollToPage(int page) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
final pdf = ref.read(pdfProvider);
final pdf = ref.read(documentRepositoryProvider);
const isContinuous = true;
// Real continuous: drive via PdfViewerController
@ -156,11 +156,11 @@ class _PdfPageAreaState extends ConsumerState<PdfPageArea> {
@override
Widget build(BuildContext context) {
final pdf = ref.watch(pdfProvider);
final pdf = ref.watch(documentRepositoryProvider);
const pageViewMode = 'continuous';
// React to provider currentPage changes (e.g., user tapped overview)
ref.listen(pdfProvider, (prev, next) {
ref.listen(documentRepositoryProvider, (prev, next) {
if (_suppressProviderListen) return;
if ((prev?.currentPage != next.currentPage)) {
final target = next.currentPage;
@ -288,7 +288,9 @@ class _PdfPageAreaState extends ConsumerState<PdfPageArea> {
],
onViewerReady: (doc, controller) {
if (pdf.pageCount != doc.pages.length) {
ref.read(pdfProvider.notifier).setPageCount(doc.pages.length);
ref
.read(documentRepositoryProvider.notifier)
.setPageCount(doc.pages.length);
}
final target = _pendingPage ?? pdf.currentPage;
_pendingPage = null;
@ -305,9 +307,9 @@ class _PdfPageAreaState extends ConsumerState<PdfPageArea> {
// Programmatic navigation: wait until target reached
if (_programmaticTargetPage != null) {
if (n == _programmaticTargetPage) {
if (n != ref.read(pdfProvider).currentPage) {
if (n != ref.read(documentRepositoryProvider).currentPage) {
_suppressProviderListen = true;
ref.read(pdfProvider.notifier).jumpTo(n);
ref.read(documentRepositoryProvider.notifier).jumpTo(n);
WidgetsBinding.instance.addPostFrameCallback((_) {
_suppressProviderListen = false;
});
@ -317,9 +319,9 @@ class _PdfPageAreaState extends ConsumerState<PdfPageArea> {
return;
}
// User scroll -> reflect page to provider without re-triggering scroll
if (n != ref.read(pdfProvider).currentPage) {
if (n != ref.read(documentRepositoryProvider).currentPage) {
_suppressProviderListen = true;
ref.read(pdfProvider.notifier).jumpTo(n);
ref.read(documentRepositoryProvider.notifier).jumpTo(n);
WidgetsBinding.instance.addPostFrameCallback((_) {
_suppressProviderListen = false;
});
@ -348,8 +350,8 @@ class _PdfPageAreaState extends ConsumerState<PdfPageArea> {
}
ref.read(signatureProvider.notifier).placeAtCenter(Offset(cx, cy));
ref
.read(pdfProvider.notifier)
.setSignedPage(ref.read(pdfProvider).currentPage);
.read(documentRepositoryProvider.notifier)
.setSignedPage(ref.read(documentRepositoryProvider).currentPage);
},
builder:
(context, candidateData, rejected) => Stack(

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/signature_repository.dart';
import '../../../../data/model/model.dart';
import '../../../../domain/models/model.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'signature_overlay.dart';
@ -29,8 +29,8 @@ class PdfPageOverlays extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final pdf = ref.watch(pdfProvider);
final sig = ref.watch(signatureProvider);
final pdf = ref.watch(documentRepositoryProvider);
final sig = ref.watch(signatureCardProvider);
final placed =
pdf.placementsByPage[pageNumber] ?? const <SignaturePlacement>[];
final widgets = <Widget>[];
@ -44,16 +44,17 @@ class PdfPageOverlays extends ConsumerWidget {
rect: uiRect,
sig: sig,
pageNumber: pageNumber,
interactive: false,
placedIndex: i,
onSelectPlaced: onSelectPlaced,
),
);
}
final currentRect = ref.watch(currentRectProvider);
final editingEnabled = ref.watch(editingEnabledProvider);
final showActive =
sig.rect != null &&
sig.editingEnabled &&
currentRect != null &&
editingEnabled &&
(pdf.signedPage == null || pdf.signedPage == pageNumber) &&
pdf.currentPage == pageNumber;
@ -61,10 +62,9 @@ class PdfPageOverlays extends ConsumerWidget {
widgets.add(
SignatureOverlay(
pageSize: pageSize,
rect: sig.rect!,
rect: currentRect,
sig: sig,
pageNumber: pageNumber,
interactive: true,
onDragSignature: onDragSignature,
onResizeSignature: onResizeSignature,
onConfirmSignature: onConfirmSignature,

View File

@ -10,7 +10,7 @@ class PdfPagesOverview extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final pdf = ref.watch(pdfProvider);
final pdf = ref.watch(documentRepositoryProvider);
final useMock = ref.watch(useMockViewerProvider);
final theme = Theme.of(context);
@ -25,7 +25,10 @@ class PdfPagesOverview extends ConsumerWidget {
final pageNumber = index + 1;
final isSelected = pdf.currentPage == pageNumber;
return InkWell(
onTap: () => ref.read(pdfProvider.notifier).jumpTo(pageNumber),
onTap:
() => ref
.read(documentRepositoryProvider.notifier)
.jumpTo(pageNumber),
child: DecoratedBox(
decoration: BoxDecoration(
color:
@ -74,7 +77,9 @@ class PdfPagesOverview extends ConsumerWidget {
final pages = document.pages;
if (pdf.pageCount != pages.length) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(pdfProvider.notifier).setPageCount(pages.length);
ref
.read(documentRepositoryProvider.notifier)
.setPageCount(pages.length);
});
}
return buildList(

View File

@ -12,7 +12,7 @@ import '../../../../data/services/export_providers.dart';
import 'package:image/image.dart' as img;
import 'package:pdf_signature/data/repositories/signature_repository.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'draw_canvas.dart';
import 'pdf_toolbar.dart';
import 'pdf_page_area.dart';
@ -61,13 +61,15 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
} catch (_) {
bytes = null;
}
ref.read(pdfProvider.notifier).openPicked(path: file.path, bytes: bytes);
ref
.read(documentRepositoryProvider.notifier)
.openPicked(path: file.path, bytes: bytes);
ref.read(signatureProvider.notifier).resetForNewPage();
}
}
void _jumpToPage(int page) {
ref.read(pdfProvider.notifier).jumpTo(page);
ref.read(documentRepositoryProvider.notifier).jumpTo(page);
}
Future<Uint8List?> _loadSignatureFromFile() async {
@ -81,9 +83,11 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
final bytes = await file.readAsBytes();
final sig = ref.read(signatureProvider.notifier);
sig.setImageBytes(bytes);
final p = ref.read(pdfProvider);
final p = ref.read(documentRepositoryProvider);
if (p.loaded) {
ref.read(pdfProvider.notifier).setSignedPage(p.currentPage);
ref
.read(documentRepositoryProvider.notifier)
.setSignedPage(p.currentPage);
}
return bytes;
}
@ -101,7 +105,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
}
void _onSelectPlaced(int? index) {
ref.read(pdfProvider.notifier).selectPlacement(index);
ref.read(documentRepositoryProvider.notifier).selectPlacement(index);
}
Future<Uint8List?> _openDrawCanvas() async {
@ -113,9 +117,11 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
);
if (result != null && result.isNotEmpty) {
ref.read(signatureProvider.notifier).setImageBytes(result);
final p = ref.read(pdfProvider);
final p = ref.read(documentRepositoryProvider);
if (p.loaded) {
ref.read(pdfProvider.notifier).setSignedPage(p.currentPage);
ref
.read(documentRepositoryProvider.notifier)
.setSignedPage(p.currentPage);
}
}
return result;
@ -124,7 +130,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
Future<void> _saveSignedPdf() async {
ref.read(exportingProvider.notifier).state = true;
try {
final pdf = ref.read(pdfProvider);
final pdf = ref.read(documentRepositoryProvider);
final sig = ref.read(signatureProvider);
final messenger = ScaffoldMessenger.of(context);
if (!pdf.loaded || sig.rect == null) {
@ -175,7 +181,8 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
signatureImageBytes: rotated,
placementsByPage: pdf.placementsByPage,
libraryBytes: {
for (final a in ref.read(signatureLibraryProvider)) a.id: a.bytes,
for (final a in ref.read(signatureAssetRepositoryProvider))
a.id: a.bytes,
},
targetDpi: targetDpi,
);
@ -211,7 +218,8 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
signatureImageBytes: rotated,
placementsByPage: pdf.placementsByPage,
libraryBytes: {
for (final a in ref.read(signatureLibraryProvider)) a.id: a.bytes,
for (final a in ref.read(signatureAssetRepositoryProvider))
a.id: a.bytes,
},
targetDpi: targetDpi,
);
@ -241,7 +249,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
signatureImageBytes: rotated,
placementsByPage: pdf.placementsByPage,
libraryBytes: {
for (final a in ref.read(signatureLibraryProvider))
for (final a in ref.read(signatureAssetRepositoryProvider))
a.id: a.bytes,
},
targetDpi: targetDpi,
@ -411,7 +419,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
});
},
zoomLevel: _zoomLevel,
fileName: ref.watch(pdfProvider).pickedPdfPath,
fileName: ref.watch(documentRepositoryProvider).pickedPdfPath,
showPagesSidebar: _showPagesSidebar,
showSignaturesSidebar: _showSignaturesSidebar,
onTogglePagesSidebar:

View File

@ -55,7 +55,7 @@ class _PdfToolbarState extends ConsumerState<PdfToolbar> {
@override
Widget build(BuildContext context) {
final pdf = ref.watch(pdfProvider);
final pdf = ref.watch(documentRepositoryProvider);
final l = AppLocalizations.of(context);
final pageInfo = l.pageInfo(pdf.currentPage, pdf.pageCount);

View File

@ -2,11 +2,11 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/l10n/app_localizations.dart';
import 'package:pdf_signature/data/model/model.dart' as model;
import 'package:pdf_signature/domain/models/model.dart' as model;
import '../../../../data/services/export_providers.dart';
import 'package:pdf_signature/data/repositories/signature_repository.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'image_editor_dialog.dart';
import '../../signature/widgets/signature_card.dart';
@ -37,7 +37,7 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
final sig = ref.watch(signatureProvider);
final processed = ref.watch(processedSignatureImageProvider);
final bytes = processed ?? sig.imageBytes;
final library = ref.watch(signatureLibraryProvider);
final library = ref.watch(signatureAssetRepositoryProvider);
final isExporting = ref.watch(exportingProvider);
final disabled = widget.disabled || isExporting;
@ -64,7 +64,7 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
disabled: disabled,
onDelete:
() => ref
.read(signatureLibraryProvider.notifier)
.read(signatureAssetRepositoryProvider.notifier)
.remove(a.id),
onAdjust: () async {
ref
@ -151,10 +151,16 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
ref.read(signatureProvider).imageBytes;
if (b != null) {
final id = ref
.read(signatureLibraryProvider.notifier)
.read(
signatureAssetRepositoryProvider
.notifier,
)
.add(b, name: 'image');
final asset = ref
.read(signatureLibraryProvider.notifier)
.read(
signatureAssetRepositoryProvider
.notifier,
)
.byId(id);
if (asset != null) {
ref
@ -179,10 +185,16 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
ref.read(signatureProvider).imageBytes;
if (b != null) {
final id = ref
.read(signatureLibraryProvider.notifier)
.read(
signatureAssetRepositoryProvider
.notifier,
)
.add(b, name: 'drawing');
final asset = ref
.read(signatureLibraryProvider.notifier)
.read(
signatureAssetRepositoryProvider
.notifier,
)
.byId(id);
if (asset != null) {
ref

View File

@ -4,10 +4,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/l10n/app_localizations.dart';
import '../../../../data/model/model.dart';
import '../../../../domain/models/model.dart';
import 'package:pdf_signature/data/repositories/signature_repository.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'image_editor_dialog.dart';
import '../../signature/widgets/rotated_signature_image.dart';
@ -19,7 +19,6 @@ class SignatureOverlay extends ConsumerWidget {
required this.rect,
required this.sig,
required this.pageNumber,
this.interactive = true,
this.placedIndex,
this.onDragSignature,
this.onResizeSignature,
@ -30,9 +29,8 @@ class SignatureOverlay extends ConsumerWidget {
final Size pageSize;
final Rect rect;
final SignatureState sig;
final SignatureCard sig;
final int pageNumber;
final bool interactive;
final int? placedIndex;
// Callbacks used by interactive overlay
@ -75,7 +73,8 @@ class SignatureOverlay extends ConsumerWidget {
double scaleX,
double scaleY,
) {
final selectedIdx = ref.read(pdfProvider).selectedPlacementIndex;
final selectedIdx =
ref.read(documentRepositoryProvider).selectedPlacementIndex;
final bool isPlaced = placedIndex != null;
final bool isSelected = isPlaced && selectedIdx == placedIndex;
final Color borderColor = isPlaced ? Colors.red : Colors.indigo;
@ -92,7 +91,7 @@ class SignatureOverlay extends ConsumerWidget {
0,
0,
0,
0.05 + math.min(0.25, (sig.contrast - 1.0).abs()),
0.05 + math.min(0.25, (sig.graphicAdjust.contrast - 1.0).abs()),
),
),
),
@ -210,7 +209,7 @@ class SignatureOverlay extends ConsumerWidget {
onClearActiveOverlay?.call();
} else {
ref
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.removePlacement(page: pageNumber, index: placedIndex);
}
} else if (choice == 'adjust') {
@ -231,23 +230,24 @@ class _SignatureImage extends ConsumerWidget {
final bool interactive;
final int? placedIndex;
final int pageNumber;
final SignatureState sig;
final SignatureCard sig;
@override
Widget build(BuildContext context, WidgetRef ref) {
Uint8List? bytes;
if (interactive) {
final processed = ref.watch(processedSignatureImageProvider);
bytes = processed ?? sig.imageBytes;
bytes = processed ?? sig.asset.bytes;
} else if (placedIndex != null) {
final placementList = ref.read(pdfProvider).placementsByPage[pageNumber];
final placementList =
ref.read(documentRepositoryProvider).placementsByPage[pageNumber];
final placement =
(placementList != null && placedIndex! < placementList.length)
? placementList[placedIndex!]
: null;
final imgId = (placement?.asset)?.id;
if (imgId != null && imgId.isNotEmpty) {
final lib = ref.watch(signatureLibraryProvider);
final lib = ref.watch(signatureAssetRepositoryProvider);
for (final a in lib) {
if (a.id == imgId) {
bytes = a.bytes;
@ -255,7 +255,7 @@ class _SignatureImage extends ConsumerWidget {
}
}
}
bytes ??= ref.read(processedSignatureImageProvider) ?? sig.imageBytes;
bytes ??= ref.read(processedSignatureImageProvider) ?? sig.asset.bytes;
}
if (bytes == null) {
@ -271,9 +271,10 @@ class _SignatureImage extends ConsumerWidget {
// Use live rotation for interactive overlay; stored rotation for placed
double rotationDeg = 0.0;
if (interactive) {
rotationDeg = sig.rotation;
rotationDeg = sig.rotationDeg;
} else if (placedIndex != null) {
final placementList = ref.read(pdfProvider).placementsByPage[pageNumber];
final placementList =
ref.read(documentRepositoryProvider).placementsByPage[pageNumber];
if (placementList != null && placedIndex! < placementList.length) {
rotationDeg = placementList[placedIndex!].rotationDeg;
}

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
import 'signature_drag_data.dart';
import 'rotated_signature_image.dart';
import 'package:pdf_signature/l10n/app_localizations.dart';

View File

@ -1,4 +1,4 @@
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
class SignatureDragData {
final SignatureAsset? asset; // null means use current processed signature

View File

@ -50,7 +50,9 @@ Future<void> handleDroppedFiles(
bytes = null;
}
final String path = pdf.path ?? pdf.name;
read(pdfProvider.notifier).openPicked(path: path, bytes: bytes);
read(
documentRepositoryProvider.notifier,
).openPicked(path: path, bytes: bytes);
read(signatureProvider.notifier).resetForNewPage();
}
@ -74,7 +76,9 @@ class _WelcomeScreenState extends ConsumerState<WelcomeScreen> {
} catch (_) {
bytes = null;
}
ref.read(pdfProvider.notifier).openPicked(path: file.path, bytes: bytes);
ref
.read(documentRepositoryProvider.notifier)
.openPicked(path: file.path, bytes: bytes);
ref.read(signatureProvider.notifier).resetForNewPage();
}
}

View File

@ -52,6 +52,11 @@ dependencies:
flutter_localized_locales: ^2.0.5
desktop_drop: ^0.5.0
multi_split_view: ^3.6.1
freezed_annotation: ^3.1.0
json_annotation: ^4.9.0
share_plus: ^11.1.0
logging: ^1.3.0
riverpod_annotation: ^2.6.1
dev_dependencies:
flutter_test:
@ -61,6 +66,8 @@ dev_dependencies:
build_runner: ^2.4.12
build: ^3.0.2
bdd_widget_test: ^2.0.1
mocktail: ^1.0.4
freezed: ^3.0.0
custom_lint: ^0.7.6
riverpod_lint: ^2.6.5

View File

@ -1,8 +1,8 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: a created signature card
@ -15,5 +15,5 @@ Future<void> aCreatedSignatureCard(WidgetTester tester) async {
bytes: Uint8List(100),
name: 'Test Card',
);
container.read(signatureLibraryProvider.notifier).state = [asset];
container.read(signatureAssetRepositoryProvider.notifier).state = [asset];
}

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: a document is open and contains at least one signature placement
@ -13,10 +13,10 @@ Future<void> aDocumentIsOpenAndContainsAtLeastOneSignaturePlacement(
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.openPicked(path: 'test.pdf', pageCount: 5);
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: 1,
rect: Rect.fromLTWH(10, 10, 100, 50),

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: a document is open and contains multiple placed signature placements across pages
@ -14,24 +14,24 @@ aDocumentIsOpenAndContainsMultiplePlacedSignaturePlacementsAcrossPages(
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.openPicked(path: 'multi.pdf', pageCount: 5);
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: 1,
rect: Rect.fromLTWH(10, 10, 100, 50),
asset: SignatureAsset(id: 'sig1.png', bytes: Uint8List(0)),
);
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: 2,
rect: Rect.fromLTWH(20, 20, 100, 50),
asset: SignatureAsset(id: 'sig2.png', bytes: Uint8List(0)),
);
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: 3,
rect: Rect.fromLTWH(30, 30, 100, 50),

View File

@ -10,7 +10,7 @@ Future<void> aDocumentIsOpenWithNoSignaturePlacementsPlaced(
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.openPicked(path: 'empty.pdf', pageCount: 5);
// No placements added
}

View File

@ -7,6 +7,6 @@ import '_world.dart';
Future<void> aDocumentPageIsSelectedForSigning(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container.read(pdfProvider.notifier).setSignedPage(1);
container.read(pdfProvider.notifier).jumpTo(1);
container.read(documentRepositoryProvider.notifier).setSignedPage(1);
container.read(documentRepositoryProvider.notifier).jumpTo(1);
}

View File

@ -2,18 +2,23 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/repositories/signature_repository.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: a multi-page document is open
Future<void> aMultipageDocumentIsOpen(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container.read(signatureLibraryProvider.notifier).state = [];
container.read(pdfProvider.notifier).state = PdfState.initial();
container.read(signatureProvider.notifier).state = SignatureState.initial();
container.read(signatureAssetRepositoryProvider.notifier).state = [];
container.read(documentRepositoryProvider.notifier).state =
Document.initial();
container.read(signatureCardProvider.notifier).state =
SignatureCard.initial();
container.read(currentRectProvider.notifier).state = null;
container.read(editingEnabledProvider.notifier).state = false;
container.read(aspectLockedProvider.notifier).state = false;
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 5);
}

View File

@ -10,6 +10,6 @@ Future<void> aSampleMultipageDocument5PagesIsAvailable(
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.openPicked(path: 'sample.pdf', pageCount: 5);
}

View File

@ -3,8 +3,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: a signature asset is created
@ -13,9 +13,9 @@ Future<void> aSignatureAssetIsCreated(WidgetTester tester) async {
TestWorld.container = container;
// Ensure PDF is open
if (!container.read(pdfProvider).loaded) {
if (!container.read(documentRepositoryProvider).loaded) {
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 5);
}
@ -25,12 +25,12 @@ Future<void> aSignatureAssetIsCreated(WidgetTester tester) async {
bytes: Uint8List(100),
name: 'Test Asset',
);
container.read(signatureLibraryProvider.notifier).state = [asset];
container.read(signatureAssetRepositoryProvider.notifier).state = [asset];
// Place it on the current page
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: pdf.currentPage,
rect: Rect.fromLTWH(50, 50, 100, 50),

View File

@ -1,21 +1,26 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/repositories/signature_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: a signature asset is loaded or drawn
Future<void> aSignatureAssetIsLoadedOrDrawn(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container.read(signatureLibraryProvider.notifier).state = [];
container.read(pdfProvider.notifier).state = PdfState.initial();
container.read(signatureProvider.notifier).state = SignatureState.initial();
container.read(signatureAssetRepositoryProvider.notifier).state = [];
container.read(documentRepositoryProvider.notifier).state =
Document.initial();
container.read(signatureCardProvider.notifier).state =
SignatureCard.initial();
container.read(currentRectProvider.notifier).state = null;
container.read(editingEnabledProvider.notifier).state = false;
container.read(aspectLockedProvider.notifier).state = false;
final bytes = Uint8List.fromList([1, 2, 3, 4, 5]);
container
.read(signatureLibraryProvider.notifier)
.read(signatureAssetRepositoryProvider.notifier)
.add(bytes, name: 'test.png');
}

View File

@ -3,8 +3,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: a signature asset is placed on the page
@ -13,31 +13,31 @@ Future<void> aSignatureAssetIsPlacedOnThePage(WidgetTester tester) async {
TestWorld.container = container;
// Ensure PDF is open
if (!container.read(pdfProvider).loaded) {
if (!container.read(documentRepositoryProvider).loaded) {
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 5);
}
// Get or create an asset
var library = container.read(signatureLibraryProvider);
var library = container.read(signatureAssetRepositoryProvider);
SignatureAsset asset;
if (library.isNotEmpty) {
asset = library.first;
} else {
final bytes = Uint8List.fromList([1, 2, 3, 4, 5]);
final id = container
.read(signatureLibraryProvider.notifier)
.read(signatureAssetRepositoryProvider.notifier)
.add(bytes, name: 'test.png');
asset = container
.read(signatureLibraryProvider)
.read(signatureAssetRepositoryProvider)
.firstWhere((a) => a.id == id);
}
// Place it on the current page
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: pdf.currentPage,
rect: Rect.fromLTWH(50, 50, 100, 50),

View File

@ -1,15 +1,15 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: a signature asset is selected
Future<void> aSignatureAssetIsSelected(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
var library = container.read(signatureLibraryProvider);
var library = container.read(signatureAssetRepositoryProvider);
// If library is empty, add a dummy asset
if (library.isEmpty) {
@ -18,9 +18,9 @@ Future<void> aSignatureAssetIsSelected(WidgetTester tester) async {
bytes: Uint8List(100),
name: 'Selected Asset',
);
container.read(signatureLibraryProvider.notifier).state = [asset];
container.read(signatureAssetRepositoryProvider.notifier).state = [asset];
// Re-read the library
library = container.read(signatureLibraryProvider);
library = container.read(signatureAssetRepositoryProvider);
}
expect(

View File

@ -1,10 +1,10 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/repositories/signature_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: a signature asset loaded or drawn is wrapped in a signature card
@ -13,11 +13,16 @@ Future<void> aSignatureAssetLoadedOrDrawnIsWrappedInASignatureCard(
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container.read(signatureLibraryProvider.notifier).state = [];
container.read(pdfProvider.notifier).state = PdfState.initial();
container.read(signatureProvider.notifier).state = SignatureState.initial();
container.read(signatureAssetRepositoryProvider.notifier).state = [];
container.read(documentRepositoryProvider.notifier).state =
Document.initial();
container.read(signatureCardProvider.notifier).state =
SignatureCard.initial();
container.read(currentRectProvider.notifier).state = null;
container.read(editingEnabledProvider.notifier).state = false;
container.read(aspectLockedProvider.notifier).state = false;
final bytes = Uint8List.fromList([1, 2, 3, 4, 5]);
container
.read(signatureLibraryProvider.notifier)
.read(signatureAssetRepositoryProvider.notifier)
.add(bytes, name: 'test.png');
}

View File

@ -7,7 +7,7 @@ Future<void> aSignaturePlacementAppearsOnThePageBasedOnTheSignatureCard(
WidgetTester tester,
) async {
final container = TestWorld.container!;
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];
expect(
placements.isNotEmpty,

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: a signature placement is placed on page {2}
@ -15,7 +15,7 @@ Future<void> aSignaturePlacementIsPlacedOnPage(
TestWorld.container = container;
final page = param1.toInt();
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: page,
rect: Rect.fromLTWH(20, 20, 100, 50),

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: a signature placement is placed with a position and size relative to the page
@ -12,9 +12,9 @@ Future<void> aSignaturePlacementIsPlacedWithAPositionAndSizeRelativeToThePage(
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: pdf.currentPage,
rect: Rect.fromLTWH(50, 50, 200, 100),

View File

@ -1,7 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: adjusting one instance does not affect the others
@ -9,13 +9,19 @@ Future<void> adjustingOneInstanceDoesNotAffectTheOthers(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final before = container.read(pdfProvider.notifier).placementsOn(2);
final before = container
.read(documentRepositoryProvider.notifier)
.placementsOn(2);
expect(before.length, greaterThanOrEqualTo(2));
final modified = before[0].rect.translate(5, 0).inflate(3);
container.read(pdfProvider.notifier).removePlacement(page: 2, index: 0);
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.removePlacement(page: 2, index: 0);
container
.read(documentRepositoryProvider.notifier)
.addPlacement(page: 2, rect: modified, asset: before[0].asset);
final after = container.read(pdfProvider.notifier).placementsOn(2);
final after = container
.read(documentRepositoryProvider.notifier)
.placementsOn(2);
expect(after.any((p) => p.rect == before[1].rect), isTrue);
}

View File

@ -7,7 +7,7 @@ Future<void> adjustingOneOfTheSignaturePlacementsDoesNotAffectTheOthers(
WidgetTester tester,
) async {
final container = TestWorld.container!;
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
final placements =
pdf.placementsByPage.values.expand((list) => list).toList();

View File

@ -9,7 +9,7 @@ allPlacedSignaturePlacementsAppearOnTheirCorrespondingPagesInTheOutput(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
final totalPlacements = pdf.placementsByPage.values.fold(
0,
(sum, list) => sum + list.length,

View File

@ -8,7 +8,7 @@ Future<void> bothSignaturePlacementsAreShownOnTheirRespectivePages(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
expect(pdf.placementsByPage[1], isNotEmpty);
expect(pdf.placementsByPage[3], isNotEmpty);
}

View File

@ -2,7 +2,7 @@ import 'dart:ui';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: dragging or resizing one does not change the other
@ -10,20 +10,26 @@ Future<void> draggingOrResizingOneDoesNotChangeTheOther(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final list = container.read(pdfProvider.notifier).placementsOn(1);
final list = container
.read(documentRepositoryProvider.notifier)
.placementsOn(1);
expect(list.length, greaterThanOrEqualTo(2));
final before = List<Rect>.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)
.read(documentRepositoryProvider.notifier)
.removePlacement(page: 1, index: 0);
container
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: 1,
rect: changed,
asset: list[1].asset,
rotationDeg: list[1].rotationDeg,
);
final after = container.read(pdfProvider.notifier).placementsOn(1);
final after = container
.read(documentRepositoryProvider.notifier)
.placementsOn(1);
expect(after.any((p) => p.rect == before[1]), isTrue);
}

View File

@ -8,7 +8,7 @@ Future<void> eachSignaturePlacementCanBeDraggedAndResizedIndependently(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];
expect(placements.length, greaterThan(1));
}

View File

@ -9,7 +9,7 @@ Future<void> identicalSignatureInstancesAppearInEachLocation(
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final state = container.read(pdfProvider);
final state = container.read(documentRepositoryProvider);
final p2 = state.placementsByPage[2] ?? const [];
final p4 = state.placementsByPage[4] ?? const [];
expect(p2.length, greaterThanOrEqualTo(2));

View File

@ -8,7 +8,7 @@ Future<void> identicalSignaturePlacementsAppearInEachLocation(
WidgetTester tester,
) async {
final container = TestWorld.container!;
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
final allPlacements =
pdf.placementsByPage.values.expand((list) => list).toList();
final assetIds = allPlacements.map((p) => p.asset.id).toSet();

View File

@ -8,7 +8,7 @@ Future<void> onlyTheSelectedSignaturePlacementIsRemoved(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];
expect(placements.length, 2); // Started with 3, removed 1, should have 2
}

View File

@ -10,5 +10,5 @@ Future<void> pageBecomesVisibleInTheScrollArea(
) async {
final page = param1.toInt();
final c = TestWorld.container ?? ProviderContainer();
expect(c.read(pdfProvider).currentPage, page);
expect(c.read(documentRepositoryProvider).currentPage, page);
}

View File

@ -7,5 +7,5 @@ import '_world.dart';
Future<void> pageIsDisplayed(WidgetTester tester, num param1) async {
final expected = param1.toInt();
final c = TestWorld.container ?? ProviderContainer();
expect(c.read(pdfProvider).currentPage, expected);
expect(c.read(documentRepositoryProvider).currentPage, expected);
}

View File

@ -6,7 +6,7 @@ import '_world.dart';
/// Usage: resize to fit within bounding box
Future<void> resizeToFitWithinBoundingBox(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
if (pdf.selectedPlacementIndex != null) {
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];

View File

@ -9,7 +9,7 @@ Future<void> signaturePlacementOccursOnTheSelectedPage(
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
// Check that there's at least one placement on the current page
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import '_world.dart';
/// Usage: the app attempts to load the asset
@ -8,6 +8,6 @@ Future<void> theAppAttemptsToLoadTheAsset(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
// Simulate attempting to load an asset - for now just ensure library is accessible
final library = container.read(signatureLibraryProvider);
final library = container.read(signatureAssetRepositoryProvider);
expect(library, isNotNull);
}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import '_world.dart';
/// Usage: the asset is loaded and shown as a signature asset
@ -7,7 +7,7 @@ Future<void> theAssetIsLoadedAndShownAsASignatureAsset(
WidgetTester tester,
) async {
final container = TestWorld.container!;
final library = container.read(signatureLibraryProvider);
final library = container.read(signatureAssetRepositoryProvider);
expect(
library.isNotEmpty,
true,

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import '_world.dart';
/// Usage: the asset is loaded and shown as a signature card
@ -7,7 +7,7 @@ Future<void> theAssetIsLoadedAndShownAsASignatureCard(
WidgetTester tester,
) async {
final container = TestWorld.container!;
final library = container.read(signatureLibraryProvider);
final library = container.read(signatureAssetRepositoryProvider);
expect(
library.isNotEmpty,
true,

View File

@ -1,11 +1,11 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import '_world.dart';
/// Usage: the asset is not added to the document
Future<void> theAssetIsNotAddedToTheDocument(WidgetTester tester) async {
final container = TestWorld.container!;
final library = container.read(signatureLibraryProvider);
final library = container.read(signatureAssetRepositoryProvider);
expect(
library.isEmpty,
true,

View File

@ -7,7 +7,7 @@ import '_world.dart';
Future<void> theDocumentIsOpen(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
expect(pdf.loaded, isTrue);
expect(pdf.pageCount, greaterThan(0));
}

View File

@ -6,6 +6,6 @@ import '_world.dart';
/// Usage: the first page is displayed
Future<void> theFirstPageIsDisplayed(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
expect(pdf.currentPage, 1);
}

View File

@ -7,9 +7,9 @@ import '_world.dart';
Future<void> theGoToInputCannotBeUsed(WidgetTester tester) async {
final c = TestWorld.container ?? ProviderContainer();
// Not loaded, currentPage should remain 1 even after jump attempt
expect(c.read(pdfProvider).loaded, isFalse);
final before = c.read(pdfProvider).currentPage;
c.read(pdfProvider.notifier).jumpTo(3);
final after = c.read(pdfProvider).currentPage;
expect(c.read(documentRepositoryProvider).loaded, isFalse);
final before = c.read(documentRepositoryProvider).currentPage;
c.read(documentRepositoryProvider.notifier).jumpTo(3);
final after = c.read(documentRepositoryProvider).currentPage;
expect(before, equals(after));
}

View File

@ -7,7 +7,7 @@ import '_world.dart';
Future<void> theLastPageIsDisplayedPage(WidgetTester tester, num param1) async {
final last = param1.toInt();
final c = TestWorld.container ?? ProviderContainer();
final pdf = c.read(pdfProvider);
final pdf = c.read(documentRepositoryProvider);
expect(pdf.pageCount, last);
expect(pdf.currentPage, last);
}

View File

@ -10,5 +10,5 @@ Future<void> theLeftPagesOverviewHighlightsPage(
) async {
final n = param1.toInt();
final c = TestWorld.container ?? ProviderContainer();
expect(c.read(pdfProvider).currentPage, n);
expect(c.read(documentRepositoryProvider).currentPage, n);
}

View File

@ -7,7 +7,7 @@ Future<void> theOtherSignaturePlacementsRemainUnchanged(
WidgetTester tester,
) async {
final container = TestWorld.container!;
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];
expect(placements.length, 2); // Should have 2 remaining after deleting 1
}

View File

@ -12,7 +12,7 @@ Future<void> thePageLabelShowsPageOf(
final current = param1.toInt();
final total = param2.toInt();
final c = TestWorld.container ?? ProviderContainer();
final pdf = c.read(pdfProvider);
final pdf = c.read(documentRepositoryProvider);
expect(pdf.currentPage, current);
expect(pdf.pageCount, total);
}

View File

@ -10,7 +10,7 @@ Future<void> theSignaturePlacementIsStampedAtTheExactPdfPageCoordinatesAndSize(
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final pdfState = container.read(pdfProvider);
final pdfState = container.read(documentRepositoryProvider);
// Verify PDF is loaded
expect(pdfState.loaded, isTrue, reason: 'PDF should be loaded');

View File

@ -10,7 +10,7 @@ Future<void> theSignaturePlacementOnPageIsShownOnPage(
num param2,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
final page = param1.toInt();
expect(pdf.placementsByPage[page], isNotEmpty);
}

View File

@ -9,7 +9,7 @@ Future<void> theSignaturePlacementOnPageRemains(
num param1,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
final page = param1.toInt();
expect(pdf.placementsByPage[page], isNotEmpty);
}

View File

@ -8,7 +8,7 @@ Future<void> theSignaturePlacementRemainsWithinThePageArea(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];
for (final placement in placements) {

View File

@ -8,7 +8,7 @@ Future<void> theSignaturePlacementRotatesAroundItsCenterInRealTime(
WidgetTester tester,
) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
if (pdf.selectedPlacementIndex != null) {
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];

View File

@ -10,7 +10,7 @@ Future<void> theSignaturePlacementsAppearOnTheCorrespondingPageInTheOutput(
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final pdfState = container.read(pdfProvider);
final pdfState = container.read(documentRepositoryProvider);
// Verify that export was successful
expect(

View File

@ -6,7 +6,7 @@ import '_world.dart';
/// Usage: the size and position update in real time
Future<void> theSizeAndPositionUpdateInRealTime(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
if (pdf.selectedPlacementIndex != null) {
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];

View File

@ -8,7 +8,7 @@ import '_world.dart';
Future<void> theUserAttemptsToSave(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
final sig = container.read(signatureProvider);
// Simulate save attempt: since rect is null, mark flag
if (!pdf.loaded || sig.rect == null) {

View File

@ -6,11 +6,11 @@ import '_world.dart';
/// Usage: the user can move to the next or previous page
Future<void> theUserCanMoveToTheNextOrPreviousPage(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
final pdfN = container.read(pdfProvider.notifier);
final pdf = container.read(pdfProvider);
final pdfN = container.read(documentRepositoryProvider.notifier);
final pdf = container.read(documentRepositoryProvider);
expect(pdf.currentPage, 1);
pdfN.jumpTo(2);
expect(container.read(pdfProvider).currentPage, 2);
expect(container.read(documentRepositoryProvider).currentPage, 2);
pdfN.jumpTo(1);
expect(container.read(pdfProvider).currentPage, 1);
expect(container.read(documentRepositoryProvider).currentPage, 1);
}

View File

@ -1,7 +1,7 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import '_world.dart';
/// Usage: the user chooses a image file as a signature asset
@ -12,6 +12,6 @@ Future<void> theUserChoosesAImageFileAsASignatureAsset(
TestWorld.container = container;
final bytes = Uint8List.fromList([1, 2, 3, 4, 5]);
container
.read(signatureLibraryProvider.notifier)
.read(signatureAssetRepositoryProvider.notifier)
.add(bytes, name: 'chosen.png');
}

View File

@ -1,7 +1,7 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import '_world.dart';
/// Usage: the user chooses a signature asset to created a signature card
@ -12,6 +12,6 @@ Future<void> theUserChoosesASignatureAssetToCreatedASignatureCard(
TestWorld.container = container;
final bytes = Uint8List.fromList([1, 2, 3, 4, 5]);
container
.read(signatureLibraryProvider.notifier)
.read(signatureAssetRepositoryProvider.notifier)
.add(bytes, name: 'card.png');
}

View File

@ -8,7 +8,7 @@ Future<void> theUserClicksTheGoToApplyButton(WidgetTester tester) async {
final c = TestWorld.container ?? ProviderContainer();
final pending = TestWorld.pendingGoTo;
if (pending != null) {
c.read(pdfProvider.notifier).jumpTo(pending);
c.read(documentRepositoryProvider.notifier).jumpTo(pending);
await tester.pump();
}
}

View File

@ -10,6 +10,6 @@ Future<void> theUserClicksTheThumbnailForPage(
) async {
final page = param1.toInt();
final c = TestWorld.container ?? ProviderContainer();
c.read(pdfProvider.notifier).jumpTo(page);
c.read(documentRepositoryProvider.notifier).jumpTo(page);
await tester.pump();
}

View File

@ -9,9 +9,9 @@ Future<void> theUserDeletesOneSelectedSignaturePlacement(
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
if (pdf.selectedPlacementIndex == null) {
container.read(pdfProvider.notifier).selectPlacement(0);
container.read(documentRepositoryProvider.notifier).selectPlacement(0);
}
container.read(pdfProvider.notifier).deleteSelectedPlacement();
container.read(documentRepositoryProvider.notifier).deleteSelectedPlacement();
}

View File

@ -10,8 +10,8 @@ Future<void> theUserDragsHandlesToResizeAndDragsToReposition(
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final pdf = container.read(pdfProvider);
final pdfN = container.read(pdfProvider.notifier);
final pdf = container.read(documentRepositoryProvider);
final pdfN = container.read(documentRepositoryProvider.notifier);
if (pdf.selectedPlacementIndex != null) {
final placements = pdf.placementsByPage[pdf.currentPage] ?? [];

View File

@ -3,8 +3,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: the user drags it on the page of the document to place signature placements in multiple locations in the document
@ -13,7 +13,7 @@ theUserDragsItOnThePageOfTheDocumentToPlaceSignaturePlacementsInMultipleLocation
WidgetTester tester,
) async {
final container = TestWorld.container!;
final lib = container.read(signatureLibraryProvider);
final lib = container.read(signatureAssetRepositoryProvider);
final asset =
lib.isNotEmpty
? lib.first
@ -24,28 +24,28 @@ theUserDragsItOnThePageOfTheDocumentToPlaceSignaturePlacementsInMultipleLocation
);
// Ensure PDF is open
if (!container.read(pdfProvider).loaded) {
if (!container.read(documentRepositoryProvider).loaded) {
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 5);
}
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: 1,
rect: Rect.fromLTWH(10, 10, 100, 50),
asset: asset,
);
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: 2,
rect: Rect.fromLTWH(20, 20, 100, 50),
asset: asset,
);
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: 3,
rect: Rect.fromLTWH(30, 30, 100, 50),

View File

@ -3,8 +3,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: the user drags this signature card on the page of the document to place a signature placement
@ -16,31 +16,31 @@ theUserDragsThisSignatureCardOnThePageOfTheDocumentToPlaceASignaturePlacement(
TestWorld.container = container;
// Ensure PDF is open
if (!container.read(pdfProvider).loaded) {
if (!container.read(documentRepositoryProvider).loaded) {
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 5);
}
// Get or create an asset
var library = container.read(signatureLibraryProvider);
var library = container.read(signatureAssetRepositoryProvider);
SignatureAsset asset;
if (library.isNotEmpty) {
asset = library.first;
} else {
final bytes = Uint8List.fromList([1, 2, 3, 4, 5]);
final id = container
.read(signatureLibraryProvider.notifier)
.read(signatureAssetRepositoryProvider.notifier)
.add(bytes, name: 'placement.png');
asset = container
.read(signatureLibraryProvider)
.read(signatureAssetRepositoryProvider)
.firstWhere((a) => a.id == id);
}
// Place it on the current page
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: pdf.currentPage,
rect: Rect.fromLTWH(100, 100, 100, 50),

View File

@ -10,6 +10,6 @@ Future<void> theUserEntersIntoTheGoToInputAndAppliesIt(
) async {
final value = param1.toInt();
final c = TestWorld.container ?? ProviderContainer();
c.read(pdfProvider.notifier).jumpTo(value);
c.read(documentRepositoryProvider.notifier).jumpTo(value);
await tester.pump();
}

View File

@ -7,6 +7,6 @@ import '_world.dart';
Future<void> theUserJumpsToPage(WidgetTester tester, num param1) async {
final page = param1.toInt();
final c = TestWorld.container ?? ProviderContainer();
c.read(pdfProvider.notifier).jumpTo(page);
c.read(documentRepositoryProvider.notifier).jumpTo(page);
await tester.pump();
}

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: the user navigates to page {5} and places another signature placement
@ -14,9 +14,9 @@ Future<void> theUserNavigatesToPageAndPlacesAnotherSignaturePlacement(
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final page = param1.toInt();
container.read(pdfProvider.notifier).jumpTo(page);
container.read(documentRepositoryProvider.notifier).jumpTo(page);
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: page,
rect: Rect.fromLTWH(40, 40, 100, 50),

View File

@ -3,8 +3,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: the user places a signature placement from asset <second_asset> on page <second_page>
@ -15,14 +15,14 @@ Future<void> theUserPlacesASignaturePlacementFromAssetOnPage(
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final library = container.read(signatureLibraryProvider);
final library = container.read(signatureAssetRepositoryProvider);
var asset = library.where((a) => a.name == assetName).firstOrNull;
if (asset == null) {
// add dummy asset
final id = container
.read(signatureLibraryProvider.notifier)
.read(signatureAssetRepositoryProvider.notifier)
.add(Uint8List(0), name: assetName);
final updatedLibrary = container.read(signatureLibraryProvider);
final updatedLibrary = container.read(signatureAssetRepositoryProvider);
asset = updatedLibrary.firstWhere(
(a) => a.id == id,
orElse:
@ -30,7 +30,7 @@ Future<void> theUserPlacesASignaturePlacementFromAssetOnPage(
);
}
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: page,
rect: Rect.fromLTWH(10, 10, 50, 50),

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: the user places a signature placement on page {1}
@ -15,7 +15,7 @@ Future<void> theUserPlacesASignaturePlacementOnPage(
TestWorld.container = container;
final page = param1.toInt();
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: page,
rect: Rect.fromLTWH(20, 20, 100, 50),

View File

@ -10,7 +10,7 @@ Future<void> theUserPlacesItInMultipleLocationsInTheDocument(
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final notifier = container.read(pdfProvider.notifier);
final notifier = container.read(documentRepositoryProvider.notifier);
// Always open a fresh doc to avoid state bleed between scenarios
notifier.openPicked(path: 'mock.pdf', pageCount: 6);
// Place two on page 2 and one on page 4

View File

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: the user places two signature placements on the same page
@ -12,10 +12,10 @@ Future<void> theUserPlacesTwoSignaturePlacementsOnTheSamePage(
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
final page = pdf.currentPage;
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: page,
rect: Rect.fromLTWH(10, 10, 100, 50),
@ -26,7 +26,7 @@ Future<void> theUserPlacesTwoSignaturePlacementsOnTheSamePage(
),
);
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: page,
rect: Rect.fromLTWH(120, 10, 100, 50),

View File

@ -12,7 +12,7 @@ Future<void> theUserSavesexportsTheDocument(WidgetTester tester) async {
TestWorld.container = container;
// Ensure state looks exportable
final pdf = container.read(pdfProvider);
final pdf = container.read(documentRepositoryProvider);
final sig = container.read(signatureProvider);
expect(pdf.loaded, isTrue, reason: 'PDF must be loaded before export');
// Check if there are placements

View File

@ -11,9 +11,9 @@ Future<void> theUserSelects(WidgetTester tester, dynamic file) async {
TestWorld.container = container;
// Mark page for signing to enable signature ops
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 1);
container.read(pdfProvider.notifier).setSignedPage(1);
container.read(documentRepositoryProvider.notifier).setSignedPage(1);
// For invalid/unsupported/empty selections we do NOT set image bytes.
// This simulates a failed load and keeps rect null.
final token = file.toString();

View File

@ -11,6 +11,6 @@ Future<void> theUserTypesIntoTheGoToInputAndPressesEnter(
final target = param1.toInt();
final c = TestWorld.container ?? ProviderContainer();
TestWorld.container = c;
c.read(pdfProvider.notifier).jumpTo(target);
c.read(documentRepositoryProvider.notifier).jumpTo(target);
await tester.pump();
}

View File

@ -6,8 +6,8 @@ import '_world.dart';
/// Usage: the user uses rotate controls
Future<void> theUserUsesRotateControls(WidgetTester tester) async {
final container = TestWorld.container ?? ProviderContainer();
final pdf = container.read(pdfProvider);
final pdfN = container.read(pdfProvider.notifier);
final pdf = container.read(documentRepositoryProvider);
final pdfN = container.read(documentRepositoryProvider.notifier);
if (pdf.selectedPlacementIndex != null) {
// Rotate the selected placement by 45 degrees

View File

@ -3,9 +3,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/repositories/signature_library_repository.dart';
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
import 'package:pdf_signature/data/repositories/signature_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
import '_world.dart';
/// Usage: three signature placements are placed on the current page
@ -14,14 +14,19 @@ Future<void> threeSignaturePlacementsArePlacedOnTheCurrentPage(
) async {
final container = TestWorld.container ?? ProviderContainer();
TestWorld.container = container;
container.read(signatureLibraryProvider.notifier).state = [];
container.read(pdfProvider.notifier).state = PdfState.initial();
container.read(signatureProvider.notifier).state = SignatureState.initial();
container.read(signatureAssetRepositoryProvider.notifier).state = [];
container.read(documentRepositoryProvider.notifier).state =
Document.initial();
container.read(signatureCardProvider.notifier).state =
SignatureCard.initial();
container.read(currentRectProvider.notifier).state = null;
container.read(editingEnabledProvider.notifier).state = false;
container.read(aspectLockedProvider.notifier).state = false;
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.openPicked(path: 'mock.pdf', pageCount: 5);
final pdfN = container.read(pdfProvider.notifier);
final pdf = container.read(pdfProvider);
final pdfN = container.read(documentRepositoryProvider.notifier);
final pdf = container.read(documentRepositoryProvider);
final page = pdf.currentPage;
pdfN.addPlacement(
page: page,

View File

@ -26,8 +26,8 @@ void main() {
await tester.pumpWidget(
ProviderScope(
overrides: [
pdfProvider.overrideWith(
(ref) => PdfController()..openPicked(path: 'test.pdf'),
documentRepositoryProvider.overrideWith(
(ref) => DocumentStateNotifier()..openPicked(path: 'test.pdf'),
),
signatureProvider.overrideWith(
(ref) => SignatureController()..placeDefaultRect(),

View File

@ -15,8 +15,8 @@ Future<void> pumpWithOpenPdf(WidgetTester tester) async {
await tester.pumpWidget(
ProviderScope(
overrides: [
pdfProvider.overrideWith(
(ref) => PdfController()..openPicked(path: 'test.pdf'),
documentRepositoryProvider.overrideWith(
(ref) => DocumentStateNotifier()..openPicked(path: 'test.pdf'),
),
useMockViewerProvider.overrideWith((ref) => true),
// Continuous mode is always-on; no page view override needed
@ -49,8 +49,8 @@ Future<void> pumpWithOpenPdfAndSig(WidgetTester tester) async {
await tester.pumpWidget(
ProviderScope(
overrides: [
pdfProvider.overrideWith(
(ref) => PdfController()..openPicked(path: 'test.pdf'),
documentRepositoryProvider.overrideWith(
(ref) => DocumentStateNotifier()..openPicked(path: 'test.pdf'),
),
signatureProvider.overrideWith(
(ref) =>

View File

@ -4,14 +4,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
import 'package:pdf_signature/data/services/export_providers.dart';
import 'package:pdf_signature/l10n/app_localizations.dart';
class _TestPdfController extends PdfController {
class _TestPdfController extends DocumentStateNotifier {
_TestPdfController() : super() {
// Start with a loaded multi-page doc, page 1 of 5
state = PdfState.initial().copyWith(
state = Document.initial().copyWith(
loaded: true,
pageCount: 5,
currentPage: 1,
@ -27,7 +27,9 @@ void main() {
ProviderScope(
overrides: [
useMockViewerProvider.overrideWithValue(true),
pdfProvider.overrideWith((ref) => _TestPdfController()),
documentRepositoryProvider.overrideWith(
(ref) => _TestPdfController(),
),
],
child: MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,

View File

@ -6,11 +6,11 @@ import 'package:pdf_signature/ui/features/pdf/widgets/pdf_page_area.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/services/export_providers.dart';
import 'package:pdf_signature/l10n/app_localizations.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
class _TestPdfController extends PdfController {
class _TestPdfController extends DocumentStateNotifier {
_TestPdfController() : super() {
state = PdfState.initial().copyWith(
state = Document.initial().copyWith(
loaded: true,
pageCount: 6,
currentPage: 1,
@ -30,7 +30,7 @@ void main() {
overrides: [
useMockViewerProvider.overrideWithValue(true),
// Continuous mode is always-on; no page view override needed
pdfProvider.overrideWith((ref) => ctrl),
documentRepositoryProvider.overrideWith((ref) => ctrl),
],
child: MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,

View File

@ -6,11 +6,11 @@ import 'package:pdf_signature/ui/features/pdf/widgets/pdf_page_area.dart';
import 'package:pdf_signature/data/repositories/pdf_repository.dart';
import 'package:pdf_signature/data/services/export_providers.dart';
import 'package:pdf_signature/l10n/app_localizations.dart';
import 'package:pdf_signature/data/model/model.dart';
import 'package:pdf_signature/domain/models/model.dart';
class _TestPdfController extends PdfController {
class _TestPdfController extends DocumentStateNotifier {
_TestPdfController() : super() {
state = PdfState.initial().copyWith(
state = Document.initial().copyWith(
loaded: true,
pageCount: 6,
currentPage: 2,
@ -29,7 +29,7 @@ void main() {
overrides: [
useMockViewerProvider.overrideWithValue(true),
// Continuous mode is always-on; no page view override needed
pdfProvider.overrideWith((ref) => ctrl),
documentRepositoryProvider.overrideWith((ref) => ctrl),
],
child: MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,

View File

@ -53,10 +53,10 @@ void main() {
await tester.pumpWidget(buildHarness(width: 480));
// Open sample and add a normalized placement to page 1
container.read(pdfProvider.notifier).openSample();
container.read(documentRepositoryProvider.notifier).openSample();
// One placement at (25% x, 50% y), size 10% x 10%
container
.read(pdfProvider.notifier)
.read(documentRepositoryProvider.notifier)
.addPlacement(
page: 1,
rect: const Rect.fromLTWH(0.25, 0.50, 0.10, 0.10),

Some files were not shown because too many files have changed in this diff Show More