refactor: ui_services.dart to PdfExportViewModel for export functionality
This commit is contained in:
parent
eee75f6fdb
commit
0c38178502
|
|
@ -14,7 +14,7 @@ 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/widgets/ui_services.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_export_view_model.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';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
@ -77,12 +77,15 @@ void main() {
|
||||||
pdfViewModelProvider.overrideWith(
|
pdfViewModelProvider.overrideWith(
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
(ref) => PdfViewModel(ref, useMockViewer: false),
|
||||||
),
|
),
|
||||||
exportServiceProvider.overrideWith((_) => fake),
|
pdfExportViewModelProvider.overrideWith(
|
||||||
savePathPickerProvider.overrideWith(
|
(ref) => PdfExportViewModel(
|
||||||
(_) => () async {
|
ref,
|
||||||
final dir = Directory.systemTemp.createTempSync('pdfsig_');
|
exporter: fake,
|
||||||
return '${dir.path}/output.pdf';
|
savePathPicker: () async {
|
||||||
},
|
final dir = Directory.systemTemp.createTempSync('pdfsig_');
|
||||||
|
return '${dir.path}/output.pdf';
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
|
|
@ -432,12 +435,17 @@ void main() {
|
||||||
pdfViewModelProvider.overrideWith(
|
pdfViewModelProvider.overrideWith(
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: false),
|
(ref) => PdfViewModel(ref, useMockViewer: false),
|
||||||
),
|
),
|
||||||
exportServiceProvider.overrideWith((ref) => LightweightExporter()),
|
pdfExportViewModelProvider.overrideWith(
|
||||||
savePathPickerProvider.overrideWith(
|
(ref) => PdfExportViewModel(
|
||||||
(_) => () async {
|
ref,
|
||||||
final dir = Directory.systemTemp.createTempSync('pdfsig_after_');
|
exporter: LightweightExporter(),
|
||||||
return '${dir.path}/output-after-export.pdf';
|
savePathPicker: () async {
|
||||||
},
|
final dir = Directory.systemTemp.createTempSync(
|
||||||
|
'pdfsig_after_',
|
||||||
|
);
|
||||||
|
return '${dir.path}/output-after-export.pdf';
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
import 'package:file_selector/file_selector.dart' as fs;
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:pdf_signature/data/services/export_service.dart';
|
||||||
|
|
||||||
|
/// ViewModel for export-related UI state and helpers.
|
||||||
|
class PdfExportViewModel extends ChangeNotifier {
|
||||||
|
final Ref ref;
|
||||||
|
bool _exporting = false;
|
||||||
|
|
||||||
|
// Dependencies (injectable via constructor for tests)
|
||||||
|
final ExportService _exporter;
|
||||||
|
final Future<String?> Function() _savePathPicker;
|
||||||
|
|
||||||
|
PdfExportViewModel(
|
||||||
|
this.ref, {
|
||||||
|
ExportService? exporter,
|
||||||
|
Future<String?> Function()? savePathPicker,
|
||||||
|
}) : _exporter = exporter ?? ExportService(),
|
||||||
|
_savePathPicker = savePathPicker ?? _defaultSavePathPicker;
|
||||||
|
|
||||||
|
bool get exporting => _exporting;
|
||||||
|
|
||||||
|
void setExporting(bool value) {
|
||||||
|
if (_exporting == value) return;
|
||||||
|
_exporting = value;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the export service (overridable in tests via constructor).
|
||||||
|
ExportService get exporter => _exporter;
|
||||||
|
|
||||||
|
/// Show save dialog and return the chosen path (null if canceled).
|
||||||
|
Future<String?> pickSavePath() async {
|
||||||
|
return _savePathPicker();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<String?> _defaultSavePathPicker() async {
|
||||||
|
final group = fs.XTypeGroup(label: 'PDF', extensions: ['pdf']);
|
||||||
|
final location = await fs.getSaveLocation(
|
||||||
|
acceptedTypeGroups: [group],
|
||||||
|
suggestedName: 'signed.pdf',
|
||||||
|
confirmButtonText: 'Save',
|
||||||
|
);
|
||||||
|
return location?.path; // null if user cancels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final pdfExportViewModelProvider = ChangeNotifierProvider<PdfExportViewModel>((
|
||||||
|
ref,
|
||||||
|
) {
|
||||||
|
return PdfExportViewModel(ref);
|
||||||
|
});
|
||||||
|
|
@ -12,7 +12,7 @@ import 'pdf_toolbar.dart';
|
||||||
import 'pdf_page_area.dart';
|
import 'pdf_page_area.dart';
|
||||||
import 'pages_sidebar.dart';
|
import 'pages_sidebar.dart';
|
||||||
import 'signatures_sidebar.dart';
|
import 'signatures_sidebar.dart';
|
||||||
import 'ui_services.dart';
|
import '../view_model/pdf_export_view_model.dart';
|
||||||
import 'package:pdf_signature/utils/download.dart';
|
import 'package:pdf_signature/utils/download.dart';
|
||||||
import '../view_model/pdf_view_model.dart';
|
import '../view_model/pdf_view_model.dart';
|
||||||
|
|
||||||
|
|
@ -133,7 +133,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _saveSignedPdf() async {
|
Future<void> _saveSignedPdf() async {
|
||||||
ref.read(exportingProvider.notifier).state = true;
|
ref.read(pdfExportViewModelProvider.notifier).setExporting(true);
|
||||||
try {
|
try {
|
||||||
final pdf = _viewModel.document;
|
final pdf = _viewModel.document;
|
||||||
final messenger = ScaffoldMessenger.of(context);
|
final messenger = ScaffoldMessenger.of(context);
|
||||||
|
|
@ -145,7 +145,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final exporter = ref.read(exportServiceProvider);
|
final exporter = ref.read(pdfExportViewModelProvider).exporter;
|
||||||
|
|
||||||
// get DPI from preferences
|
// get DPI from preferences
|
||||||
final targetDpi = ref.read(preferencesRepositoryProvider).exportDpi;
|
final targetDpi = ref.read(preferencesRepositoryProvider).exportDpi;
|
||||||
|
|
@ -153,8 +153,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
String? savedPath;
|
String? savedPath;
|
||||||
|
|
||||||
if (!kIsWeb) {
|
if (!kIsWeb) {
|
||||||
final pick = ref.read(savePathPickerProvider);
|
final path = await ref.read(pdfExportViewModelProvider).pickSavePath();
|
||||||
final path = await pick();
|
|
||||||
if (path == null || path.trim().isEmpty) return;
|
if (path == null || path.trim().isEmpty) return;
|
||||||
final fullPath = _ensurePdfExtension(path.trim());
|
final fullPath = _ensurePdfExtension(path.trim());
|
||||||
savedPath = fullPath;
|
savedPath = fullPath;
|
||||||
|
|
@ -216,7 +215,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
ref.read(exportingProvider.notifier).state = false;
|
ref.read(pdfExportViewModelProvider.notifier).setExporting(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -362,7 +361,7 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildScaffold(BuildContext context) {
|
Widget _buildScaffold(BuildContext context) {
|
||||||
final isExporting = ref.watch(exportingProvider);
|
final isExporting = ref.watch(pdfExportViewModelProvider).exporting;
|
||||||
final l = AppLocalizations.of(context);
|
final l = AppLocalizations.of(context);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Padding(
|
body: Padding(
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ 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/widgets/signature_drawer.dart';
|
import '../../signature/widgets/signature_drawer.dart';
|
||||||
import 'ui_services.dart';
|
import '../view_model/pdf_export_view_model.dart';
|
||||||
|
|
||||||
class SignaturesSidebar extends ConsumerWidget {
|
class SignaturesSidebar extends ConsumerWidget {
|
||||||
const SignaturesSidebar({
|
const SignaturesSidebar({
|
||||||
|
|
@ -21,7 +21,7 @@ class SignaturesSidebar extends ConsumerWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final l = AppLocalizations.of(context);
|
final l = AppLocalizations.of(context);
|
||||||
final isExporting = ref.watch(exportingProvider);
|
final isExporting = ref.watch(pdfExportViewModelProvider).exporting;
|
||||||
return AbsorbPointer(
|
return AbsorbPointer(
|
||||||
absorbing: isExporting,
|
absorbing: isExporting,
|
||||||
child: Card(
|
child: Card(
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import 'package:file_selector/file_selector.dart' as fs;
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:pdf_signature/data/services/export_service.dart';
|
|
||||||
|
|
||||||
/// Global exporting flag used to disable parts of the UI during long tasks.
|
|
||||||
final exportingProvider = StateProvider<bool>((ref) => false);
|
|
||||||
|
|
||||||
/// Provider for the export service. Can be overridden in tests.
|
|
||||||
final exportServiceProvider = Provider<ExportService>((ref) => ExportService());
|
|
||||||
|
|
||||||
/// Provider for a function that picks a save path. Tests may override.
|
|
||||||
final savePathPickerProvider = Provider<Future<String?> Function()>((ref) {
|
|
||||||
return () async {
|
|
||||||
// Desktop save dialog with PDF filter; mobile platforms may not support this.
|
|
||||||
final group = fs.XTypeGroup(label: 'PDF', extensions: ['pdf']);
|
|
||||||
final location = await fs.getSaveLocation(
|
|
||||||
acceptedTypeGroups: [group],
|
|
||||||
suggestedName: 'signed.pdf',
|
|
||||||
confirmButtonText: 'Save',
|
|
||||||
);
|
|
||||||
return location?.path; // null if user cancels
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
@ -7,7 +7,7 @@ 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/widgets/ui_services.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_export_view_model.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';
|
||||||
|
|
||||||
|
|
@ -51,8 +51,13 @@ Future<ProviderContainer> pumpApp(
|
||||||
pdfViewModelProvider.overrideWith(
|
pdfViewModelProvider.overrideWith(
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: true),
|
(ref) => PdfViewModel(ref, useMockViewer: true),
|
||||||
),
|
),
|
||||||
exportServiceProvider.overrideWith((ref) => fakeExport),
|
pdfExportViewModelProvider.overrideWith(
|
||||||
savePathPickerProvider.overrideWith((ref) => () async => 'out.pdf'),
|
(ref) => PdfExportViewModel(
|
||||||
|
ref,
|
||||||
|
exporter: fakeExport,
|
||||||
|
savePathPicker: () async => 'out.pdf',
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
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/view_model/pdf_export_view_model.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_view_model.dart';
|
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
|
||||||
|
|
||||||
|
|
@ -62,9 +62,12 @@ void main() {
|
||||||
pdfViewModelProvider.overrideWith(
|
pdfViewModelProvider.overrideWith(
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: true),
|
(ref) => PdfViewModel(ref, useMockViewer: true),
|
||||||
),
|
),
|
||||||
exportServiceProvider.overrideWith((_) => fake),
|
pdfExportViewModelProvider.overrideWith(
|
||||||
savePathPickerProvider.overrideWith(
|
(ref) => PdfExportViewModel(
|
||||||
(_) => () async => 'C:/tmp/output.pdf',
|
ref,
|
||||||
|
exporter: fake,
|
||||||
|
savePathPicker: () async => 'C:/tmp/output.pdf',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ 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_view_model.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/view_model/pdf_export_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/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';
|
||||||
|
|
@ -26,7 +26,9 @@ Future<void> pumpWithOpenPdf(WidgetTester tester) async {
|
||||||
pdfViewModelProvider.overrideWith(
|
pdfViewModelProvider.overrideWith(
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: true),
|
(ref) => PdfViewModel(ref, useMockViewer: true),
|
||||||
),
|
),
|
||||||
exportingProvider.overrideWith((ref) => false),
|
pdfExportViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfExportViewModel(ref),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
|
|
@ -398,7 +400,9 @@ Future<void> pumpWithOpenPdfAndSig(WidgetTester tester) async {
|
||||||
pdfViewModelProvider.overrideWith(
|
pdfViewModelProvider.overrideWith(
|
||||||
(ref) => PdfViewModel(ref, useMockViewer: true),
|
(ref) => PdfViewModel(ref, useMockViewer: true),
|
||||||
),
|
),
|
||||||
exportingProvider.overrideWith((ref) => false),
|
pdfExportViewModelProvider.overrideWith(
|
||||||
|
(ref) => PdfExportViewModel(ref),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue