chore: adopt MVVM and default `Package structure` at official

This commit is contained in:
insleker 2025-08-29 13:24:44 +08:00
parent 60630c6ddd
commit b8918717b5
84 changed files with 243 additions and 216 deletions

View File

@ -19,4 +19,6 @@ flutter run
flutter test
flutter build
# create windows installer
flutter pub run msix:create
```

4
docs/NFRs.md Normal file
View File

@ -0,0 +1,4 @@
# Non-Functional Requirements
* Package structure
* plz follow official [Package structure](https://docs.flutter.dev/app-architecture/case-study#package-structure) with a slight modification, put each `<FEATURE NAME>/`s in `features/` sub-directory under `ui/`.

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
class MyApp extends StatelessWidget {
const MyApp({super.key});

91
lib/data/model/model.dart Normal file
View File

@ -0,0 +1,91 @@
import 'dart:typed_data';
import 'package:flutter/widgets.dart';
class PdfState {
final bool loaded;
final int pageCount;
final int currentPage;
final bool markedForSigning;
final String? pickedPdfPath;
final Uint8List? pickedPdfBytes;
final int? signedPage;
const PdfState({
required this.loaded,
required this.pageCount,
required this.currentPage,
required this.markedForSigning,
this.pickedPdfPath,
this.pickedPdfBytes,
this.signedPage,
});
factory PdfState.initial() => const PdfState(
loaded: false,
pageCount: 0,
currentPage: 1,
markedForSigning: false,
pickedPdfBytes: null,
signedPage: null,
);
PdfState copyWith({
bool? loaded,
int? pageCount,
int? currentPage,
bool? markedForSigning,
String? pickedPdfPath,
Uint8List? pickedPdfBytes,
int? signedPage,
}) => PdfState(
loaded: loaded ?? this.loaded,
pageCount: pageCount ?? this.pageCount,
currentPage: currentPage ?? this.currentPage,
markedForSigning: markedForSigning ?? this.markedForSigning,
pickedPdfPath: pickedPdfPath ?? this.pickedPdfPath,
pickedPdfBytes: pickedPdfBytes ?? this.pickedPdfBytes,
signedPage: signedPage ?? this.signedPage,
);
}
class SignatureState {
final Rect? rect;
final bool aspectLocked;
final bool bgRemoval;
final double contrast;
final double brightness;
final List<List<Offset>> strokes;
final Uint8List? imageBytes;
const SignatureState({
required this.rect,
required this.aspectLocked,
required this.bgRemoval,
required this.contrast,
required this.brightness,
required this.strokes,
this.imageBytes,
});
factory SignatureState.initial() => const SignatureState(
rect: null,
aspectLocked: false,
bgRemoval: false,
contrast: 1.0,
brightness: 0.0,
strokes: [],
imageBytes: null,
);
SignatureState copyWith({
Rect? rect,
bool? aspectLocked,
bool? bgRemoval,
double? contrast,
double? brightness,
List<List<Offset>>? strokes,
Uint8List? imageBytes,
}) => SignatureState(
rect: rect ?? this.rect,
aspectLocked: aspectLocked ?? this.aspectLocked,
bgRemoval: bgRemoval ?? this.bgRemoval,
contrast: contrast ?? this.contrast,
brightness: brightness ?? this.brightness,
strokes: strokes ?? this.strokes,
imageBytes: imageBytes ?? this.imageBytes,
);
}

View File

@ -0,0 +1,45 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:path_provider/path_provider.dart' as pp;
import 'package:file_selector/file_selector.dart' as fs;
import 'package:pdf_signature/data/services/export_service.dart';
// Feature-scoped DI and configuration providers
// Toggle mock viewer (used by tests to show a gray placeholder instead of real PDF pages)
final useMockViewerProvider = Provider<bool>((_) => false);
// Export service injection for testability
final exportServiceProvider = Provider<ExportService>((_) => ExportService());
// Export DPI setting (points per inch mapping), default 144 DPI
final exportDpiProvider = StateProvider<double>((_) => 144.0);
// Controls whether signature overlay is visible (used to hide on non-stamped pages during export)
final signatureVisibilityProvider = StateProvider<bool>((_) => true);
// Save path picker (injected for tests)
final savePathPickerProvider = Provider<Future<String?> Function()>((ref) {
return () async {
String? initialDir;
try {
final d = await pp.getDownloadsDirectory();
initialDir = d?.path;
} catch (_) {}
if (initialDir == null) {
try {
final d = await pp.getApplicationDocumentsDirectory();
initialDir = d.path;
} catch (_) {}
}
final location = await fs.getSaveLocation(
suggestedName: 'signed.pdf',
acceptedTypeGroups: [
const fs.XTypeGroup(label: 'PDF', extensions: ['pdf']),
],
initialDirectory: initialDir,
);
if (location == null) return null;
final path = location.path;
return path.toLowerCase().endsWith('.pdf') ? path : '$path.pdf';
};
});

View File

@ -1,52 +1,14 @@
part of 'viewer.dart';
import 'dart:math' as math;
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class PdfState {
final bool loaded;
final int pageCount;
final int currentPage;
final bool markedForSigning;
final String? pickedPdfPath;
final Uint8List? pickedPdfBytes;
final int? signedPage;
const PdfState({
required this.loaded,
required this.pageCount,
required this.currentPage,
required this.markedForSigning,
this.pickedPdfPath,
this.pickedPdfBytes,
this.signedPage,
});
factory PdfState.initial() => const PdfState(
loaded: false,
pageCount: 0,
currentPage: 1,
markedForSigning: false,
pickedPdfBytes: null,
signedPage: null,
);
PdfState copyWith({
bool? loaded,
int? pageCount,
int? currentPage,
bool? markedForSigning,
String? pickedPdfPath,
Uint8List? pickedPdfBytes,
int? signedPage,
}) => PdfState(
loaded: loaded ?? this.loaded,
pageCount: pageCount ?? this.pageCount,
currentPage: currentPage ?? this.currentPage,
markedForSigning: markedForSigning ?? this.markedForSigning,
pickedPdfPath: pickedPdfPath ?? this.pickedPdfPath,
pickedPdfBytes: pickedPdfBytes ?? this.pickedPdfBytes,
signedPage: signedPage ?? this.signedPage,
);
}
import '../../../../data/model/model.dart';
class PdfController extends StateNotifier<PdfState> {
PdfController() : super(PdfState.initial());
static const int samplePageCount = 5;
void openSample() {
state = state.copyWith(
loaded: true,
@ -102,51 +64,6 @@ final pdfProvider = StateNotifierProvider<PdfController, PdfState>(
(ref) => PdfController(),
);
class SignatureState {
final Rect? rect;
final bool aspectLocked;
final bool bgRemoval;
final double contrast;
final double brightness;
final List<List<Offset>> strokes;
final Uint8List? imageBytes;
const SignatureState({
required this.rect,
required this.aspectLocked,
required this.bgRemoval,
required this.contrast,
required this.brightness,
required this.strokes,
this.imageBytes,
});
factory SignatureState.initial() => const SignatureState(
rect: null,
aspectLocked: false,
bgRemoval: false,
contrast: 1.0,
brightness: 0.0,
strokes: [],
imageBytes: null,
);
SignatureState copyWith({
Rect? rect,
bool? aspectLocked,
bool? bgRemoval,
double? contrast,
double? brightness,
List<List<Offset>>? strokes,
Uint8List? imageBytes,
}) => SignatureState(
rect: rect ?? this.rect,
aspectLocked: aspectLocked ?? this.aspectLocked,
bgRemoval: bgRemoval ?? this.bgRemoval,
contrast: contrast ?? this.contrast,
brightness: brightness ?? this.brightness,
strokes: strokes ?? this.strokes,
imageBytes: imageBytes ?? this.imageBytes,
);
}
class SignatureController extends StateNotifier<SignatureState> {
SignatureController() : super(SignatureState.initial());
static const Size pageSize = Size(400, 560);

View File

@ -1,4 +1,7 @@
part of 'viewer.dart';
import 'dart:math' as math;
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:hand_signature/signature.dart' as hand;
class DrawCanvas extends StatefulWidget {
const DrawCanvas({
@ -52,11 +55,8 @@ class _DrawCanvasState extends State<DrawCanvas> {
height: 512,
);
final bytes = data?.buffer.asUint8List();
// print("onPressed, Exported signature bytes: ${bytes?.length}");
// Notify tests if provided
widget.debugBytesSink?.value = bytes;
if (widget.onConfirm != null) {
// print("onConfirm callback called");
widget.onConfirm!(bytes);
} else {
if (context.mounted) {

View File

@ -1,51 +1,16 @@
import 'dart:math' as math;
import 'dart:typed_data';
import 'package:file_selector/file_selector.dart' as fs;
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdfrx/pdfrx.dart';
import 'package:path_provider/path_provider.dart' as pp;
import 'dart:typed_data';
import '../share/export_service.dart';
import 'package:hand_signature/signature.dart' as hand;
import 'package:printing/printing.dart' as printing;
part 'viewer_state.dart';
part 'viewer_widgets.dart';
final useMockViewerProvider = Provider<bool>((_) => false);
// Export service injection for testability
final exportServiceProvider = Provider<ExportService>((_) => ExportService());
// Export DPI setting (points per inch mapping), default 144 DPI
final exportDpiProvider = StateProvider<double>((_) => 144.0);
// Controls whether signature overlay is visible (used to hide on non-stamped pages during export)
final signatureVisibilityProvider = StateProvider<bool>((_) => true);
// Save path picker (injected for tests)
final savePathPickerProvider = Provider<Future<String?> Function()>((ref) {
return () async {
String? initialDir;
try {
final d = await pp.getDownloadsDirectory();
initialDir = d?.path;
} catch (_) {}
if (initialDir == null) {
try {
final d = await pp.getApplicationDocumentsDirectory();
initialDir = d.path;
} catch (_) {}
}
final location = await fs.getSaveLocation(
suggestedName: 'signed.pdf',
acceptedTypeGroups: [
const fs.XTypeGroup(label: 'PDF', extensions: ['pdf']),
],
initialDirectory: initialDir,
);
if (location == null) return null;
final path = location.path;
return path.toLowerCase().endsWith('.pdf') ? path : '$path.pdf';
};
});
import '../../../../data/model/model.dart';
import '../../../../data/services/providers.dart';
import '../view_model/view_model.dart';
import 'draw_canvas.dart';
class PdfSignatureHomePage extends ConsumerStatefulWidget {
const PdfSignatureHomePage({super.key});
@ -102,8 +67,6 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
sig.setImageBytes(bytes);
}
// removed invalid loader; not part of normal app
void _onDragSignature(Offset delta) {
ref.read(signatureProvider.notifier).drag(delta);
}
@ -239,8 +202,6 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
}
}
// Removed manual full-path dialog; using file_selector.getSaveLocation via provider
String _ensurePdfExtension(String name) {
if (!name.toLowerCase().endsWith('.pdf')) return '$name.pdf';
return name;

View File

@ -39,12 +39,13 @@ dependencies:
flutter_dotenv: ^6.0.0
file_selector: ^1.0.3
path_provider: ^2.1.5
pdfrx: ^1.3.5
pdfrx: ^2.1.9
pdf: ^3.10.8
hand_signature: ^3.1.0+2
image: ^4.2.0
printing: ^5.14.2
result_dart: ^2.1.1
go_router: ^16.2.0
dev_dependencies:
flutter_test:
@ -57,7 +58,9 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^5.0.0
flutter_lints: ^6.0.0
msix: ^3.16.12
json_serializable: ^6.11.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View File

@ -7,7 +7,7 @@ import 'package:image/image.dart' as img;
import 'package:pdf/pdf.dart' as pdf;
import 'package:pdf/widgets.dart' as pw;
import 'package:pdf_signature/features/share/export_service.dart';
import 'package:pdf_signature/data/services/export_service.dart';
void main() {
test(

View File

@ -2,7 +2,7 @@ import 'dart:typed_data';
import 'dart:ui' show Rect, Size;
import 'dart:io';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
// A lightweight fake exporter to avoid platform rasterization in tests.

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: a default signature rect is placed

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: a drawn signature exists in the canvas

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: a multi-page PDF is open

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: a PDF document is available

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/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: a PDF is open and contains at least one placed signature

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: a PDF is open with no signatures placed

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: a pdf is open with path {'test.pdf'} and pageCount {5}

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: a PDF page is selected for signing

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: a signature image is created

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/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: a signature image is placed on the page

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/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: a signature image is selected

View File

@ -2,7 +2,7 @@ import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: a signature is placed with a position and size relative to the page

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: an empty signature canvas

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: aspect lock is {true}

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: I drag signature by {Offset(10000, -10000)}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: I jumpTo {10}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: I openPicked with path {'test.pdf'} and pageCount {7}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: I place default signature rect

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: I resize signature by {Offset(1000, 1000)}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: I set page count {9}

View File

@ -1,6 +1,6 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: I set signature image bytes {Uint8List.fromList([0, 1, 2])}

View File

@ -1,6 +1,6 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: I set tiny signature image bytes

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: I toggle mark

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: it is placed on the selected page

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: multiple strokes were drawn

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: near-white background becomes transparent in the preview

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: pdf current page is {1}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: pdf marked for signing is {false}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: pdf page count is {7}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: pdf picked path is {'test.pdf'}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: pdf state is loaded {true}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: signature aspect ratio is preserved within {0.05}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: signature image bytes is not null

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: signature rect bottom <= {560}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: signature rect height > {20}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: signature rect is not null

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: signature rect is null

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: signature rect left >= {0}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: signature rect moved from center

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: signature rect right <= {400}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: signature rect top >= {0}

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: signature rect width > {50}

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: that page is displayed

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the canvas becomes blank

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the first page is displayed

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the image is loaded and shown as a signature asset

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the image is not added to the document

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the image scales proportionally

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the last stroke is removed

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the preview updates immediately

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the signature is stamped at the exact PDF page coordinates and size

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the signature remains within the page area

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the signatures appear on the corresponding page in the output

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the size and position update in real time

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user attempts to save

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user can apply or reset adjustments

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user can move to the next or previous page

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user changes contrast and brightness controls

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/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user chooses a signature image file

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user chooses undo

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user clears the canvas

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user drags handles to resize and drags to reposition

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/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user draws strokes and confirms

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user enables aspect ratio lock and resizes

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user enables background removal

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user is notified of the issue

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/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user saves/exports the document

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user selects "<file>"

View File

@ -1,6 +1,6 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import '_world.dart';
/// Usage: the user selects a specific page number

View File

@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {

View File

@ -2,7 +2,7 @@ import 'dart:typed_data';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
void main() {
test('placeDefaultRect centers a reasonable default rect', () {

View File

@ -11,8 +11,12 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'dart:typed_data';
import 'dart:ui' show PointerDeviceKind;
import 'package:pdf_signature/features/pdf/viewer.dart';
import 'package:pdf_signature/features/share/export_service.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/view_model.dart';
import 'package:pdf_signature/data/services/providers.dart';
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
import 'package:pdf_signature/ui/features/pdf/widgets/draw_canvas.dart';
import 'package:pdf_signature/data/services/export_service.dart';
import 'package:hand_signature/signature.dart' as hand;
// Fakes for export service (top-level; Dart does not allow local class declarations)