feat: migrate pdf state to viewmodel abstraction
This commit is contained in:
parent
7336ca4d57
commit
5549f08b4c
|
|
@ -3,3 +3,4 @@
|
||||||
* support multiple platforms (windows, linux, android, web)
|
* support multiple platforms (windows, linux, android, web)
|
||||||
* only FOSS libs can use
|
* only FOSS libs can use
|
||||||
* should not exceed 350 lines of code per file
|
* should not exceed 350 lines of code per file
|
||||||
|
* Direct Passing is better than Singleton(e.g.Provider) especially for `view`, `viewModel`.
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
import 'package:image/image.dart' as img;
|
import 'package:image/image.dart' as img;
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:file_selector/file_selector.dart' as fs;
|
||||||
|
|
||||||
import 'package:pdf_signature/data/services/export_service.dart';
|
import 'package:pdf_signature/data/services/export_service.dart';
|
||||||
|
|
||||||
|
|
@ -13,7 +14,6 @@ import 'package:pdf_signature/data/repositories/signature_card_repository.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
import 'package:pdf_signature/domain/models/model.dart';
|
import 'package:pdf_signature/domain/models/model.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/ui_services.dart';
|
import 'package:pdf_signature/ui/features/pdf/widgets/ui_services.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/pages_sidebar.dart';
|
import 'package:pdf_signature/ui/features/pdf/widgets/pages_sidebar.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
||||||
|
|
@ -50,17 +50,23 @@ void main() {
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) => DocumentStateNotifier()..openPicked(pageCount: 3),
|
(ref) => DocumentStateNotifier()..openPicked(pageCount: 3),
|
||||||
),
|
),
|
||||||
useMockViewerProvider.overrideWith((ref) => false),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: false),
|
||||||
|
),
|
||||||
exportServiceProvider.overrideWith((_) => fake),
|
exportServiceProvider.overrideWith((_) => fake),
|
||||||
savePathPickerProvider.overrideWith(
|
savePathPickerProvider.overrideWith(
|
||||||
(_) => () async => 'C:/tmp/output.pdf',
|
(_) => () async => 'C:/tmp/output.pdf',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: const MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
locale: Locale('en'),
|
locale: Locale('en'),
|
||||||
home: PdfSignatureHomePage(),
|
home: PdfSignatureHomePage(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onClosePdf: () {},
|
||||||
|
currentFile: fs.XFile('test.pdf'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -120,13 +126,19 @@ void main() {
|
||||||
cardRepo.addWithAsset(asset, 0.0);
|
cardRepo.addWithAsset(asset, 0.0);
|
||||||
return cardRepo;
|
return cardRepo;
|
||||||
}),
|
}),
|
||||||
useMockViewerProvider.overrideWithValue(false),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: false),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
locale: Locale('en'),
|
locale: Locale('en'),
|
||||||
home: PdfSignatureHomePage(),
|
home: PdfSignatureHomePage(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onClosePdf: () {},
|
||||||
|
currentFile: fs.XFile('test.pdf'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -145,17 +157,18 @@ void main() {
|
||||||
// Programmatically simulate confirm: add placement with current rect and bound image, then clear active overlay.
|
// Programmatically simulate confirm: add placement with current rect and bound image, then clear active overlay.
|
||||||
final ctx = tester.element(find.byType(PdfSignatureHomePage));
|
final ctx = tester.element(find.byType(PdfSignatureHomePage));
|
||||||
final container = ProviderScope.containerOf(ctx);
|
final container = ProviderScope.containerOf(ctx);
|
||||||
final r = container.read(activeRectProvider)!;
|
final r = container.read(pdfViewModelProvider).activeRect!;
|
||||||
final lib = container.read(signatureAssetRepositoryProvider);
|
final lib = container.read(signatureAssetRepositoryProvider);
|
||||||
final asset = lib.isNotEmpty ? lib.first : null;
|
final asset = lib.isNotEmpty ? lib.first : null;
|
||||||
final currentPage = container.read(pdfViewModelProvider);
|
final currentPage = container.read(pdfViewModelProvider).currentPage;
|
||||||
container
|
container
|
||||||
.read(documentRepositoryProvider.notifier)
|
.read(documentRepositoryProvider.notifier)
|
||||||
.addPlacement(page: currentPage, rect: r, asset: asset);
|
.addPlacement(page: currentPage, rect: r, asset: asset);
|
||||||
// Clear active overlay by hiding signatures temporarily
|
// Clear active overlay by hiding signatures temporarily
|
||||||
container.read(signatureVisibilityProvider.notifier).state = false;
|
// Note: signatureVisibilityProvider was removed in migration
|
||||||
|
// container.read(signatureVisibilityProvider.notifier).state = false;
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
container.read(signatureVisibilityProvider.notifier).state = true;
|
// container.read(signatureVisibilityProvider.notifier).state = true;
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
final placed = find.byKey(const Key('placed_signature_0'));
|
final placed = find.byKey(const Key('placed_signature_0'));
|
||||||
|
|
@ -192,13 +205,19 @@ void main() {
|
||||||
DocumentStateNotifier()
|
DocumentStateNotifier()
|
||||||
..openPicked(pageCount: 3, bytes: pdfBytes),
|
..openPicked(pageCount: 3, bytes: pdfBytes),
|
||||||
),
|
),
|
||||||
useMockViewerProvider.overrideWithValue(false),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: false),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
locale: Locale('en'),
|
locale: Locale('en'),
|
||||||
home: PdfSignatureHomePage(),
|
home: PdfSignatureHomePage(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onClosePdf: () {},
|
||||||
|
currentFile: fs.XFile('test.pdf'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -232,13 +251,19 @@ void main() {
|
||||||
DocumentStateNotifier()
|
DocumentStateNotifier()
|
||||||
..openPicked(pageCount: 3, bytes: pdfBytes),
|
..openPicked(pageCount: 3, bytes: pdfBytes),
|
||||||
),
|
),
|
||||||
useMockViewerProvider.overrideWithValue(false),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: false),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
locale: Locale('en'),
|
locale: Locale('en'),
|
||||||
home: PdfSignatureHomePage(),
|
home: PdfSignatureHomePage(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onClosePdf: () {},
|
||||||
|
currentFile: fs.XFile('test.pdf'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -275,13 +300,19 @@ void main() {
|
||||||
DocumentStateNotifier()
|
DocumentStateNotifier()
|
||||||
..openPicked(pageCount: 3, bytes: pdfBytes),
|
..openPicked(pageCount: 3, bytes: pdfBytes),
|
||||||
),
|
),
|
||||||
useMockViewerProvider.overrideWithValue(false),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: false),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
locale: Locale('en'),
|
locale: Locale('en'),
|
||||||
home: PdfSignatureHomePage(),
|
home: PdfSignatureHomePage(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onClosePdf: () {},
|
||||||
|
currentFile: fs.XFile('test.pdf'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -321,13 +352,19 @@ void main() {
|
||||||
DocumentStateNotifier()
|
DocumentStateNotifier()
|
||||||
..openPicked(pageCount: 3, bytes: pdfBytes),
|
..openPicked(pageCount: 3, bytes: pdfBytes),
|
||||||
),
|
),
|
||||||
useMockViewerProvider.overrideWithValue(false),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: false),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
locale: Locale('en'),
|
locale: Locale('en'),
|
||||||
home: PdfSignatureHomePage(),
|
home: PdfSignatureHomePage(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onClosePdf: () {},
|
||||||
|
currentFile: fs.XFile('test.pdf'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:file_selector/file_selector.dart' as fs;
|
||||||
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/pages_sidebar.dart';
|
import 'package:pdf_signature/ui/features/pdf/widgets/pages_sidebar.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
@ -37,13 +37,19 @@ void main() {
|
||||||
DocumentStateNotifier()
|
DocumentStateNotifier()
|
||||||
..openPicked(pageCount: 3, bytes: pdfBytes),
|
..openPicked(pageCount: 3, bytes: pdfBytes),
|
||||||
),
|
),
|
||||||
useMockViewerProvider.overrideWithValue(false),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: false),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
locale: Locale('en'),
|
locale: Locale('en'),
|
||||||
home: PdfSignatureHomePage(),
|
home: PdfSignatureHomePage(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onClosePdf: () {},
|
||||||
|
currentFile: fs.XFile('test.pdf'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -85,13 +91,19 @@ void main() {
|
||||||
DocumentStateNotifier()
|
DocumentStateNotifier()
|
||||||
..openPicked(pageCount: 3, bytes: pdfBytes),
|
..openPicked(pageCount: 3, bytes: pdfBytes),
|
||||||
),
|
),
|
||||||
useMockViewerProvider.overrideWithValue(false),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: false),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
locale: Locale('en'),
|
locale: Locale('en'),
|
||||||
home: PdfSignatureHomePage(),
|
home: PdfSignatureHomePage(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onClosePdf: () {},
|
||||||
|
currentFile: fs.XFile('test.pdf'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -133,13 +145,19 @@ void main() {
|
||||||
DocumentStateNotifier()
|
DocumentStateNotifier()
|
||||||
..openPicked(pageCount: 3, bytes: pdfBytes),
|
..openPicked(pageCount: 3, bytes: pdfBytes),
|
||||||
),
|
),
|
||||||
useMockViewerProvider.overrideWithValue(false),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: false),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
locale: Locale('en'),
|
locale: Locale('en'),
|
||||||
home: PdfSignatureHomePage(),
|
home: PdfSignatureHomePage(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onClosePdf: () {},
|
||||||
|
currentFile: fs.XFile('test.pdf'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -182,13 +200,19 @@ void main() {
|
||||||
DocumentStateNotifier()
|
DocumentStateNotifier()
|
||||||
..openPicked(pageCount: 3, bytes: pdfBytes),
|
..openPicked(pageCount: 3, bytes: pdfBytes),
|
||||||
),
|
),
|
||||||
useMockViewerProvider.overrideWithValue(false),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: false),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
locale: Locale('en'),
|
locale: Locale('en'),
|
||||||
home: PdfSignatureHomePage(),
|
home: PdfSignatureHomePage(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onClosePdf: () {},
|
||||||
|
currentFile: fs.XFile('test.pdf'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
61
lib/app.dart
61
lib/app.dart
|
|
@ -2,11 +2,9 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_localized_locales/flutter_localized_locales.dart';
|
import 'package:flutter_localized_locales/flutter_localized_locales.dart';
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
import 'package:pdf_signature/routing/router.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/welcome/widgets/welcome_screen.dart';
|
|
||||||
import 'data/repositories/preferences_repository.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/preferences/widgets/settings_screen.dart';
|
import 'package:pdf_signature/ui/features/preferences/widgets/settings_screen.dart';
|
||||||
|
import 'data/repositories/preferences_repository.dart';
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
@ -42,7 +40,7 @@ class MyApp extends StatelessWidget {
|
||||||
data: (_) {
|
data: (_) {
|
||||||
final themeMode = ref.watch(themeModeProvider);
|
final themeMode = ref.watch(themeModeProvider);
|
||||||
final appLocale = ref.watch(localeProvider);
|
final appLocale = ref.watch(localeProvider);
|
||||||
return MaterialApp(
|
return MaterialApp.router(
|
||||||
onGenerateTitle: (ctx) => AppLocalizations.of(ctx).appTitle,
|
onGenerateTitle: (ctx) => AppLocalizations.of(ctx).appTitle,
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
colorScheme: ColorScheme.fromSeed(
|
colorScheme: ColorScheme.fromSeed(
|
||||||
|
|
@ -63,27 +61,27 @@ class MyApp extends StatelessWidget {
|
||||||
...AppLocalizations.localizationsDelegates,
|
...AppLocalizations.localizationsDelegates,
|
||||||
LocaleNamesLocalizationsDelegate(),
|
LocaleNamesLocalizationsDelegate(),
|
||||||
],
|
],
|
||||||
home: Builder(
|
routerConfig: ref.watch(routerProvider),
|
||||||
builder:
|
builder: (context, child) {
|
||||||
(ctx) => Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(AppLocalizations.of(ctx).appTitle),
|
title: Text(AppLocalizations.of(context).appTitle),
|
||||||
actions: [
|
actions: [
|
||||||
OutlinedButton.icon(
|
OutlinedButton.icon(
|
||||||
key: const Key('btn_appbar_settings'),
|
key: const Key('btn_appbar_settings'),
|
||||||
icon: const Icon(Icons.settings),
|
icon: const Icon(Icons.settings),
|
||||||
label: Text(AppLocalizations.of(ctx).settings),
|
label: Text(AppLocalizations.of(context).settings),
|
||||||
onPressed:
|
onPressed:
|
||||||
() => showDialog<bool>(
|
() => showDialog<bool>(
|
||||||
context: ctx,
|
context: context,
|
||||||
builder: (_) => const SettingsDialog(),
|
builder: (_) => const SettingsDialog(),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
body: const _RootHomeSwitcher(),
|
],
|
||||||
),
|
),
|
||||||
),
|
body: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -92,16 +90,3 @@ class MyApp extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RootHomeSwitcher extends ConsumerWidget {
|
|
||||||
const _RootHomeSwitcher();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final pdf = ref.watch(documentRepositoryProvider);
|
|
||||||
if (!pdf.loaded) {
|
|
||||||
return const WelcomeScreen();
|
|
||||||
}
|
|
||||||
return const PdfSignatureHomePage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,7 @@ class DocumentStateNotifier extends StateNotifier<Document> {
|
||||||
state = state.copyWith(loaded: true, pageCount: 5, placementsByPage: {});
|
state = state.copyWith(loaded: true, pageCount: 5, placementsByPage: {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void openPicked({
|
void openPicked({required int pageCount, Uint8List? bytes}) {
|
||||||
required int pageCount,
|
|
||||||
Uint8List? bytes,
|
|
||||||
}) {
|
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
loaded: true,
|
loaded: true,
|
||||||
pageCount: pageCount,
|
pageCount: pageCount,
|
||||||
|
|
@ -27,6 +24,10 @@ class DocumentStateNotifier extends StateNotifier<Document> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void close() {
|
||||||
|
state = Document.initial();
|
||||||
|
}
|
||||||
|
|
||||||
void setPageCount(int count) {
|
void setPageCount(int count) {
|
||||||
if (!state.loaded) return;
|
if (!state.loaded) return;
|
||||||
state = state.copyWith(pageCount: count.clamp(1, 9999));
|
state = state.copyWith(pageCount: count.clamp(1, 9999));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/welcome/widgets/welcome_screen.dart';
|
||||||
|
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
|
import 'package:pdf_signature/data/repositories/signature_card_repository.dart';
|
||||||
|
import 'package:file_selector/file_selector.dart' as fs;
|
||||||
|
import 'package:pdfrx/pdfrx.dart';
|
||||||
|
|
||||||
|
class PdfManager {
|
||||||
|
final DocumentStateNotifier _documentNotifier;
|
||||||
|
final SignatureCardStateNotifier _signatureCardNotifier;
|
||||||
|
final GoRouter _router;
|
||||||
|
|
||||||
|
fs.XFile _currentFile = fs.XFile('');
|
||||||
|
|
||||||
|
PdfManager({
|
||||||
|
required DocumentStateNotifier documentNotifier,
|
||||||
|
required SignatureCardStateNotifier signatureCardNotifier,
|
||||||
|
required GoRouter router,
|
||||||
|
}) : _documentNotifier = documentNotifier,
|
||||||
|
_signatureCardNotifier = signatureCardNotifier,
|
||||||
|
_router = router;
|
||||||
|
|
||||||
|
fs.XFile get currentFile => _currentFile;
|
||||||
|
|
||||||
|
Future<void> openPdf({String? path, Uint8List? bytes}) async {
|
||||||
|
int pageCount = 1; // default
|
||||||
|
if (bytes != null) {
|
||||||
|
try {
|
||||||
|
final doc = await PdfDocument.openData(bytes);
|
||||||
|
pageCount = doc.pages.length;
|
||||||
|
} catch (_) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update file reference if path is provided
|
||||||
|
if (path != null) {
|
||||||
|
_currentFile = fs.XFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
_documentNotifier.openPicked(pageCount: pageCount, bytes: bytes);
|
||||||
|
_signatureCardNotifier.clearAll();
|
||||||
|
|
||||||
|
// Navigate to PDF screen after successfully opening PDF
|
||||||
|
_router.go('/pdf');
|
||||||
|
}
|
||||||
|
|
||||||
|
void closePdf() {
|
||||||
|
_documentNotifier.close();
|
||||||
|
_signatureCardNotifier.clearAll();
|
||||||
|
_currentFile = fs.XFile('');
|
||||||
|
|
||||||
|
// Navigate back to welcome screen when closing PDF
|
||||||
|
_router.go('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> pickAndOpenPdf() async {
|
||||||
|
final typeGroup = const fs.XTypeGroup(label: 'PDF', extensions: ['pdf']);
|
||||||
|
final file = await fs.openFile(acceptedTypeGroups: [typeGroup]);
|
||||||
|
if (file != null) {
|
||||||
|
Uint8List? bytes;
|
||||||
|
try {
|
||||||
|
bytes = await file.readAsBytes();
|
||||||
|
} catch (_) {
|
||||||
|
bytes = null;
|
||||||
|
}
|
||||||
|
await openPdf(path: file.path, bytes: bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final routerProvider = Provider<GoRouter>((ref) {
|
||||||
|
// Create PdfManager instance with dependencies
|
||||||
|
final documentNotifier = ref.read(documentRepositoryProvider.notifier);
|
||||||
|
final signatureCardNotifier = ref.read(
|
||||||
|
signatureCardRepositoryProvider.notifier,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create a late variable for the router
|
||||||
|
late final GoRouter router;
|
||||||
|
|
||||||
|
// Create PdfManager with router dependency (will be set after router creation)
|
||||||
|
late final PdfManager pdfManager;
|
||||||
|
|
||||||
|
router = GoRouter(
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: '/',
|
||||||
|
builder:
|
||||||
|
(context, state) => WelcomeScreen(
|
||||||
|
onPickPdf: () => pdfManager.pickAndOpenPdf(),
|
||||||
|
onOpenPdf:
|
||||||
|
({String? path, Uint8List? bytes, String? fileName}) =>
|
||||||
|
pdfManager.openPdf(path: path, bytes: bytes),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/pdf',
|
||||||
|
builder:
|
||||||
|
(context, state) => PdfSignatureHomePage(
|
||||||
|
onPickPdf: () => pdfManager.pickAndOpenPdf(),
|
||||||
|
onClosePdf: () => pdfManager.closePdf(),
|
||||||
|
currentFile: pdfManager.currentFile,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
initialLocation: '/',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Now create PdfManager with the router
|
||||||
|
pdfManager = PdfManager(
|
||||||
|
documentNotifier: documentNotifier,
|
||||||
|
signatureCardNotifier: signatureCardNotifier,
|
||||||
|
router: router,
|
||||||
|
);
|
||||||
|
|
||||||
|
return router;
|
||||||
|
});
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:pdfrx/pdfrx.dart';
|
|
||||||
|
|
||||||
/// Whether to use a mock continuous viewer (ListView) instead of a real PDF viewer.
|
|
||||||
/// Tests will override this to true.
|
|
||||||
final useMockViewerProvider = Provider<bool>(
|
|
||||||
(ref) => const bool.fromEnvironment('FLUTTER_TEST', defaultValue: false),
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Global visibility toggle for signature overlays (placed items). Kept simple for tests.
|
|
||||||
final signatureVisibilityProvider = StateProvider<bool>((ref) => true);
|
|
||||||
|
|
||||||
/// Whether resizing keeps the current aspect ratio for the active overlay
|
|
||||||
final aspectLockedProvider = StateProvider<bool>((ref) => false);
|
|
||||||
|
|
||||||
/// Current active overlay rect (normalized 0..1) for the mock viewer.
|
|
||||||
/// Integration tests can read this to confirm or compute placements.
|
|
||||||
final activeRectProvider = StateProvider<Rect?>((ref) => null);
|
|
||||||
|
|
||||||
/// Exposes the PdfViewerController so toolbar / thumbnails can invoke navigation.
|
|
||||||
/// It must be overridden at runtime by the hosting screen (e.g. `PdfSignatureHomePage`).
|
|
||||||
// Default controller (can be overridden by a screen to ensure a stable instance within its subtree).
|
|
||||||
final PdfViewerController _defaultPdfViewerController = PdfViewerController();
|
|
||||||
final pdfViewerControllerProvider = Provider<PdfViewerController>((ref) {
|
|
||||||
return _defaultPdfViewerController;
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Current page (1-based). Updated by PdfViewer via onPageChanged.
|
|
||||||
final currentPageProvider = StateProvider<int>((ref) => 1);
|
|
||||||
|
|
@ -6,15 +6,41 @@ import 'package:pdf_signature/data/repositories/signature_card_repository.dart';
|
||||||
import 'package:pdf_signature/domain/models/model.dart';
|
import 'package:pdf_signature/domain/models/model.dart';
|
||||||
import 'package:pdfrx/pdfrx.dart';
|
import 'package:pdfrx/pdfrx.dart';
|
||||||
|
|
||||||
class PdfViewModel extends StateNotifier<int> {
|
class PdfViewModel extends ChangeNotifier {
|
||||||
final Ref ref;
|
final Ref ref;
|
||||||
|
PdfViewerController _controller = PdfViewerController();
|
||||||
|
PdfViewerController get controller => _controller;
|
||||||
|
int _currentPage = 1;
|
||||||
|
late final bool _useMockViewer;
|
||||||
|
|
||||||
PdfViewModel(this.ref) : super(1);
|
// Active rect for signature placement overlay
|
||||||
|
Rect? _activeRect;
|
||||||
|
Rect? get activeRect => _activeRect;
|
||||||
|
set activeRect(Rect? value) {
|
||||||
|
_activeRect = value;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
Document get document => ref.read(documentRepositoryProvider);
|
// const bool.fromEnvironment('FLUTTER_TEST', defaultValue: false);
|
||||||
|
PdfViewModel(this.ref, {bool? useMockViewer})
|
||||||
|
: _useMockViewer =
|
||||||
|
useMockViewer ??
|
||||||
|
bool.fromEnvironment('FLUTTER_TEST', defaultValue: false);
|
||||||
|
|
||||||
|
bool get useMockViewer => _useMockViewer;
|
||||||
|
|
||||||
|
int get currentPage => _currentPage;
|
||||||
|
|
||||||
|
set currentPage(int value) {
|
||||||
|
_currentPage = value.clamp(1, document.pageCount);
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
Document get document => ref.watch(documentRepositoryProvider);
|
||||||
|
|
||||||
void jumpToPage(int page) {
|
void jumpToPage(int page) {
|
||||||
state = page.clamp(1, document.pageCount);
|
currentPage = page;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> openPdf({required String path, Uint8List? bytes}) async {
|
Future<void> openPdf({required String path, Uint8List? bytes}) async {
|
||||||
|
|
@ -30,37 +56,125 @@ class PdfViewModel extends StateNotifier<int> {
|
||||||
ref
|
ref
|
||||||
.read(documentRepositoryProvider.notifier)
|
.read(documentRepositoryProvider.notifier)
|
||||||
.openPicked(pageCount: pageCount, bytes: bytes);
|
.openPicked(pageCount: pageCount, bytes: bytes);
|
||||||
|
clearAllSignatureCards();
|
||||||
|
|
||||||
|
currentPage = 1; // Reset current page to 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Document repository methods
|
||||||
|
void closeDocument() {
|
||||||
|
ref.read(documentRepositoryProvider.notifier).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPageCount(int count) {
|
||||||
|
ref.read(documentRepositoryProvider.notifier).setPageCount(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPlacement({
|
||||||
|
required int page,
|
||||||
|
required Rect rect,
|
||||||
|
SignatureAsset? asset,
|
||||||
|
double rotationDeg = 0.0,
|
||||||
|
GraphicAdjust? graphicAdjust,
|
||||||
|
}) {
|
||||||
|
ref
|
||||||
|
.read(documentRepositoryProvider.notifier)
|
||||||
|
.addPlacement(
|
||||||
|
page: page,
|
||||||
|
rect: rect,
|
||||||
|
asset: asset,
|
||||||
|
rotationDeg: rotationDeg,
|
||||||
|
graphicAdjust: graphicAdjust,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updatePlacementRotation({
|
||||||
|
required int page,
|
||||||
|
required int index,
|
||||||
|
required double rotationDeg,
|
||||||
|
}) {
|
||||||
|
ref
|
||||||
|
.read(documentRepositoryProvider.notifier)
|
||||||
|
.updatePlacementRotation(
|
||||||
|
page: page,
|
||||||
|
index: index,
|
||||||
|
rotationDeg: rotationDeg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removePlacement({required int page, required int index}) {
|
||||||
|
ref
|
||||||
|
.read(documentRepositoryProvider.notifier)
|
||||||
|
.removePlacement(page: page, index: index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updatePlacementRect({
|
||||||
|
required int page,
|
||||||
|
required int index,
|
||||||
|
required Rect rect,
|
||||||
|
}) {
|
||||||
|
ref
|
||||||
|
.read(documentRepositoryProvider.notifier)
|
||||||
|
.updatePlacementRect(page: page, index: index, rect: rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SignaturePlacement> placementsOn(int page) {
|
||||||
|
return ref.read(documentRepositoryProvider.notifier).placementsOn(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
SignatureAsset? assetOfPlacement({required int page, required int index}) {
|
||||||
|
return ref
|
||||||
|
.read(documentRepositoryProvider.notifier)
|
||||||
|
.assetOfPlacement(page: page, index: index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> exportDocument({
|
||||||
|
required String outputPath,
|
||||||
|
required Size uiPageSize,
|
||||||
|
required Uint8List? signatureImageBytes,
|
||||||
|
}) async {
|
||||||
|
await ref
|
||||||
|
.read(documentRepositoryProvider.notifier)
|
||||||
|
.exportDocument(
|
||||||
|
outputPath: outputPath,
|
||||||
|
uiPageSize: uiPageSize,
|
||||||
|
signatureImageBytes: signatureImageBytes,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature card repository methods
|
||||||
|
List<SignatureCard> get signatureCards =>
|
||||||
|
ref.read(signatureCardRepositoryProvider);
|
||||||
|
|
||||||
|
void addSignatureCard(SignatureCard card) {
|
||||||
|
ref.read(signatureCardRepositoryProvider.notifier).add(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addSignatureCardWithAsset(SignatureAsset asset, double rotationDeg) {
|
||||||
|
ref
|
||||||
|
.read(signatureCardRepositoryProvider.notifier)
|
||||||
|
.addWithAsset(asset, rotationDeg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSignatureCard(
|
||||||
|
SignatureCard card,
|
||||||
|
double? rotationDeg,
|
||||||
|
GraphicAdjust? graphicAdjust,
|
||||||
|
) {
|
||||||
|
ref
|
||||||
|
.read(signatureCardRepositoryProvider.notifier)
|
||||||
|
.update(card, rotationDeg, graphicAdjust);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeSignatureCard(SignatureCard card) {
|
||||||
|
ref.read(signatureCardRepositoryProvider.notifier).remove(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearAllSignatureCards() {
|
||||||
ref.read(signatureCardRepositoryProvider.notifier).clearAll();
|
ref.read(signatureCardRepositoryProvider.notifier).clearAll();
|
||||||
state = 1; // Reset current page to 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Uint8List?> loadSignatureFromFile() async {
|
|
||||||
// This would need file picker, but since it's UI logic, perhaps keep in widget
|
|
||||||
// For now, return null
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void confirmSignature() {
|
|
||||||
// Need to implement based on original logic
|
|
||||||
}
|
|
||||||
|
|
||||||
void onDragSignature(Offset delta) {
|
|
||||||
// Implement drag
|
|
||||||
}
|
|
||||||
|
|
||||||
void onResizeSignature(Offset delta) {
|
|
||||||
// Implement resize
|
|
||||||
}
|
|
||||||
|
|
||||||
void onSelectPlaced(int? index) {
|
|
||||||
// ref.read(documentRepositoryProvider.notifier).selectPlacement(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> saveSignedPdf() async {
|
|
||||||
// Implement save logic
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final pdfViewModelProvider = StateNotifierProvider<PdfViewModel, int>((ref) {
|
final pdfViewModelProvider = ChangeNotifierProvider<PdfViewModel>((ref) {
|
||||||
return PdfViewModel(ref);
|
return PdfViewModel(ref);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,112 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'thumbnails_view.dart';
|
import 'package:pdfrx/pdfrx.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
class ThumbnailsView extends ConsumerWidget {
|
||||||
|
const ThumbnailsView({
|
||||||
|
super.key,
|
||||||
|
required this.documentRef,
|
||||||
|
required this.controller,
|
||||||
|
required this.currentPage,
|
||||||
|
});
|
||||||
|
|
||||||
|
final PdfDocumentRefData documentRef;
|
||||||
|
final PdfViewerController controller;
|
||||||
|
final int currentPage;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
color: theme.colorScheme.surface,
|
||||||
|
child: PdfDocumentViewBuilder(
|
||||||
|
documentRef: documentRef,
|
||||||
|
builder: (context, document) {
|
||||||
|
final pageCount = document?.pages.length ?? 0;
|
||||||
|
return ListView.separated(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
|
||||||
|
itemCount: pageCount,
|
||||||
|
separatorBuilder: (_, _) => const SizedBox(height: 8),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final pageNumber = index + 1;
|
||||||
|
final isSelected = currentPage == pageNumber;
|
||||||
|
return InkWell(
|
||||||
|
onTap: () {
|
||||||
|
controller.goToPage(
|
||||||
|
pageNumber: pageNumber,
|
||||||
|
anchor: PdfPageAnchor.top,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color:
|
||||||
|
isSelected
|
||||||
|
? theme.colorScheme.primaryContainer
|
||||||
|
: theme.cardColor,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(
|
||||||
|
color:
|
||||||
|
isSelected
|
||||||
|
? theme.colorScheme.primary
|
||||||
|
: theme.dividerColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(6),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 180,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
child: PdfPageView(
|
||||||
|
document: document,
|
||||||
|
pageNumber: pageNumber,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text('$pageNumber', style: theme.textTheme.bodySmall),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class PagesSidebar extends StatelessWidget {
|
class PagesSidebar extends StatelessWidget {
|
||||||
const PagesSidebar({super.key});
|
const PagesSidebar({
|
||||||
|
super.key,
|
||||||
|
required this.documentRef,
|
||||||
|
required this.controller,
|
||||||
|
required this.currentPage,
|
||||||
|
});
|
||||||
|
|
||||||
|
final PdfDocumentRefData? documentRef;
|
||||||
|
final PdfViewerController controller;
|
||||||
|
final int currentPage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Card(margin: EdgeInsets.zero, child: const ThumbnailsView());
|
if (documentRef == null) {
|
||||||
|
return Card(margin: EdgeInsets.zero, child: const SizedBox.shrink());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: ThumbnailsView(
|
||||||
|
documentRef: documentRef!,
|
||||||
|
controller: controller,
|
||||||
|
currentPage: currentPage,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
|
|
||||||
import 'pdf_page_overlays.dart';
|
import 'pdf_page_overlays.dart';
|
||||||
import '../view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
|
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
|
||||||
// using only adjusted overlay, no direct model imports needed
|
// using only adjusted overlay, no direct model imports needed
|
||||||
import '../../signature/widgets/signature_drag_data.dart';
|
import '../../signature/widgets/signature_drag_data.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import '../view_model/pdf_view_model.dart';
|
||||||
|
|
||||||
/// Mocked continuous viewer for tests or platforms without real viewer.
|
/// Mocked continuous viewer for tests or platforms without real viewer.
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
|
|
@ -57,7 +56,6 @@ class _PdfMockContinuousListState extends ConsumerState<PdfMockContinuousList> {
|
||||||
final pendingPage = widget.pendingPage;
|
final pendingPage = widget.pendingPage;
|
||||||
final scrollToPage = widget.scrollToPage;
|
final scrollToPage = widget.scrollToPage;
|
||||||
final clearPending = widget.clearPending;
|
final clearPending = widget.clearPending;
|
||||||
final visible = ref.watch(signatureVisibilityProvider);
|
|
||||||
final assets = ref.watch(signatureAssetRepositoryProvider);
|
final assets = ref.watch(signatureAssetRepositoryProvider);
|
||||||
if (pendingPage != null) {
|
if (pendingPage != null) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
|
@ -111,7 +109,7 @@ class _PdfMockContinuousListState extends ConsumerState<PdfMockContinuousList> {
|
||||||
|
|
||||||
// Add placement to the document
|
// Add placement to the document
|
||||||
ref
|
ref
|
||||||
.read(documentRepositoryProvider.notifier)
|
.read(pdfViewModelProvider.notifier)
|
||||||
.addPlacement(
|
.addPlacement(
|
||||||
page: pageNum,
|
page: pageNum,
|
||||||
rect: rect,
|
rect: rect,
|
||||||
|
|
@ -151,88 +149,75 @@ class _PdfMockContinuousListState extends ConsumerState<PdfMockContinuousList> {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
visible
|
Stack(
|
||||||
? Stack(
|
children: [
|
||||||
children: [
|
PdfPageOverlays(
|
||||||
PdfPageOverlays(
|
pageSize: pageSize,
|
||||||
pageSize: pageSize,
|
pageNumber: pageNum,
|
||||||
pageNumber: pageNum,
|
onDragSignature: widget.onDragSignature,
|
||||||
onDragSignature: widget.onDragSignature,
|
onResizeSignature: widget.onResizeSignature,
|
||||||
onResizeSignature: widget.onResizeSignature,
|
onConfirmSignature: widget.onConfirmSignature,
|
||||||
onConfirmSignature: widget.onConfirmSignature,
|
onClearActiveOverlay: widget.onClearActiveOverlay,
|
||||||
onClearActiveOverlay: widget.onClearActiveOverlay,
|
onSelectPlaced: widget.onSelectPlaced,
|
||||||
onSelectPlaced: widget.onSelectPlaced,
|
),
|
||||||
),
|
// For tests expecting an active overlay, draw a mock
|
||||||
// For tests expecting an active overlay, draw a mock
|
// overlay on page 1 when library has at least one asset
|
||||||
// overlay on page 1 when library has at least one asset
|
if (pageNum == 1 && assets.isNotEmpty)
|
||||||
if (pageNum == 1 && assets.isNotEmpty)
|
LayoutBuilder(
|
||||||
LayoutBuilder(
|
builder: (context, constraints) {
|
||||||
builder: (context, constraints) {
|
final left =
|
||||||
final left =
|
_activeRect.left * constraints.maxWidth;
|
||||||
_activeRect.left * constraints.maxWidth;
|
final top =
|
||||||
final top =
|
_activeRect.top * constraints.maxHeight;
|
||||||
_activeRect.top * constraints.maxHeight;
|
final width =
|
||||||
final width =
|
_activeRect.width * constraints.maxWidth;
|
||||||
_activeRect.width * constraints.maxWidth;
|
final height =
|
||||||
final height =
|
_activeRect.height * constraints.maxHeight;
|
||||||
_activeRect.height *
|
// Publish rect for tests/other UI to observe
|
||||||
constraints.maxHeight;
|
return Stack(
|
||||||
// Publish rect for tests/other UI to observe
|
children: [
|
||||||
WidgetsBinding.instance.addPostFrameCallback((
|
Positioned(
|
||||||
_,
|
left: left,
|
||||||
) {
|
top: top,
|
||||||
if (!mounted) return;
|
width: width,
|
||||||
ref
|
height: height,
|
||||||
.read(activeRectProvider.notifier)
|
child: GestureDetector(
|
||||||
.state = _activeRect;
|
key: const Key('signature_overlay'),
|
||||||
});
|
// Removed onPanUpdate to allow scrolling
|
||||||
return Stack(
|
child: DecoratedBox(
|
||||||
children: [
|
decoration: BoxDecoration(
|
||||||
Positioned(
|
border: Border.all(
|
||||||
left: left,
|
color: Colors.red,
|
||||||
top: top,
|
width: 2,
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
child: GestureDetector(
|
|
||||||
key: const Key('signature_overlay'),
|
|
||||||
// Removed onPanUpdate to allow scrolling
|
|
||||||
child: DecoratedBox(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border.all(
|
|
||||||
color: Colors.red,
|
|
||||||
width: 2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const SizedBox.expand(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
child: const SizedBox.expand(),
|
||||||
),
|
),
|
||||||
// resize handle bottom-right
|
),
|
||||||
Positioned(
|
),
|
||||||
left: left + width - 14,
|
// resize handle bottom-right
|
||||||
top: top + height - 14,
|
Positioned(
|
||||||
width: 14,
|
left: left + width - 14,
|
||||||
height: 14,
|
top: top + height - 14,
|
||||||
child: GestureDetector(
|
width: 14,
|
||||||
key: const Key('signature_handle'),
|
height: 14,
|
||||||
// Removed onPanUpdate to allow scrolling
|
child: GestureDetector(
|
||||||
child: DecoratedBox(
|
key: const Key('signature_handle'),
|
||||||
decoration: BoxDecoration(
|
// Removed onPanUpdate to allow scrolling
|
||||||
color: Colors.white,
|
child: DecoratedBox(
|
||||||
border: Border.all(
|
decoration: BoxDecoration(
|
||||||
color: Colors.red,
|
color: Colors.white,
|
||||||
),
|
border: Border.all(color: Colors.red),
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
),
|
||||||
},
|
],
|
||||||
),
|
);
|
||||||
],
|
},
|
||||||
)
|
),
|
||||||
: const SizedBox.shrink(),
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
// Real viewer removed in migration; mock continuous list is used in tests.
|
// Real viewer removed in migration; mock continuous list is used in tests.
|
||||||
|
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
|
||||||
import 'pdf_viewer_widget.dart';
|
import 'pdf_viewer_widget.dart';
|
||||||
|
import 'package:pdfrx/pdfrx.dart';
|
||||||
import '../view_model/pdf_view_model.dart';
|
import '../view_model/pdf_view_model.dart';
|
||||||
import '../view_model/pdf_providers.dart';
|
|
||||||
|
|
||||||
class PdfPageArea extends ConsumerStatefulWidget {
|
class PdfPageArea extends ConsumerStatefulWidget {
|
||||||
const PdfPageArea({
|
const PdfPageArea({
|
||||||
|
|
@ -17,6 +16,7 @@ class PdfPageArea extends ConsumerStatefulWidget {
|
||||||
required this.onConfirmSignature,
|
required this.onConfirmSignature,
|
||||||
required this.onClearActiveOverlay,
|
required this.onClearActiveOverlay,
|
||||||
required this.onSelectPlaced,
|
required this.onSelectPlaced,
|
||||||
|
required this.controller,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Size pageSize;
|
final Size pageSize;
|
||||||
|
|
@ -26,6 +26,7 @@ class PdfPageArea extends ConsumerStatefulWidget {
|
||||||
final VoidCallback onConfirmSignature;
|
final VoidCallback onConfirmSignature;
|
||||||
final VoidCallback onClearActiveOverlay;
|
final VoidCallback onClearActiveOverlay;
|
||||||
final ValueChanged<int?> onSelectPlaced;
|
final ValueChanged<int?> onSelectPlaced;
|
||||||
|
final PdfViewerController controller;
|
||||||
@override
|
@override
|
||||||
ConsumerState<PdfPageArea> createState() => _PdfPageAreaState();
|
ConsumerState<PdfPageArea> createState() => _PdfPageAreaState();
|
||||||
}
|
}
|
||||||
|
|
@ -117,20 +118,21 @@ class _PdfPageAreaState extends ConsumerState<PdfPageArea> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final pdf = ref.watch(documentRepositoryProvider);
|
final pdfViewModel = ref.watch(pdfViewModelProvider);
|
||||||
|
final pdf = pdfViewModel.document;
|
||||||
const pageViewMode = 'continuous';
|
const pageViewMode = 'continuous';
|
||||||
// React to PdfViewModel (source of truth for current page)
|
// React to PdfViewModel (source of truth for current page)
|
||||||
ref.listen<int>(pdfViewModelProvider, (prev, next) {
|
ref.listen(pdfViewModelProvider, (prev, next) {
|
||||||
if (prev != next) {
|
if (prev?.currentPage != next.currentPage) {
|
||||||
_scrollToPage(next);
|
_scrollToPage(next.currentPage);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// React to provider currentPage changes (e.g., user tapped overview)
|
// React to provider currentPage changes (e.g., user tapped overview)
|
||||||
ref.listen(currentPageProvider, (prev, next) {
|
ref.listen(pdfViewModelProvider, (prev, next) {
|
||||||
if (_suppressProviderListen) return;
|
if (_suppressProviderListen) return;
|
||||||
if (prev != next) {
|
if (prev?.currentPage != next.currentPage) {
|
||||||
final target = next;
|
final target = next.currentPage;
|
||||||
// If we're already navigating to this target, ignore; otherwise allow new target.
|
// If we're already navigating to this target, ignore; otherwise allow new target.
|
||||||
if (_programmaticTargetPage != null &&
|
if (_programmaticTargetPage != null &&
|
||||||
_programmaticTargetPage == target) {
|
_programmaticTargetPage == target) {
|
||||||
|
|
@ -159,7 +161,6 @@ class _PdfPageAreaState extends ConsumerState<PdfPageArea> {
|
||||||
|
|
||||||
// Use real PDF viewer
|
// Use real PDF viewer
|
||||||
if (isContinuous) {
|
if (isContinuous) {
|
||||||
final controller = ref.watch(pdfViewerControllerProvider);
|
|
||||||
return PdfViewerWidget(
|
return PdfViewerWidget(
|
||||||
pageSize: widget.pageSize,
|
pageSize: widget.pageSize,
|
||||||
onDragSignature: widget.onDragSignature,
|
onDragSignature: widget.onDragSignature,
|
||||||
|
|
@ -169,7 +170,7 @@ class _PdfPageAreaState extends ConsumerState<PdfPageArea> {
|
||||||
onSelectPlaced: widget.onSelectPlaced,
|
onSelectPlaced: widget.onSelectPlaced,
|
||||||
pageKeyBuilder: _pageKey,
|
pageKeyBuilder: _pageKey,
|
||||||
scrollToPage: _scrollToPage,
|
scrollToPage: _scrollToPage,
|
||||||
controller: controller,
|
controller: widget.controller,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
|
|
||||||
import '../../../../domain/models/model.dart';
|
import '../../../../domain/models/model.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
|
||||||
import 'signature_overlay.dart';
|
import 'signature_overlay.dart';
|
||||||
import '../view_model/pdf_providers.dart';
|
|
||||||
|
|
||||||
/// Builds all overlays for a given page: placed signatures and the active one.
|
/// Builds all overlays for a given page: placed signatures and the active one.
|
||||||
class PdfPageOverlays extends ConsumerWidget {
|
class PdfPageOverlays extends ConsumerWidget {
|
||||||
|
|
@ -29,9 +28,11 @@ class PdfPageOverlays extends ConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final pdf = ref.watch(documentRepositoryProvider);
|
final pdfViewModel = ref.watch(pdfViewModelProvider);
|
||||||
|
final pdf = pdfViewModel.document;
|
||||||
final placed =
|
final placed =
|
||||||
pdf.placementsByPage[pageNumber] ?? const <SignaturePlacement>[];
|
pdf.placementsByPage[pageNumber] ?? const <SignaturePlacement>[];
|
||||||
|
final activeRect = pdfViewModel.activeRect;
|
||||||
final widgets = <Widget>[];
|
final widgets = <Widget>[];
|
||||||
|
|
||||||
for (int i = 0; i < placed.length; i++) {
|
for (int i = 0; i < placed.length; i++) {
|
||||||
|
|
@ -48,9 +49,9 @@ class PdfPageOverlays extends ConsumerWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add active overlay if present and not using mock (mock has its own)
|
// TODO:Add active overlay if present and not using mock (mock has its own)
|
||||||
final activeRect = ref.watch(activeRectProvider);
|
|
||||||
final useMock = ref.watch(useMockViewerProvider);
|
final useMock = pdfViewModel.useMockViewer;
|
||||||
if (!useMock && activeRect != null) {
|
if (!useMock && activeRect != null) {
|
||||||
widgets.add(
|
widgets.add(
|
||||||
LayoutBuilder(
|
LayoutBuilder(
|
||||||
|
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:pdfrx/pdfrx.dart';
|
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
|
||||||
import '../view_model/pdf_providers.dart';
|
|
||||||
|
|
||||||
class PdfPagesOverview extends ConsumerWidget {
|
|
||||||
const PdfPagesOverview({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final pdf = ref.watch(documentRepositoryProvider);
|
|
||||||
final controller = ref.watch(pdfViewerControllerProvider);
|
|
||||||
final theme = Theme.of(context);
|
|
||||||
|
|
||||||
if (!pdf.loaded || pdf.pickedPdfBytes == null)
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
|
|
||||||
final documentRef = PdfDocumentRefData(
|
|
||||||
pdf.pickedPdfBytes!,
|
|
||||||
sourceName: 'document.pdf',
|
|
||||||
);
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
color: theme.colorScheme.surface,
|
|
||||||
child: PdfDocumentViewBuilder(
|
|
||||||
documentRef: documentRef,
|
|
||||||
builder: (context, document) {
|
|
||||||
final pageCount = document?.pages.length ?? 0;
|
|
||||||
return ListView.separated(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
|
|
||||||
itemCount: pageCount,
|
|
||||||
separatorBuilder: (_, _) => const SizedBox(height: 8),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final pageNumber = index + 1;
|
|
||||||
final isSelected = ref.watch(currentPageProvider) == pageNumber;
|
|
||||||
return InkWell(
|
|
||||||
onTap: () {
|
|
||||||
controller.goToPage(
|
|
||||||
pageNumber: pageNumber,
|
|
||||||
anchor: PdfPageAnchor.top,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: DecoratedBox(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color:
|
|
||||||
isSelected
|
|
||||||
? theme.colorScheme.primaryContainer
|
|
||||||
: theme.cardColor,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
border: Border.all(
|
|
||||||
color:
|
|
||||||
isSelected
|
|
||||||
? theme.colorScheme.primary
|
|
||||||
: theme.dividerColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(6),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
height: 180,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
child: PdfPageView(
|
|
||||||
document: document,
|
|
||||||
pageNumber: pageNumber,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text('$pageNumber', style: theme.textTheme.bodySmall),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,8 +7,6 @@ import 'package:pdf_signature/data/repositories/preferences_repository.dart';
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
import 'package:multi_split_view/multi_split_view.dart';
|
import 'package:multi_split_view/multi_split_view.dart';
|
||||||
|
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
|
||||||
import '../view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdfrx/pdfrx.dart';
|
import 'package:pdfrx/pdfrx.dart';
|
||||||
import 'draw_canvas.dart';
|
import 'draw_canvas.dart';
|
||||||
import 'pdf_toolbar.dart';
|
import 'pdf_toolbar.dart';
|
||||||
|
|
@ -19,7 +17,16 @@ import 'ui_services.dart';
|
||||||
import '../view_model/pdf_view_model.dart';
|
import '../view_model/pdf_view_model.dart';
|
||||||
|
|
||||||
class PdfSignatureHomePage extends ConsumerStatefulWidget {
|
class PdfSignatureHomePage extends ConsumerStatefulWidget {
|
||||||
const PdfSignatureHomePage({super.key});
|
final Future<void> Function() onPickPdf;
|
||||||
|
final VoidCallback onClosePdf;
|
||||||
|
final fs.XFile currentFile;
|
||||||
|
|
||||||
|
const PdfSignatureHomePage({
|
||||||
|
super.key,
|
||||||
|
required this.onPickPdf,
|
||||||
|
required this.onClosePdf,
|
||||||
|
required this.currentFile,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<PdfSignatureHomePage> createState() =>
|
ConsumerState<PdfSignatureHomePage> createState() =>
|
||||||
|
|
@ -31,7 +38,6 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
bool _showPagesSidebar = true;
|
bool _showPagesSidebar = true;
|
||||||
bool _showSignaturesSidebar = true;
|
bool _showSignaturesSidebar = true;
|
||||||
int _zoomLevel = 100; // percentage for display only
|
int _zoomLevel = 100; // percentage for display only
|
||||||
fs.XFile _file = fs.XFile('');
|
|
||||||
|
|
||||||
// Split view controller to manage resizable sidebars without remounting the center area.
|
// Split view controller to manage resizable sidebars without remounting the center area.
|
||||||
late final MultiSplitViewController _splitController;
|
late final MultiSplitViewController _splitController;
|
||||||
|
|
@ -43,6 +49,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
final double _pagesMax = 250;
|
final double _pagesMax = 250;
|
||||||
final double _signaturesMin = 140;
|
final double _signaturesMin = 140;
|
||||||
final double _signaturesMax = 250;
|
final double _signaturesMax = 250;
|
||||||
|
late PdfViewModel _viewModel;
|
||||||
|
|
||||||
// Exposed for tests to trigger the invalid-file SnackBar without UI.
|
// Exposed for tests to trigger the invalid-file SnackBar without UI.
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
|
|
@ -55,38 +62,17 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _pickPdf() async {
|
Future<void> _pickPdf() async {
|
||||||
final typeGroup = const fs.XTypeGroup(label: 'PDF', extensions: ['pdf']);
|
await widget.onPickPdf();
|
||||||
final file = await fs.openFile(acceptedTypeGroups: [typeGroup]);
|
}
|
||||||
if (file != null) {
|
|
||||||
setState(() {
|
void _closePdf() {
|
||||||
_file = file;
|
widget.onClosePdf();
|
||||||
});
|
|
||||||
Uint8List? bytes;
|
|
||||||
try {
|
|
||||||
bytes = await file.readAsBytes();
|
|
||||||
} catch (_) {
|
|
||||||
bytes = null;
|
|
||||||
}
|
|
||||||
// infer page count if possible
|
|
||||||
int pageCount = 1;
|
|
||||||
if (bytes != null) {
|
|
||||||
try {
|
|
||||||
final doc = await PdfDocument.openData(bytes);
|
|
||||||
pageCount = doc.pages.length;
|
|
||||||
} catch (_) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ref
|
|
||||||
.read(documentRepositoryProvider.notifier)
|
|
||||||
.openPicked(pageCount: pageCount, bytes: bytes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _jumpToPage(int page) {
|
void _jumpToPage(int page) {
|
||||||
final controller = ref.read(pdfViewerControllerProvider);
|
final controller = _viewModel.controller;
|
||||||
final current = ref.read(currentPageProvider);
|
final current = _viewModel.currentPage;
|
||||||
final pdf = ref.read(documentRepositoryProvider);
|
final pdf = _viewModel.document;
|
||||||
int target;
|
int target;
|
||||||
if (page == -1) {
|
if (page == -1) {
|
||||||
target = (current - 1).clamp(1, pdf.pageCount);
|
target = (current - 1).clamp(1, pdf.pageCount);
|
||||||
|
|
@ -95,10 +81,9 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
}
|
}
|
||||||
// Update reactive page providers so UI/tests reflect navigation even if controller is a stub
|
// Update reactive page providers so UI/tests reflect navigation even if controller is a stub
|
||||||
if (current != target) {
|
if (current != target) {
|
||||||
ref.read(currentPageProvider.notifier).state = target;
|
|
||||||
// Also notify view model (if used elsewhere) via its public API
|
// Also notify view model (if used elsewhere) via its public API
|
||||||
try {
|
try {
|
||||||
ref.read(pdfViewModelProvider.notifier).jumpToPage(target);
|
_viewModel.jumpToPage(target);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// ignore if provider not available
|
// ignore if provider not available
|
||||||
}
|
}
|
||||||
|
|
@ -153,7 +138,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
Future<void> _saveSignedPdf() async {
|
Future<void> _saveSignedPdf() async {
|
||||||
ref.read(exportingProvider.notifier).state = true;
|
ref.read(exportingProvider.notifier).state = true;
|
||||||
try {
|
try {
|
||||||
final pdf = ref.read(documentRepositoryProvider);
|
final pdf = _viewModel.document;
|
||||||
final messenger = ScaffoldMessenger.of(context);
|
final messenger = ScaffoldMessenger.of(context);
|
||||||
if (!pdf.loaded) {
|
if (!pdf.loaded) {
|
||||||
messenger.showSnackBar(
|
messenger.showSnackBar(
|
||||||
|
|
@ -219,6 +204,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// Build areas once with builders; keep these instances stable.
|
// Build areas once with builders; keep these instances stable.
|
||||||
|
_viewModel = ref.read(pdfViewModelProvider.notifier);
|
||||||
_areas = [
|
_areas = [
|
||||||
Area(
|
Area(
|
||||||
size: _lastPagesWidth,
|
size: _lastPagesWidth,
|
||||||
|
|
@ -227,7 +213,26 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
builder:
|
builder:
|
||||||
(context, area) => Offstage(
|
(context, area) => Offstage(
|
||||||
offstage: !_showPagesSidebar,
|
offstage: !_showPagesSidebar,
|
||||||
child: const PagesSidebar(),
|
child: Consumer(
|
||||||
|
builder: (context, ref, child) {
|
||||||
|
final pdfViewModel = ref.watch(pdfViewModelProvider);
|
||||||
|
final pdf = pdfViewModel.document;
|
||||||
|
|
||||||
|
final documentRef =
|
||||||
|
pdf.loaded && pdf.pickedPdfBytes != null
|
||||||
|
? PdfDocumentRefData(
|
||||||
|
pdf.pickedPdfBytes!,
|
||||||
|
sourceName: 'document.pdf',
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return PagesSidebar(
|
||||||
|
documentRef: documentRef,
|
||||||
|
controller: _viewModel.controller,
|
||||||
|
currentPage: _viewModel.currentPage,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Area(
|
Area(
|
||||||
|
|
@ -235,6 +240,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
builder:
|
builder:
|
||||||
(context, area) => RepaintBoundary(
|
(context, area) => RepaintBoundary(
|
||||||
child: PdfPageArea(
|
child: PdfPageArea(
|
||||||
|
controller: _viewModel.controller,
|
||||||
key: const ValueKey('pdf_page_area'),
|
key: const ValueKey('pdf_page_area'),
|
||||||
pageSize: _pageSize,
|
pageSize: _pageSize,
|
||||||
onDragSignature: _onDragSignature,
|
onDragSignature: _onDragSignature,
|
||||||
|
|
@ -300,15 +306,9 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Provide controller override so descendants can access it.
|
return _buildScaffold(context);
|
||||||
return ProviderScope(
|
|
||||||
overrides: [pdfViewerControllerProvider.overrideWithValue(_controller)],
|
|
||||||
child: _buildScaffold(context),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
late final PdfViewerController _controller = PdfViewerController();
|
|
||||||
|
|
||||||
Widget _buildScaffold(BuildContext context) {
|
Widget _buildScaffold(BuildContext context) {
|
||||||
final isExporting = ref.watch(exportingProvider);
|
final isExporting = ref.watch(exportingProvider);
|
||||||
final l = AppLocalizations.of(context);
|
final l = AppLocalizations.of(context);
|
||||||
|
|
@ -323,6 +323,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
PdfToolbar(
|
PdfToolbar(
|
||||||
disabled: isExporting,
|
disabled: isExporting,
|
||||||
onPickPdf: _pickPdf,
|
onPickPdf: _pickPdf,
|
||||||
|
onClosePdf: _closePdf,
|
||||||
onJumpToPage: _jumpToPage,
|
onJumpToPage: _jumpToPage,
|
||||||
onZoomOut: () {
|
onZoomOut: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
@ -335,7 +336,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
zoomLevel: _zoomLevel,
|
zoomLevel: _zoomLevel,
|
||||||
fileName: _file.name,
|
filePath: widget.currentFile.path,
|
||||||
showPagesSidebar: _showPagesSidebar,
|
showPagesSidebar: _showPagesSidebar,
|
||||||
showSignaturesSidebar: _showSignaturesSidebar,
|
showSignaturesSidebar: _showSignaturesSidebar,
|
||||||
onTogglePagesSidebar:
|
onTogglePagesSidebar:
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,19 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
|
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import '../view_model/pdf_providers.dart';
|
|
||||||
|
|
||||||
class PdfToolbar extends ConsumerStatefulWidget {
|
class PdfToolbar extends ConsumerStatefulWidget {
|
||||||
const PdfToolbar({
|
const PdfToolbar({
|
||||||
super.key,
|
super.key,
|
||||||
required this.disabled,
|
required this.disabled,
|
||||||
required this.onPickPdf,
|
required this.onPickPdf,
|
||||||
|
required this.onClosePdf,
|
||||||
required this.onJumpToPage,
|
required this.onJumpToPage,
|
||||||
required this.onZoomOut,
|
required this.onZoomOut,
|
||||||
required this.onZoomIn,
|
required this.onZoomIn,
|
||||||
this.zoomLevel,
|
this.zoomLevel,
|
||||||
this.fileName,
|
this.filePath,
|
||||||
required this.showPagesSidebar,
|
required this.showPagesSidebar,
|
||||||
required this.showSignaturesSidebar,
|
required this.showSignaturesSidebar,
|
||||||
required this.onTogglePagesSidebar,
|
required this.onTogglePagesSidebar,
|
||||||
|
|
@ -24,8 +24,9 @@ class PdfToolbar extends ConsumerStatefulWidget {
|
||||||
|
|
||||||
final bool disabled;
|
final bool disabled;
|
||||||
final VoidCallback onPickPdf;
|
final VoidCallback onPickPdf;
|
||||||
|
final VoidCallback onClosePdf;
|
||||||
final ValueChanged<int> onJumpToPage;
|
final ValueChanged<int> onJumpToPage;
|
||||||
final String? fileName;
|
final String? filePath;
|
||||||
final VoidCallback onZoomOut;
|
final VoidCallback onZoomOut;
|
||||||
final VoidCallback onZoomIn;
|
final VoidCallback onZoomIn;
|
||||||
// Current zoom level as a percentage (e.g., 100 for 100%)
|
// Current zoom level as a percentage (e.g., 100 for 100%)
|
||||||
|
|
@ -56,8 +57,9 @@ class _PdfToolbarState extends ConsumerState<PdfToolbar> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final pdf = ref.watch(documentRepositoryProvider);
|
final pdfViewModel = ref.watch(pdfViewModelProvider);
|
||||||
final currentPage = ref.watch(currentPageProvider);
|
final pdf = pdfViewModel.document;
|
||||||
|
final currentPage = pdfViewModel.currentPage;
|
||||||
final l = AppLocalizations.of(context);
|
final l = AppLocalizations.of(context);
|
||||||
final pageInfo = l.pageInfo(currentPage, pdf.pageCount);
|
final pageInfo = l.pageInfo(currentPage, pdf.pageCount);
|
||||||
|
|
||||||
|
|
@ -83,9 +85,9 @@ class _PdfToolbarState extends ConsumerState<PdfToolbar> {
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxWidth: 220),
|
constraints: const BoxConstraints(maxWidth: 220),
|
||||||
child: Text(
|
child: Text(
|
||||||
// if filename not null
|
// if filePath not null
|
||||||
widget.fileName != null
|
widget.filePath != null
|
||||||
? widget.fileName!
|
? widget.filePath!
|
||||||
: 'No file selected',
|
: 'No file selected',
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
|
@ -94,6 +96,12 @@ class _PdfToolbarState extends ConsumerState<PdfToolbar> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (pdf.loaded) ...[
|
if (pdf.loaded) ...[
|
||||||
|
IconButton(
|
||||||
|
key: const Key('btn_close_pdf'),
|
||||||
|
onPressed: widget.disabled ? null : widget.onClosePdf,
|
||||||
|
icon: const Icon(Icons.close),
|
||||||
|
tooltip: l.close,
|
||||||
|
),
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdfrx/pdfrx.dart';
|
import 'package:pdfrx/pdfrx.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
import 'pdf_page_overlays.dart';
|
import 'pdf_page_overlays.dart';
|
||||||
import './pdf_mock_continuous_list.dart';
|
import './pdf_mock_continuous_list.dart';
|
||||||
import '../view_model/pdf_providers.dart';
|
|
||||||
import '../view_model/pdf_view_model.dart';
|
import '../view_model/pdf_view_model.dart';
|
||||||
|
|
||||||
class PdfViewerWidget extends ConsumerStatefulWidget {
|
class PdfViewerWidget extends ConsumerStatefulWidget {
|
||||||
|
|
@ -55,11 +53,10 @@ class _PdfViewerWidgetState extends ConsumerState<PdfViewerWidget> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final document = ref.watch(documentRepositoryProvider);
|
final pdfViewModel = ref.watch(pdfViewModelProvider);
|
||||||
final useMock = ref.watch(useMockViewerProvider);
|
final document = pdfViewModel.document;
|
||||||
ref.watch(activeRectProvider); // trigger rebuild when active rect changes
|
final useMock = pdfViewModel.useMockViewer;
|
||||||
// Watch to rebuild on page change
|
// trigger rebuild when active rect changes
|
||||||
ref.watch(currentPageProvider);
|
|
||||||
|
|
||||||
// Update document ref when document changes
|
// Update document ref when document changes
|
||||||
if (document.loaded && document.pickedPdfBytes != null) {
|
if (document.loaded && document.pickedPdfBytes != null) {
|
||||||
|
|
@ -109,12 +106,11 @@ class _PdfViewerWidgetState extends ConsumerState<PdfViewerWidget> {
|
||||||
onViewerReady: (document, controller) {
|
onViewerReady: (document, controller) {
|
||||||
// Update page count in repository
|
// Update page count in repository
|
||||||
ref
|
ref
|
||||||
.read(documentRepositoryProvider.notifier)
|
.read(pdfViewModelProvider.notifier)
|
||||||
.setPageCount(document.pages.length);
|
.setPageCount(document.pages.length);
|
||||||
},
|
},
|
||||||
onPageChanged: (page) {
|
onPageChanged: (page) {
|
||||||
if (page != null) {
|
if (page != null) {
|
||||||
ref.read(currentPageProvider.notifier).state = page;
|
|
||||||
// Also update the view model to keep them in sync
|
// Also update the view model to keep them in sync
|
||||||
ref.read(pdfViewModelProvider.notifier).jumpToPage(page);
|
ref.read(pdfViewModelProvider.notifier).jumpToPage(page);
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +119,7 @@ class _PdfViewerWidgetState extends ConsumerState<PdfViewerWidget> {
|
||||||
return [
|
return [
|
||||||
PdfPageOverlays(
|
PdfPageOverlays(
|
||||||
pageSize: widget.pageSize,
|
pageSize: widget.pageSize,
|
||||||
pageNumber: ref.watch(currentPageProvider),
|
pageNumber: pdfViewModel.currentPage,
|
||||||
onDragSignature: widget.onDragSignature,
|
onDragSignature: widget.onDragSignature,
|
||||||
onResizeSignature: widget.onResizeSignature,
|
onResizeSignature: widget.onResizeSignature,
|
||||||
onConfirmSignature: widget.onConfirmSignature,
|
onConfirmSignature: widget.onConfirmSignature,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
|
|
||||||
import 'signature_drawer.dart';
|
import '../../signature/widgets/signature_drawer.dart';
|
||||||
import 'ui_services.dart';
|
import 'ui_services.dart';
|
||||||
|
|
||||||
class SignaturesSidebar extends ConsumerWidget {
|
class SignaturesSidebar extends ConsumerWidget {
|
||||||
|
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:pdfrx/pdfrx.dart';
|
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
|
||||||
import '../view_model/pdf_providers.dart';
|
|
||||||
|
|
||||||
class ThumbnailsView extends ConsumerWidget {
|
|
||||||
const ThumbnailsView({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
final pdf = ref.watch(documentRepositoryProvider);
|
|
||||||
final controller = ref.watch(pdfViewerControllerProvider);
|
|
||||||
final theme = Theme.of(context);
|
|
||||||
|
|
||||||
if (!pdf.loaded || pdf.pickedPdfBytes == null)
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
|
|
||||||
final documentRef = PdfDocumentRefData(
|
|
||||||
pdf.pickedPdfBytes!,
|
|
||||||
sourceName: 'document.pdf',
|
|
||||||
);
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
color: theme.colorScheme.surface,
|
|
||||||
child: PdfDocumentViewBuilder(
|
|
||||||
documentRef: documentRef,
|
|
||||||
builder: (context, document) {
|
|
||||||
final pageCount = document?.pages.length ?? 0;
|
|
||||||
return ListView.separated(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
|
|
||||||
itemCount: pageCount,
|
|
||||||
separatorBuilder: (_, _) => const SizedBox(height: 8),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final pageNumber = index + 1;
|
|
||||||
final isSelected = ref.watch(currentPageProvider) == pageNumber;
|
|
||||||
return InkWell(
|
|
||||||
onTap: () {
|
|
||||||
controller.goToPage(
|
|
||||||
pageNumber: pageNumber,
|
|
||||||
anchor: PdfPageAnchor.top,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: DecoratedBox(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color:
|
|
||||||
isSelected
|
|
||||||
? theme.colorScheme.primaryContainer
|
|
||||||
: theme.cardColor,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
border: Border.all(
|
|
||||||
color:
|
|
||||||
isSelected
|
|
||||||
? theme.colorScheme.primary
|
|
||||||
: theme.dividerColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(6),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
height: 180,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
child: PdfPageView(
|
|
||||||
document: document,
|
|
||||||
pageNumber: pageNumber,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text('$pageNumber', style: theme.textTheme.bodySmall),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,9 +6,8 @@ import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
|
|
||||||
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
|
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
|
||||||
import 'package:pdf_signature/data/repositories/signature_card_repository.dart';
|
import 'package:pdf_signature/data/repositories/signature_card_repository.dart';
|
||||||
import 'image_editor_dialog.dart';
|
import '../../pdf/widgets/image_editor_dialog.dart';
|
||||||
import '../../signature/widgets/signature_card.dart';
|
import 'signature_card.dart';
|
||||||
import '../view_model/pdf_providers.dart';
|
|
||||||
|
|
||||||
/// Data for drag-and-drop is in signature_drag_data.dart
|
/// Data for drag-and-drop is in signature_drag_data.dart
|
||||||
|
|
||||||
|
|
@ -66,9 +65,7 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onTap: () {
|
onTap: () {
|
||||||
ref
|
// state = const Rect.fromLTWH(0.2, 0.2, 0.3, 0.15);
|
||||||
.read(activeRectProvider.notifier)
|
|
||||||
.state = const Rect.fromLTWH(0.2, 0.2, 0.3, 0.15);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import 'package:pdf_signature/data/repositories/signature_card_repository.dart';
|
|
||||||
import 'package:pdfrx/pdfrx.dart';
|
|
||||||
|
|
||||||
class WelcomeViewModel {
|
class WelcomeViewModel {
|
||||||
final Ref ref;
|
final Ref ref;
|
||||||
|
|
@ -10,19 +8,9 @@ class WelcomeViewModel {
|
||||||
WelcomeViewModel(this.ref);
|
WelcomeViewModel(this.ref);
|
||||||
|
|
||||||
Future<void> openPdf({required String path, Uint8List? bytes}) async {
|
Future<void> openPdf({required String path, Uint8List? bytes}) async {
|
||||||
int pageCount = 1; // default
|
await ref
|
||||||
if (bytes != null) {
|
.read(pdfViewModelProvider.notifier)
|
||||||
try {
|
.openPdf(path: path, bytes: bytes);
|
||||||
final doc = await PdfDocument.openData(bytes);
|
|
||||||
pageCount = doc.pages.length;
|
|
||||||
} catch (_) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ref
|
|
||||||
.read(documentRepositoryProvider.notifier)
|
|
||||||
.openPicked(pageCount: pageCount, bytes: bytes);
|
|
||||||
ref.read(signatureCardRepositoryProvider.notifier).clearAll();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:desktop_drop/desktop_drop.dart';
|
import 'package:desktop_drop/desktop_drop.dart';
|
||||||
import 'package:file_selector/file_selector.dart' as fs;
|
|
||||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
import 'package:pdf_signature/ui/features/welcome/view_model/welcome_view_model.dart';
|
|
||||||
|
|
||||||
// Abstraction to make drop handling testable without constructing
|
// Abstraction to make drop handling testable without constructing
|
||||||
// platform-specific DropItem types in widget tests.
|
// platform-specific DropItem types in widget tests.
|
||||||
|
|
@ -32,7 +30,8 @@ typedef Reader = T Function<T>(ProviderListenable<T> provider);
|
||||||
|
|
||||||
// Select first .pdf file (case-insensitive) or fall back to first entry.
|
// Select first .pdf file (case-insensitive) or fall back to first entry.
|
||||||
Future<void> handleDroppedFiles(
|
Future<void> handleDroppedFiles(
|
||||||
Reader read,
|
Future<void> Function({String? path, Uint8List? bytes, String? fileName})
|
||||||
|
onOpenPdf,
|
||||||
Iterable<DropReadable> files,
|
Iterable<DropReadable> files,
|
||||||
) async {
|
) async {
|
||||||
if (files.isEmpty) return;
|
if (files.isEmpty) return;
|
||||||
|
|
@ -47,11 +46,23 @@ Future<void> handleDroppedFiles(
|
||||||
bytes = null;
|
bytes = null;
|
||||||
}
|
}
|
||||||
final String path = pdf.path ?? pdf.name;
|
final String path = pdf.path ?? pdf.name;
|
||||||
await read(welcomeViewModelProvider).openPdf(path: path, bytes: bytes);
|
await onOpenPdf(path: path, bytes: bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
class WelcomeScreen extends ConsumerStatefulWidget {
|
class WelcomeScreen extends ConsumerStatefulWidget {
|
||||||
const WelcomeScreen({super.key});
|
final Future<void> Function() onPickPdf;
|
||||||
|
final Future<void> Function({
|
||||||
|
String? path,
|
||||||
|
Uint8List? bytes,
|
||||||
|
String? fileName,
|
||||||
|
})
|
||||||
|
onOpenPdf;
|
||||||
|
|
||||||
|
const WelcomeScreen({
|
||||||
|
super.key,
|
||||||
|
required this.onPickPdf,
|
||||||
|
required this.onOpenPdf,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<WelcomeScreen> createState() => _WelcomeScreenState();
|
ConsumerState<WelcomeScreen> createState() => _WelcomeScreenState();
|
||||||
|
|
@ -61,19 +72,7 @@ class _WelcomeScreenState extends ConsumerState<WelcomeScreen> {
|
||||||
bool _dragging = false;
|
bool _dragging = false;
|
||||||
|
|
||||||
Future<void> _pickPdf() async {
|
Future<void> _pickPdf() async {
|
||||||
final typeGroup = const fs.XTypeGroup(label: 'PDF', extensions: ['pdf']);
|
await widget.onPickPdf();
|
||||||
final file = await fs.openFile(acceptedTypeGroups: [typeGroup]);
|
|
||||||
if (file != null) {
|
|
||||||
Uint8List? bytes;
|
|
||||||
try {
|
|
||||||
bytes = await file.readAsBytes();
|
|
||||||
} catch (_) {
|
|
||||||
bytes = null;
|
|
||||||
}
|
|
||||||
await ref
|
|
||||||
.read(welcomeViewModelProvider)
|
|
||||||
.openPdf(path: file.path, bytes: bytes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -113,7 +112,7 @@ class _WelcomeScreenState extends ConsumerState<WelcomeScreen> {
|
||||||
final adapters = desktopFiles.map<DropReadable>(
|
final adapters = desktopFiles.map<DropReadable>(
|
||||||
(f) => _DropReadableFromDesktop(f),
|
(f) => _DropReadableFromDesktop(f),
|
||||||
);
|
);
|
||||||
await handleDroppedFiles(ref.read, adapters);
|
await handleDroppedFiles(widget.onOpenPdf, adapters);
|
||||||
},
|
},
|
||||||
child: AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 150),
|
duration: const Duration(milliseconds: 150),
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ dev_dependencies:
|
||||||
freezed: ^3.0.0
|
freezed: ^3.0.0
|
||||||
custom_lint: ^0.7.6
|
custom_lint: ^0.7.6
|
||||||
riverpod_lint: ^2.6.5
|
riverpod_lint: ^2.6.5
|
||||||
|
go_router_builder: ^4.0.1
|
||||||
|
|
||||||
# The "flutter_lints" package below contains a set of recommended lints to
|
# The "flutter_lints" package below contains a set of recommended lints to
|
||||||
# encourage good coding practices. The lint set provided by the package is
|
# encourage good coding practices. The lint set provided by the package is
|
||||||
|
|
@ -80,6 +81,7 @@ dev_dependencies:
|
||||||
msix: ^3.16.12
|
msix: ^3.16.12
|
||||||
json_serializable: ^6.11.0
|
json_serializable: ^6.11.0
|
||||||
dead_code_analyzer: ^1.1.0
|
dead_code_analyzer: ^1.1.0
|
||||||
|
faker_dart: ^0.2.3
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ import 'dart:typed_data';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:pdf_signature/app.dart';
|
import 'package:pdf_signature/app.dart';
|
||||||
import 'package:pdf_signature/data/repositories/preferences_repository.dart';
|
import 'package:pdf_signature/data/repositories/preferences_repository.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/ui_services.dart';
|
import 'package:pdf_signature/ui/features/pdf/widgets/ui_services.dart';
|
||||||
import 'package:pdf_signature/data/services/export_service.dart';
|
import 'package:pdf_signature/data/services/export_service.dart';
|
||||||
import 'package:pdf_signature/domain/models/model.dart';
|
import 'package:pdf_signature/domain/models/model.dart';
|
||||||
|
|
@ -48,7 +48,9 @@ Future<ProviderContainer> pumpApp(
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) => DocumentStateNotifier()..openSample(),
|
(ref) => DocumentStateNotifier()..openSample(),
|
||||||
),
|
),
|
||||||
useMockViewerProvider.overrideWith((ref) => true),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: true),
|
||||||
|
),
|
||||||
exportServiceProvider.overrideWith((ref) => fakeExport),
|
exportServiceProvider.overrideWith((ref) => fakeExport),
|
||||||
savePathPickerProvider.overrideWith((ref) => () async => 'out.pdf'),
|
savePathPickerProvider.overrideWith((ref) => () async => 'out.pdf'),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
import 'package:pdf_signature/data/repositories/signature_card_repository.dart';
|
import 'package:pdf_signature/data/repositories/signature_card_repository.dart';
|
||||||
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
|
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
|
||||||
import 'package:pdf_signature/domain/models/model.dart';
|
import 'package:pdf_signature/domain/models/model.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import '_world.dart';
|
import '_world.dart';
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ Future<void> aMultipageDocumentIsOpen(WidgetTester tester) async {
|
||||||
container.read(documentRepositoryProvider.notifier).openPicked(pageCount: 5);
|
container.read(documentRepositoryProvider.notifier).openPicked(pageCount: 5);
|
||||||
// Reset page state providers
|
// Reset page state providers
|
||||||
try {
|
try {
|
||||||
container.read(currentPageProvider.notifier).state = 1;
|
container.read(pdfViewModelProvider.notifier).jumpToPage(1);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
try {
|
try {
|
||||||
container.read(pdfViewModelProvider.notifier).jumpToPage(1);
|
container.read(pdfViewModelProvider.notifier).jumpToPage(1);
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ Future<void> aSignatureAssetIsPlacedOnThePage(WidgetTester tester) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place it on the current page
|
// Place it on the current page
|
||||||
final currentPage = container.read(pdfViewModelProvider);
|
final currentPage = container.read(pdfViewModelProvider).currentPage;
|
||||||
container
|
container
|
||||||
.read(documentRepositoryProvider.notifier)
|
.read(documentRepositoryProvider.notifier)
|
||||||
.addPlacement(
|
.addPlacement(
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ Future<void> aSignaturePlacementIsPlacedWithAPositionAndSizeRelativeToThePage(
|
||||||
) async {
|
) async {
|
||||||
final container = TestWorld.container ?? ProviderContainer();
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
TestWorld.container = container;
|
TestWorld.container = container;
|
||||||
final currentPage = container.read(pdfViewModelProvider);
|
final currentPage = container.read(pdfViewModelProvider).currentPage;
|
||||||
container
|
container
|
||||||
.read(documentRepositoryProvider.notifier)
|
.read(documentRepositoryProvider.notifier)
|
||||||
.addPlacement(
|
.addPlacement(
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ Future<void> draggingOrResizingOneDoesNotChangeTheOther(
|
||||||
WidgetTester tester,
|
WidgetTester tester,
|
||||||
) async {
|
) async {
|
||||||
final container = TestWorld.container ?? ProviderContainer();
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
final page = container.read(pdfViewModelProvider);
|
final page = container.read(pdfViewModelProvider).currentPage;
|
||||||
final list = container
|
final list = container
|
||||||
.read(documentRepositoryProvider.notifier)
|
.read(documentRepositoryProvider.notifier)
|
||||||
.placementsOn(page);
|
.placementsOn(page);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import '_world.dart';
|
import '_world.dart';
|
||||||
|
|
||||||
|
|
@ -8,11 +8,10 @@ import '_world.dart';
|
||||||
Future<void> pageIsDisplayed(WidgetTester tester, num param1) async {
|
Future<void> pageIsDisplayed(WidgetTester tester, num param1) async {
|
||||||
final expected = param1.toInt();
|
final expected = param1.toInt();
|
||||||
final c = TestWorld.container ?? ProviderContainer();
|
final c = TestWorld.container ?? ProviderContainer();
|
||||||
final vm = c.read(pdfViewModelProvider);
|
final currentPage = c.read(pdfViewModelProvider).currentPage;
|
||||||
final legacy = c.read(currentPageProvider);
|
|
||||||
expect(
|
expect(
|
||||||
vm == expected || legacy == expected,
|
currentPage == expected,
|
||||||
true,
|
true,
|
||||||
reason: 'Expected page $expected but got vm=$vm current=$legacy',
|
reason: 'Expected page $expected but got current=$currentPage',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
|
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
|
||||||
import 'package:pdf_signature/data/repositories/signature_card_repository.dart';
|
import 'package:pdf_signature/data/repositories/signature_card_repository.dart';
|
||||||
import 'package:pdf_signature/domain/models/model.dart';
|
import 'package:pdf_signature/domain/models/model.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
|
|
||||||
import '_world.dart';
|
import '_world.dart';
|
||||||
|
|
||||||
class _BridgedSignatureCardStateNotifier extends SignatureCardStateNotifier {
|
class _BridgedSignatureCardStateNotifier extends SignatureCardStateNotifier {
|
||||||
|
|
@ -37,7 +38,9 @@ Future<void> theAppLaunches(WidgetTester tester) async {
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) => DocumentStateNotifier()..openSample(),
|
(ref) => DocumentStateNotifier()..openSample(),
|
||||||
),
|
),
|
||||||
useMockViewerProvider.overrideWith((ref) => true),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: true),
|
||||||
|
),
|
||||||
// Bridge: automatically mirror assets into signature cards so legacy
|
// Bridge: automatically mirror assets into signature cards so legacy
|
||||||
// feature steps that expect SignatureCard widgets keep working even
|
// feature steps that expect SignatureCard widgets keep working even
|
||||||
// though the production UI currently only stores raw assets.
|
// though the production UI currently only stores raw assets.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
import '_world.dart';
|
import '_world.dart';
|
||||||
|
|
||||||
/// Usage: the last page is displayed (page {5})
|
/// Usage: the last page is displayed (page {5})
|
||||||
|
|
@ -11,11 +11,10 @@ Future<void> theLastPageIsDisplayedPage(WidgetTester tester, num param1) async {
|
||||||
final c = TestWorld.container ?? ProviderContainer();
|
final c = TestWorld.container ?? ProviderContainer();
|
||||||
final pdf = c.read(documentRepositoryProvider);
|
final pdf = c.read(documentRepositoryProvider);
|
||||||
expect(pdf.pageCount, last);
|
expect(pdf.pageCount, last);
|
||||||
final vm = c.read(pdfViewModelProvider);
|
final currentPage = c.read(pdfViewModelProvider).currentPage;
|
||||||
final legacy = c.read(currentPageProvider);
|
|
||||||
expect(
|
expect(
|
||||||
vm == last || legacy == last,
|
currentPage == last,
|
||||||
true,
|
true,
|
||||||
reason: 'Expected last page $last but got vm=$vm current=$legacy',
|
reason: 'Expected last page $last but got current=$currentPage',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import '_world.dart';
|
import '_world.dart';
|
||||||
|
|
||||||
|
|
@ -9,12 +9,10 @@ Future<void> theUserClicksTheGoToApplyButton(WidgetTester tester) async {
|
||||||
final c = TestWorld.container ?? ProviderContainer();
|
final c = TestWorld.container ?? ProviderContainer();
|
||||||
final pending = TestWorld.pendingGoTo;
|
final pending = TestWorld.pendingGoTo;
|
||||||
if (pending != null) {
|
if (pending != null) {
|
||||||
try {
|
|
||||||
c.read(currentPageProvider.notifier).state = pending;
|
|
||||||
} catch (_) {}
|
|
||||||
try {
|
try {
|
||||||
c.read(pdfViewModelProvider.notifier).jumpToPage(pending);
|
c.read(pdfViewModelProvider.notifier).jumpToPage(pending);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
assert(c.read(pdfViewModelProvider).currentPage == pending);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import '_world.dart';
|
import '_world.dart';
|
||||||
|
|
||||||
|
|
@ -11,9 +11,6 @@ Future<void> theUserClicksTheThumbnailForPage(
|
||||||
) async {
|
) async {
|
||||||
final page = param1.toInt();
|
final page = param1.toInt();
|
||||||
final c = TestWorld.container ?? ProviderContainer();
|
final c = TestWorld.container ?? ProviderContainer();
|
||||||
try {
|
|
||||||
c.read(currentPageProvider.notifier).state = page;
|
|
||||||
} catch (_) {}
|
|
||||||
try {
|
try {
|
||||||
c.read(pdfViewModelProvider.notifier).jumpToPage(page);
|
c.read(pdfViewModelProvider.notifier).jumpToPage(page);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ Future<void> theUserDeletesOneSelectedSignaturePlacement(
|
||||||
) async {
|
) async {
|
||||||
final container = TestWorld.container ?? ProviderContainer();
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
TestWorld.container = container;
|
TestWorld.container = container;
|
||||||
final currentPage = container.read(pdfViewModelProvider);
|
final currentPage = container.read(pdfViewModelProvider).currentPage;
|
||||||
final placements = container
|
final placements = container
|
||||||
.read(documentRepositoryProvider.notifier)
|
.read(documentRepositoryProvider.notifier)
|
||||||
.placementsOn(currentPage);
|
.placementsOn(currentPage);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ Future<void> theUserDragsHandlesToResizeAndDragsToReposition(
|
||||||
final container = TestWorld.container ?? ProviderContainer();
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
TestWorld.container = container;
|
TestWorld.container = container;
|
||||||
final pdfN = container.read(documentRepositoryProvider.notifier);
|
final pdfN = container.read(documentRepositoryProvider.notifier);
|
||||||
final currentPage = container.read(pdfViewModelProvider);
|
final currentPage = container.read(pdfViewModelProvider).currentPage;
|
||||||
|
|
||||||
final placements = pdfN.placementsOn(currentPage);
|
final placements = pdfN.placementsOn(currentPage);
|
||||||
if (placements.isNotEmpty) {
|
if (placements.isNotEmpty) {
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ theUserDragsThisSignatureCardOnThePageOfTheDocumentToPlaceASignaturePlacement(
|
||||||
final drop_card = temp_card;
|
final drop_card = temp_card;
|
||||||
|
|
||||||
// Place it on the current page
|
// Place it on the current page
|
||||||
final currentPage = container.read(pdfViewModelProvider);
|
final currentPage = container.read(pdfViewModelProvider).currentPage;
|
||||||
container
|
container
|
||||||
.read(documentRepositoryProvider.notifier)
|
.read(documentRepositoryProvider.notifier)
|
||||||
.addPlacement(
|
.addPlacement(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import '_world.dart';
|
import '_world.dart';
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ Future<void> theUserEntersIntoTheGoToInputAndAppliesIt(
|
||||||
final clamped =
|
final clamped =
|
||||||
value < 1 ? 1 : value; // upper bound validated in last-page check step
|
value < 1 ? 1 : value; // upper bound validated in last-page check step
|
||||||
try {
|
try {
|
||||||
c.read(currentPageProvider.notifier).state = clamped;
|
c.read(pdfViewModelProvider.notifier).jumpToPage(clamped);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
try {
|
try {
|
||||||
c.read(pdfViewModelProvider.notifier).jumpToPage(clamped);
|
c.read(pdfViewModelProvider.notifier).jumpToPage(clamped);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import '_world.dart';
|
import '_world.dart';
|
||||||
|
|
||||||
|
|
@ -9,10 +9,7 @@ Future<void> theUserJumpsToPage(WidgetTester tester, num param1) async {
|
||||||
final page = param1.toInt();
|
final page = param1.toInt();
|
||||||
final c = TestWorld.container ?? ProviderContainer();
|
final c = TestWorld.container ?? ProviderContainer();
|
||||||
try {
|
try {
|
||||||
c.read(currentPageProvider.notifier).state = page;
|
c.read(pdfViewModelProvider).jumpToPage(page);
|
||||||
} catch (_) {}
|
|
||||||
try {
|
|
||||||
c.read(pdfViewModelProvider.notifier).jumpToPage(page);
|
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
import 'package:pdf_signature/domain/models/model.dart';
|
import 'package:pdf_signature/domain/models/model.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import '_world.dart';
|
import '_world.dart';
|
||||||
|
|
||||||
|
|
@ -17,9 +16,6 @@ Future<void> theUserNavigatesToPageAndPlacesAnotherSignaturePlacement(
|
||||||
TestWorld.container = container;
|
TestWorld.container = container;
|
||||||
final page = param1.toInt();
|
final page = param1.toInt();
|
||||||
// Update page providers directly (repository jumpTo is a no-op now)
|
// Update page providers directly (repository jumpTo is a no-op now)
|
||||||
try {
|
|
||||||
container.read(currentPageProvider.notifier).state = page;
|
|
||||||
} catch (_) {}
|
|
||||||
try {
|
try {
|
||||||
container.read(pdfViewModelProvider.notifier).jumpToPage(page);
|
container.read(pdfViewModelProvider.notifier).jumpToPage(page);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ Future<void> theUserPlacesTwoSignaturePlacementsOnTheSamePage(
|
||||||
final container = TestWorld.container ?? ProviderContainer();
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
TestWorld.container = container;
|
TestWorld.container = container;
|
||||||
// pdfViewModelProvider returns 1-based current page
|
// pdfViewModelProvider returns 1-based current page
|
||||||
final page = container.read(pdfViewModelProvider);
|
final page = container.read(pdfViewModelProvider).currentPage;
|
||||||
container
|
container
|
||||||
.read(documentRepositoryProvider.notifier)
|
.read(documentRepositoryProvider.notifier)
|
||||||
.addPlacement(
|
.addPlacement(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import '_world.dart';
|
import '_world.dart';
|
||||||
|
|
||||||
|
|
@ -12,9 +12,6 @@ Future<void> theUserTypesIntoTheGoToInputAndPressesEnter(
|
||||||
final target = param1.toInt();
|
final target = param1.toInt();
|
||||||
final c = TestWorld.container ?? ProviderContainer();
|
final c = TestWorld.container ?? ProviderContainer();
|
||||||
TestWorld.container = c;
|
TestWorld.container = c;
|
||||||
try {
|
|
||||||
c.read(currentPageProvider.notifier).state = target;
|
|
||||||
} catch (_) {}
|
|
||||||
try {
|
try {
|
||||||
c.read(pdfViewModelProvider.notifier).jumpToPage(target);
|
c.read(pdfViewModelProvider.notifier).jumpToPage(target);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import '_world.dart';
|
||||||
Future<void> theUserUsesRotateControls(WidgetTester tester) async {
|
Future<void> theUserUsesRotateControls(WidgetTester tester) async {
|
||||||
final container = TestWorld.container ?? ProviderContainer();
|
final container = TestWorld.container ?? ProviderContainer();
|
||||||
final pdfN = container.read(documentRepositoryProvider.notifier);
|
final pdfN = container.read(documentRepositoryProvider.notifier);
|
||||||
final currentPage = container.read(pdfViewModelProvider);
|
final currentPage = container.read(pdfViewModelProvider).currentPage;
|
||||||
final placements = pdfN.placementsOn(currentPage);
|
final placements = pdfN.placementsOn(currentPage);
|
||||||
if (placements.isNotEmpty) {
|
if (placements.isNotEmpty) {
|
||||||
pdfN.updatePlacementRotation(
|
pdfN.updatePlacementRotation(
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ Future<void> threeSignaturePlacementsArePlacedOnTheCurrentPage(
|
||||||
];
|
];
|
||||||
container.read(documentRepositoryProvider.notifier).openPicked(pageCount: 5);
|
container.read(documentRepositoryProvider.notifier).openPicked(pageCount: 5);
|
||||||
final pdfN = container.read(documentRepositoryProvider.notifier);
|
final pdfN = container.read(documentRepositoryProvider.notifier);
|
||||||
final page = container.read(pdfViewModelProvider);
|
final page = container.read(pdfViewModelProvider).currentPage;
|
||||||
pdfN.addPlacement(
|
pdfN.addPlacement(
|
||||||
page: page,
|
page: page,
|
||||||
rect: Rect.fromLTWH(10, 10, 50, 50),
|
rect: Rect.fromLTWH(10, 10, 50, 50),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:file_selector/file_selector.dart' as fs;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
@ -8,7 +9,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:pdf_signature/data/services/export_service.dart';
|
import 'package:pdf_signature/data/services/export_service.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/ui_services.dart';
|
import 'package:pdf_signature/ui/features/pdf/widgets/ui_services.dart';
|
||||||
import 'package:pdf_signature/data/repositories/preferences_repository.dart';
|
import 'package:pdf_signature/data/repositories/preferences_repository.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
|
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
||||||
|
|
@ -58,7 +59,9 @@ void main() {
|
||||||
DocumentStateNotifier()
|
DocumentStateNotifier()
|
||||||
..openPicked(pageCount: 5, bytes: Uint8List(0)),
|
..openPicked(pageCount: 5, bytes: Uint8List(0)),
|
||||||
),
|
),
|
||||||
useMockViewerProvider.overrideWith((ref) => true),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: true),
|
||||||
|
),
|
||||||
exportServiceProvider.overrideWith((_) => fake),
|
exportServiceProvider.overrideWith((_) => fake),
|
||||||
savePathPickerProvider.overrideWith(
|
savePathPickerProvider.overrideWith(
|
||||||
(_) => () async => 'C:/tmp/output.pdf',
|
(_) => () async => 'C:/tmp/output.pdf',
|
||||||
|
|
@ -67,7 +70,11 @@ void main() {
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
home: const PdfSignatureHomePage(),
|
home: PdfSignatureHomePage(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onClosePdf: () {},
|
||||||
|
currentFile: fs.XFile(''),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:file_selector/file_selector.dart' as fs;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:image/image.dart' as img;
|
import 'package:image/image.dart' as img;
|
||||||
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/ui_services.dart';
|
import 'package:pdf_signature/ui/features/pdf/widgets/ui_services.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
|
import 'package:pdf_signature/data/repositories/signature_asset_repository.dart';
|
||||||
|
|
@ -22,13 +23,19 @@ Future<void> pumpWithOpenPdf(WidgetTester tester) async {
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) => DocumentStateNotifier()..openSample(),
|
(ref) => DocumentStateNotifier()..openSample(),
|
||||||
),
|
),
|
||||||
useMockViewerProvider.overrideWithValue(true),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: true),
|
||||||
|
),
|
||||||
exportingProvider.overrideWith((ref) => false),
|
exportingProvider.overrideWith((ref) => false),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
home: const PdfSignatureHomePage(),
|
home: PdfSignatureHomePage(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onClosePdf: () {},
|
||||||
|
currentFile: fs.XFile(''),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -388,13 +395,19 @@ Future<void> pumpWithOpenPdfAndSig(WidgetTester tester) async {
|
||||||
return cardRepo;
|
return cardRepo;
|
||||||
}),
|
}),
|
||||||
// In new model, interactive overlay not implemented; keep library empty
|
// In new model, interactive overlay not implemented; keep library empty
|
||||||
useMockViewerProvider.overrideWithValue(true),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: true),
|
||||||
|
),
|
||||||
exportingProvider.overrideWith((ref) => false),
|
exportingProvider.overrideWith((ref) => false),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
home: const PdfSignatureHomePage(),
|
home: PdfSignatureHomePage(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onClosePdf: () {},
|
||||||
|
currentFile: fs.XFile(''),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
|
import 'package:file_selector/file_selector.dart' as fs;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
||||||
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
import 'package:pdf_signature/domain/models/model.dart';
|
import 'package:pdf_signature/domain/models/model.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
|
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
@ -23,7 +24,9 @@ void main() {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
useMockViewerProvider.overrideWithValue(true),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: true),
|
||||||
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) => _TestPdfController(),
|
(ref) => _TestPdfController(),
|
||||||
),
|
),
|
||||||
|
|
@ -32,7 +35,11 @@ void main() {
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
locale: const Locale('en'),
|
locale: const Locale('en'),
|
||||||
home: const PdfSignatureHomePage(),
|
home: PdfSignatureHomePage(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onClosePdf: () {},
|
||||||
|
currentFile: fs.XFile(''),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:pdfrx/pdfrx.dart';
|
||||||
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_page_area.dart';
|
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_page_area.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
|
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
|
|
@ -25,14 +26,16 @@ void main() {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
useMockViewerProvider.overrideWithValue(true),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: true),
|
||||||
|
),
|
||||||
documentRepositoryProvider.overrideWith((ref) => ctrl),
|
documentRepositoryProvider.overrideWith((ref) => ctrl),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
locale: const Locale('en'),
|
locale: const Locale('en'),
|
||||||
home: const Scaffold(
|
home: Scaffold(
|
||||||
body: Center(
|
body: Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 800,
|
width: 800,
|
||||||
|
|
@ -44,6 +47,7 @@ void main() {
|
||||||
onConfirmSignature: _noop,
|
onConfirmSignature: _noop,
|
||||||
onClearActiveOverlay: _noop,
|
onClearActiveOverlay: _noop,
|
||||||
onSelectPlaced: _noopInt,
|
onSelectPlaced: _noopInt,
|
||||||
|
controller: PdfViewerController(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:pdfrx/pdfrx.dart';
|
||||||
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_page_area.dart';
|
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_page_area.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
|
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
|
|
@ -25,7 +26,9 @@ void main() {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
useMockViewerProvider.overrideWithValue(true),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: true),
|
||||||
|
),
|
||||||
// Continuous mode is always-on; no page view override needed
|
// Continuous mode is always-on; no page view override needed
|
||||||
documentRepositoryProvider.overrideWith((ref) => ctrl),
|
documentRepositoryProvider.overrideWith((ref) => ctrl),
|
||||||
],
|
],
|
||||||
|
|
@ -33,7 +36,7 @@ void main() {
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
locale: const Locale('en'),
|
locale: const Locale('en'),
|
||||||
home: const Scaffold(
|
home: Scaffold(
|
||||||
body: Center(
|
body: Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 800,
|
width: 800,
|
||||||
|
|
@ -45,6 +48,7 @@ void main() {
|
||||||
onConfirmSignature: _noop,
|
onConfirmSignature: _noop,
|
||||||
onClearActiveOverlay: _noop,
|
onClearActiveOverlay: _noop,
|
||||||
onSelectPlaced: _noopInt,
|
onSelectPlaced: _noopInt,
|
||||||
|
controller: PdfViewerController(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_page_area.dart';
|
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_page_area.dart';
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_providers.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
|
import 'package:pdfrx/pdfrx.dart';
|
||||||
|
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
import 'package:pdf_signature/domain/models/model.dart';
|
import 'package:pdf_signature/domain/models/model.dart';
|
||||||
|
|
@ -24,7 +25,9 @@ void main() {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
useMockViewerProvider.overrideWithValue(true),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: true),
|
||||||
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) => _TestPdfController(),
|
(ref) => _TestPdfController(),
|
||||||
),
|
),
|
||||||
|
|
@ -33,18 +36,19 @@ void main() {
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
locale: const Locale('en'),
|
locale: const Locale('en'),
|
||||||
home: const Scaffold(
|
home: Scaffold(
|
||||||
body: Center(
|
body: Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 520,
|
height: 520,
|
||||||
child: PdfPageArea(
|
child: PdfPageArea(
|
||||||
pageSize: Size(676, 400),
|
pageSize: const Size(676, 400),
|
||||||
onDragSignature: _noopOffset,
|
onDragSignature: _noopOffset,
|
||||||
onResizeSignature: _noopOffset,
|
onResizeSignature: _noopOffset,
|
||||||
onConfirmSignature: _noop,
|
onConfirmSignature: _noop,
|
||||||
onClearActiveOverlay: _noop,
|
onClearActiveOverlay: _noop,
|
||||||
onSelectPlaced: _noopInt,
|
onSelectPlaced: _noopInt,
|
||||||
|
controller: PdfViewerController(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -66,7 +70,9 @@ void main() {
|
||||||
// Use a persistent container across rebuilds
|
// Use a persistent container across rebuilds
|
||||||
final container = ProviderContainer(
|
final container = ProviderContainer(
|
||||||
overrides: [
|
overrides: [
|
||||||
useMockViewerProvider.overrideWithValue(true),
|
pdfViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfViewModel(ref, useMockViewer: true),
|
||||||
|
),
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith(
|
||||||
(ref) => DocumentStateNotifier()..openSample(),
|
(ref) => DocumentStateNotifier()..openSample(),
|
||||||
),
|
),
|
||||||
|
|
@ -83,13 +89,14 @@ void main() {
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: width,
|
width: width,
|
||||||
// Keep aspect ratio consistent with uiPageSize
|
// Keep aspect ratio consistent with uiPageSize
|
||||||
child: const PdfPageArea(
|
child: PdfPageArea(
|
||||||
pageSize: uiPageSize,
|
pageSize: uiPageSize,
|
||||||
onDragSignature: _noopOffset,
|
onDragSignature: _noopOffset,
|
||||||
onResizeSignature: _noopOffset,
|
onResizeSignature: _noopOffset,
|
||||||
onConfirmSignature: _noop,
|
onConfirmSignature: _noop,
|
||||||
onClearActiveOverlay: _noop,
|
onClearActiveOverlay: _noop,
|
||||||
onSelectPlaced: _noopInt,
|
onSelectPlaced: _noopInt,
|
||||||
|
controller: PdfViewerController(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,15 @@ void main() {
|
||||||
tester,
|
tester,
|
||||||
) async {
|
) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const ProviderScope(
|
ProviderScope(
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
home: WelcomeScreen(),
|
home: WelcomeScreen(
|
||||||
|
onPickPdf: () async {},
|
||||||
|
onOpenPdf:
|
||||||
|
({String? path, Uint8List? bytes, String? fileName}) async {},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -39,8 +43,16 @@ void main() {
|
||||||
final bytes = Uint8List.fromList([1, 2, 3, 4]);
|
final bytes = Uint8List.fromList([1, 2, 3, 4]);
|
||||||
final fake = _FakeDropReadable('sample.pdf', '/tmp/sample.pdf', bytes);
|
final fake = _FakeDropReadable('sample.pdf', '/tmp/sample.pdf', bytes);
|
||||||
|
|
||||||
// Use the top-level helper with the WidgetRef.read function
|
// Call handleDroppedFiles with the onOpenPdf callback from the widget
|
||||||
await handleDroppedFiles(stateful.ref.read, [fake]);
|
await handleDroppedFiles(({
|
||||||
|
String? path,
|
||||||
|
Uint8List? bytes,
|
||||||
|
String? fileName,
|
||||||
|
}) async {
|
||||||
|
final container = ProviderScope.containerOf(stateful.context);
|
||||||
|
final repo = container.read(documentRepositoryProvider.notifier);
|
||||||
|
repo.openPicked(pageCount: 1, bytes: bytes);
|
||||||
|
}, [fake]);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
|
|
||||||
final container = ProviderScope.containerOf(stateful.context);
|
final container = ProviderScope.containerOf(stateful.context);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue