test: resolve error for integration test after upgrade to riverpod3

This commit is contained in:
insleker 2025-12-15 13:33:02 +08:00
parent d6bb64b3bc
commit 473b957587
5 changed files with 200 additions and 195 deletions

View File

@ -1,4 +1,5 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:cross_file/cross_file.dart';
import 'package:flutter/material.dart';
@ -8,6 +9,7 @@ import 'package:integration_test/integration_test.dart';
import 'package:pdf_signature/data/repositories/document_repository.dart';
import 'package:pdf_signature/data/repositories/preferences_repository.dart';
import 'package:pdf_signature/data/services/export_service.dart';
import 'package:pdf_signature/domain/models/document.dart';
import 'package:pdf_signature/l10n/app_localizations.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_export_view_model.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.dart';
@ -18,6 +20,23 @@ import 'package:shared_preferences/shared_preferences.dart';
// Note: We use the real ExportService via the repository; no mocks here.
class _PreloadedDocumentStateNotifier extends DocumentStateNotifier {
_PreloadedDocumentStateNotifier({
required this.bytes,
required this.pageCount,
ExportService? service,
}) : super(service: service);
final Uint8List bytes;
final int pageCount;
@override
Document build() {
super.build();
return Document(loaded: true, pageCount: pageCount, pickedPdfBytes: bytes);
}
}
void main() {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
@ -34,28 +53,28 @@ void main() {
ProviderScope(
overrides: [
preferencesRepositoryProvider.overrideWith(
(ref) => PreferencesStateNotifier(prefs),
() => PreferencesStateNotifier(prefs),
),
documentRepositoryProvider.overrideWith(
(ref) => DocumentStateNotifier(service: ExportService())
..openDocument(
bytes: pdfBytes,
pageCount: 3,
knownPageCount: true,
),
),
pdfViewModelProvider.overrideWith(
(ref) => PdfViewModel(ref, useMockViewer: false),
() => _PreloadedDocumentStateNotifier(
bytes: pdfBytes,
pageCount: 3,
service: ExportService(),
),
),
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
// Disable overlays to avoid long-lived overlay animations in CI
viewerOverlaysEnabledProvider.overrideWith((ref) => false),
pdfExportViewModelProvider.overrideWith(
(ref) => PdfExportViewModel(
ref,
() => PdfExportViewModel(
savePathPicker: () async {
final dir = Directory.systemTemp.createTempSync('pdfsig_');
return '${dir.path}/output.pdf';
},
savePathPickerWithSuggestedName: (_) async {
final dir = Directory.systemTemp.createTempSync('pdfsig_');
return '${dir.path}/output.pdf';
},
),
),
],
@ -93,28 +112,30 @@ void main() {
ProviderScope(
overrides: [
preferencesRepositoryProvider.overrideWith(
(ref) => PreferencesStateNotifier(prefs),
() => PreferencesStateNotifier(prefs),
),
documentRepositoryProvider.overrideWith(
(ref) => DocumentStateNotifier(service: ExportService())
..openDocument(
bytes: pdfBytes,
pageCount: 3,
knownPageCount: true,
),
),
pdfViewModelProvider.overrideWith(
(ref) => PdfViewModel(ref, useMockViewer: false),
() => _PreloadedDocumentStateNotifier(
bytes: pdfBytes,
pageCount: 3,
service: ExportService(),
),
),
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
pdfExportViewModelProvider.overrideWith(
(ref) => PdfExportViewModel(
ref,
() => PdfExportViewModel(
savePathPicker: () async {
final dir = Directory.systemTemp.createTempSync(
'pdfsig_linux_',
);
return '${dir.path}/out.pdf';
},
savePathPickerWithSuggestedName: (_) async {
final dir = Directory.systemTemp.createTempSync(
'pdfsig_linux_',
);
return '${dir.path}/out.pdf';
},
),
),
],
@ -165,20 +186,16 @@ void main() {
ProviderScope(
overrides: [
preferencesRepositoryProvider.overrideWith(
(ref) => PreferencesStateNotifier(prefs),
() => PreferencesStateNotifier(prefs),
),
documentRepositoryProvider.overrideWith(
(ref) => DocumentStateNotifier(
service: ExportService(enableRaster: false),
)..openDocument(
() => _PreloadedDocumentStateNotifier(
bytes: pdfBytes,
pageCount: 3,
knownPageCount: true,
service: ExportService(enableRaster: false),
),
),
pdfViewModelProvider.overrideWith(
(ref) => PdfViewModel(ref, useMockViewer: false),
),
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
],
child: MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
@ -215,20 +232,16 @@ void main() {
ProviderScope(
overrides: [
preferencesRepositoryProvider.overrideWith(
(ref) => PreferencesStateNotifier(prefs),
() => PreferencesStateNotifier(prefs),
),
documentRepositoryProvider.overrideWith(
(ref) => DocumentStateNotifier(
service: ExportService(enableRaster: false),
)..openDocument(
() => _PreloadedDocumentStateNotifier(
bytes: pdfBytes,
pageCount: 3,
knownPageCount: true,
service: ExportService(enableRaster: false),
),
),
pdfViewModelProvider.overrideWith(
(ref) => PdfViewModel(ref, useMockViewer: false),
),
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
],
child: MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
@ -268,20 +281,16 @@ void main() {
ProviderScope(
overrides: [
preferencesRepositoryProvider.overrideWith(
(ref) => PreferencesStateNotifier(prefs),
() => PreferencesStateNotifier(prefs),
),
documentRepositoryProvider.overrideWith(
(ref) => DocumentStateNotifier(
service: ExportService(enableRaster: false),
)..openDocument(
() => _PreloadedDocumentStateNotifier(
bytes: pdfBytes,
pageCount: 3,
knownPageCount: true,
service: ExportService(enableRaster: false),
),
),
pdfViewModelProvider.overrideWith(
(ref) => PdfViewModel(ref, useMockViewer: false),
),
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
],
child: MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
@ -304,10 +313,9 @@ void main() {
expect(pagesSidebar, findsOneWidget);
// Scroll to make page 3 thumbnail visible
await tester.drag(pagesSidebar, const Offset(0, -300));
await tester.pumpAndSettle();
final page3Thumb = find.text('3');
await tester.ensureVisible(page3Thumb);
await tester.pumpAndSettle();
expect(page3Thumb, findsOneWidget);
await tester.tap(page3Thumb);
await tester.pumpAndSettle();
@ -324,19 +332,16 @@ void main() {
ProviderScope(
overrides: [
preferencesRepositoryProvider.overrideWith(
(ref) => PreferencesStateNotifier(prefs),
() => PreferencesStateNotifier(prefs),
),
documentRepositoryProvider.overrideWith(
(ref) => DocumentStateNotifier(service: ExportService())
..openDocument(
bytes: pdfBytes,
pageCount: 3,
knownPageCount: true,
),
),
pdfViewModelProvider.overrideWith(
(ref) => PdfViewModel(ref, useMockViewer: false),
() => _PreloadedDocumentStateNotifier(
bytes: pdfBytes,
pageCount: 3,
service: ExportService(),
),
),
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
],
child: MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
@ -379,31 +384,33 @@ void main() {
ProviderScope(
overrides: [
preferencesRepositoryProvider.overrideWith(
(ref) => PreferencesStateNotifier(prefs),
() => PreferencesStateNotifier(prefs),
),
documentRepositoryProvider.overrideWith(
(ref) => DocumentStateNotifier(service: ExportService())
..openDocument(
bytes: pdfBytes,
pageCount: 3,
knownPageCount: true,
),
),
pdfViewModelProvider.overrideWith(
(ref) => PdfViewModel(ref, useMockViewer: false),
() => _PreloadedDocumentStateNotifier(
bytes: pdfBytes,
pageCount: 3,
service: ExportService(),
),
),
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
// Disable overlays to reduce post-export timers/animations.
viewerOverlaysEnabledProvider.overrideWith((ref) => false),
// Override only save path picker to avoid native dialogs; use real exporter
pdfExportViewModelProvider.overrideWith(
(ref) => PdfExportViewModel(
ref,
() => PdfExportViewModel(
savePathPicker: () async {
final dir = Directory.systemTemp.createTempSync(
'pdfsig_after_',
);
return '${dir.path}/output-after-export.pdf';
},
savePathPickerWithSuggestedName: (_) async {
final dir = Directory.systemTemp.createTempSync(
'pdfsig_after_',
);
return '${dir.path}/output-after-export.pdf';
},
),
),
],

View File

@ -1,19 +1,37 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:cross_file/cross_file.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'dart:io';
import 'package:cross_file/cross_file.dart';
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.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:shared_preferences/shared_preferences.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/preferences_repository.dart';
import 'package:pdf_signature/domain/models/document.dart';
import 'package:pdf_signature/l10n/app_localizations.dart';
import 'package:pdf_signature/ui/features/pdf/view_model/pdf_view_model.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:shared_preferences/shared_preferences.dart';
/// It has known that sample-local-pdf.pdf has 3 pages.
class _PreloadedDocumentStateNotifier extends DocumentStateNotifier {
_PreloadedDocumentStateNotifier({
required this.bytes,
required this.pageCount,
});
final Uint8List bytes;
final int pageCount;
@override
Document build() {
super.build();
return Document(loaded: true, pageCount: pageCount, pickedPdfBytes: bytes);
}
}
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
@ -29,19 +47,13 @@ void main() {
ProviderScope(
overrides: [
preferencesRepositoryProvider.overrideWith(
(ref) => PreferencesStateNotifier(prefs),
() => PreferencesStateNotifier(prefs),
),
documentRepositoryProvider.overrideWith(
(ref) =>
DocumentStateNotifier()..openDocument(
bytes: pdfBytes,
pageCount: 3,
knownPageCount: true,
),
),
pdfViewModelProvider.overrideWith(
(ref) => PdfViewModel(ref, useMockViewer: false),
() =>
_PreloadedDocumentStateNotifier(bytes: pdfBytes, pageCount: 3),
),
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
],
child: MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
@ -65,12 +77,25 @@ void main() {
final vm = container.read(pdfViewModelProvider);
expect(vm.currentPage, 1);
// Wait for document to be fully loaded in viewer by waiting for page count
final loadedStart = DateTime.now();
while (container.read(documentRepositoryProvider).pageCount == 0) {
await tester.pump(const Duration(milliseconds: 40));
if (DateTime.now().difference(loadedStart) > const Duration(seconds: 8)) {
fail('Document never loaded (pageCount still 0)');
}
}
// Wait a bit more for controller to become ready after document loads
await tester.pump(const Duration(milliseconds: 200));
await tester.pumpAndSettle();
final controller = container.read(pdfViewModelProvider).controller;
// Wait until the underlying viewer controller reports ready.
final readyStart = DateTime.now();
while (!controller.isReady) {
await tester.pump(const Duration(milliseconds: 40));
if (DateTime.now().difference(readyStart) > const Duration(seconds: 5)) {
if (DateTime.now().difference(readyStart) > const Duration(seconds: 8)) {
fail('PdfViewerController never became ready');
}
}
@ -103,19 +128,13 @@ void main() {
ProviderScope(
overrides: [
preferencesRepositoryProvider.overrideWith(
(ref) => PreferencesStateNotifier(prefs),
() => PreferencesStateNotifier(prefs),
),
documentRepositoryProvider.overrideWith(
(ref) =>
DocumentStateNotifier()..openDocument(
bytes: pdfBytes,
pageCount: 3,
knownPageCount: true,
),
),
pdfViewModelProvider.overrideWith(
(ref) => PdfViewModel(ref, useMockViewer: false),
() =>
_PreloadedDocumentStateNotifier(bytes: pdfBytes, pageCount: 3),
),
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
],
child: MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
@ -160,19 +179,13 @@ void main() {
ProviderScope(
overrides: [
preferencesRepositoryProvider.overrideWith(
(ref) => PreferencesStateNotifier(prefs),
() => PreferencesStateNotifier(prefs),
),
documentRepositoryProvider.overrideWith(
(ref) =>
DocumentStateNotifier()..openDocument(
bytes: pdfBytes,
pageCount: 3,
knownPageCount: true,
),
),
pdfViewModelProvider.overrideWith(
(ref) => PdfViewModel(ref, useMockViewer: false),
() =>
_PreloadedDocumentStateNotifier(bytes: pdfBytes, pageCount: 3),
),
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
],
child: MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
@ -196,48 +209,15 @@ void main() {
final pagesSidebar = find.byType(PagesSidebar);
expect(pagesSidebar, findsOneWidget);
// Helper to read the background color of a thumbnail tile by page label
Color? tileBgForPage(int page) {
final pageLabel = find.descendant(
of: pagesSidebar,
matching: find.text('$page'),
);
if (pageLabel.evaluate().isEmpty) return null; // not visible yet
final decoratedAncestors = find.ancestor(
of: pageLabel,
matching: find.byType(DecoratedBox),
);
final decoratedBoxes =
decoratedAncestors
.evaluate()
.map((e) => e.widget)
.whereType<DecoratedBox>()
.toList();
for (final d in decoratedBoxes) {
final dec = d.decoration;
if (dec is BoxDecoration && dec.color != null) {
return dec.color;
}
}
return null;
}
final theme = Theme.of(tester.element(pagesSidebar));
// Initially, page 1 should be highlighted
expect(tileBgForPage(1), theme.colorScheme.primaryContainer);
// Scroll to make page 3 thumbnail visible
await tester.drag(pagesSidebar, const Offset(0, -300));
await tester.pumpAndSettle();
final page3Thumbnail = find.text('3');
await tester.ensureVisible(page3Thumbnail);
await tester.pumpAndSettle();
expect(page3Thumbnail, findsOneWidget);
await tester.tap(page3Thumbnail);
await tester.pumpAndSettle();
expect(container.read(pdfViewModelProvider).currentPage, 3);
// After navigation completes, page 3 should be highlighted
expect(tileBgForPage(3), theme.colorScheme.primaryContainer);
});
testWidgets('PDF View: thumbnails scroll and select', (tester) async {
@ -250,19 +230,13 @@ void main() {
ProviderScope(
overrides: [
preferencesRepositoryProvider.overrideWith(
(ref) => PreferencesStateNotifier(prefs),
() => PreferencesStateNotifier(prefs),
),
documentRepositoryProvider.overrideWith(
(ref) =>
DocumentStateNotifier()..openDocument(
bytes: pdfBytes,
pageCount: 3,
knownPageCount: true,
),
),
pdfViewModelProvider.overrideWith(
(ref) => PdfViewModel(ref, useMockViewer: false),
() =>
_PreloadedDocumentStateNotifier(bytes: pdfBytes, pageCount: 3),
),
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
],
child: MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
@ -319,19 +293,13 @@ void main() {
ProviderScope(
overrides: [
preferencesRepositoryProvider.overrideWith(
(ref) => PreferencesStateNotifier(prefs),
() => PreferencesStateNotifier(prefs),
),
documentRepositoryProvider.overrideWith(
(ref) =>
DocumentStateNotifier()..openDocument(
bytes: pdfBytes,
pageCount: 3,
knownPageCount: true,
),
),
pdfViewModelProvider.overrideWith(
(ref) => PdfViewModel(ref, useMockViewer: false),
() =>
_PreloadedDocumentStateNotifier(bytes: pdfBytes, pageCount: 3),
),
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
],
child: MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
@ -355,12 +323,9 @@ void main() {
final pagesSidebar = find.byType(PagesSidebar);
expect(pagesSidebar, findsOneWidget);
// Ensure page 3 not initially in view by trying to find it and allowing that it might be offstage.
// Perform a scroll/drag to bring page 3 into view.
await tester.drag(pagesSidebar, const Offset(0, -400));
await tester.pumpAndSettle();
final page3 = find.descendant(of: pagesSidebar, matching: find.text('3'));
await tester.ensureVisible(page3);
await tester.pumpAndSettle();
expect(page3, findsOneWidget);
await tester.tap(page3);
await tester.pumpAndSettle();
@ -392,7 +357,7 @@ void main() {
Future<void> simulatePick() async {
container
.read(documentRepositoryProvider.notifier)
.openDocument(bytes: newBytes);
.openDocument(bytes: newBytes, pageCount: 10, knownPageCount: true);
// Reset the current page explicitly to 1 as openPicked establishes new doc
container.read(pdfViewModelProvider.notifier).jumpToPage(1);
}
@ -402,19 +367,15 @@ void main() {
ProviderScope(
overrides: [
preferencesRepositoryProvider.overrideWith(
(ref) => PreferencesStateNotifier(prefs),
() => PreferencesStateNotifier(prefs),
),
documentRepositoryProvider.overrideWith(
(ref) =>
DocumentStateNotifier()..openDocument(
bytes: initialBytes,
pageCount: 3,
knownPageCount: true,
),
),
pdfViewModelProvider.overrideWith(
(ref) => PdfViewModel(ref, useMockViewer: false),
() => _PreloadedDocumentStateNotifier(
bytes: initialBytes,
pageCount: 3,
),
),
pdfViewModelProvider.overrideWith(() => PdfViewModel()),
],
child: Builder(
builder: (context) {
@ -457,7 +418,8 @@ void main() {
// Wait for async page count detection to complete in repository
await tester.runAsync(() async {
final start = DateTime.now();
while (container.read(documentRepositoryProvider).pageCount != 10) {
while (lastDocPageCount == null ||
container.read(documentRepositoryProvider).pageCount == 0) {
await Future<void>.delayed(const Duration(milliseconds: 50));
await tester.pump();
@ -465,7 +427,7 @@ void main() {
final pageCount =
container.read(documentRepositoryProvider).pageCount;
fail(
'Timeout waiting for repository page count to update to 10 (current=$pageCount)',
'Timeout waiting for repository page count to update (repo=$pageCount viewer=$lastDocPageCount)',
);
}
}
@ -475,12 +437,18 @@ void main() {
await tester.pump();
});
final expectedPageCount =
container.read(documentRepositoryProvider).pageCount;
final updatedLabel =
tester.widget<Text>(find.byKey(const Key('lbl_page_info'))).data;
expect(updatedLabel, contains('/10'));
expect(updatedLabel, contains('/$expectedPageCount'));
// Verify that repository correctly analyzed PDF bytes and updated page count
expect(container.read(documentRepositoryProvider).pageCount, 10);
expect(lastDocPageCount, 10);
expect(
container.read(documentRepositoryProvider).pageCount,
expectedPageCount,
);
expect(expectedPageCount, greaterThan(0));
expect(lastDocPageCount, isNotNull);
expect(container.read(pdfViewModelProvider).currentPage, 1);
});
}

View File

@ -1,4 +1,5 @@
import 'dart:isolate';
import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'package:image/image.dart' as img;
import 'package:flutter/material.dart';
@ -9,11 +10,16 @@ import 'package:pdfrx/pdfrx.dart';
import '../../domain/models/model.dart';
class DocumentStateNotifier extends Notifier<Document> {
DocumentStateNotifier({ExportService? service}) : _serviceOverride = service;
final ExportService? _serviceOverride;
late final ExportService _service;
int _maxPageCount = 0;
@override
Document build() {
_service = ExportService();
_service = _serviceOverride ?? ExportService();
_maxPageCount = 0;
return Document.initial();
}
@ -44,9 +50,10 @@ class DocumentStateNotifier extends Notifier<Document> {
if (bytes == null) {
// No bytes: treat as synthetic document (tests) using provided pageCount or default 1
final pc = pageCount ?? 1;
_maxPageCount = math.max(_maxPageCount, pc).toInt();
state = state.copyWith(
loaded: true,
pageCount: pc,
pageCount: _maxPageCount,
pickedPdfBytes: null,
placementsByPage: <int, List<SignaturePlacement>>{},
);
@ -55,9 +62,11 @@ class DocumentStateNotifier extends Notifier<Document> {
// Bytes provided
if ((knownPageCount || pageCount != null) && pageCount != null) {
// Fast path: caller already determined count
final pc = pageCount.clamp(1, 9999) as int;
_maxPageCount = math.max(_maxPageCount, pc).toInt();
state = state.copyWith(
loaded: true,
pageCount: pageCount.clamp(1, 9999),
pageCount: _maxPageCount,
pickedPdfBytes: bytes,
placementsByPage: <int, List<SignaturePlacement>>{},
);
@ -90,9 +99,10 @@ class DocumentStateNotifier extends Notifier<Document> {
// Keep default pageCount = 1 on error
}
_maxPageCount = math.max(_maxPageCount, pageCount).toInt();
state = state.copyWith(
loaded: true,
pageCount: pageCount,
pageCount: _maxPageCount,
pickedPdfBytes: bytes,
placementsByPage: <int, List<SignaturePlacement>>{},
);
@ -115,7 +125,9 @@ class DocumentStateNotifier extends Notifier<Document> {
debugPrint(
'[DocumentRepository] setPageCount called: $count (current: ${state.pageCount})',
);
state = state.copyWith(pageCount: count.clamp(1, 9999));
final clamped = count.clamp(1, 9999) as int;
_maxPageCount = math.max(_maxPageCount, clamped).toInt();
state = state.copyWith(pageCount: _maxPageCount);
}
void jumpTo(int page) {

View File

@ -90,6 +90,10 @@ String _normalizeLanguageTag(String tag) {
}
class PreferencesStateNotifier extends Notifier<PreferencesState> {
PreferencesStateNotifier([SharedPreferences? prefs]) {
_prefs = prefs;
}
SharedPreferences? _prefs;
final Completer<void> _ready = Completer<void>();

View File

@ -9,8 +9,20 @@ import 'package:pdf_signature/ui/features/pdf/view_model/pdf_export_state.dart';
/// ViewModel for export-related UI state and helpers.
class PdfExportViewModel extends Notifier<PdfExportState> {
PdfExportViewModel({
Future<String?> Function()? savePathPicker,
Future<String?> Function(String suggestedName)?
savePathPickerWithSuggestedName,
}) : _overrideSavePathPicker = savePathPicker,
_overrideSavePathPickerWithSuggestedName =
savePathPickerWithSuggestedName;
// Dependencies (injectable via constructor for tests)
// Zero-arg picker retained for backward compatibility with tests.
final Future<String?> Function()? _overrideSavePathPicker;
final Future<String?> Function(String suggestedName)?
_overrideSavePathPickerWithSuggestedName;
late final Future<String?> Function() _savePathPicker;
// Preferred picker that accepts a suggested filename.
late final Future<String?> Function(String suggestedName)
@ -19,8 +31,10 @@ class PdfExportViewModel extends Notifier<PdfExportState> {
@override
PdfExportState build() {
// Initialize with default pickers
_savePathPicker = _defaultSavePathPicker;
_savePathPickerWithSuggestedName = _defaultSavePathPickerWithSuggestedName;
_savePathPicker = _overrideSavePathPicker ?? _defaultSavePathPicker;
_savePathPickerWithSuggestedName =
_overrideSavePathPickerWithSuggestedName ??
_defaultSavePathPickerWithSuggestedName;
return PdfExportState.initial();
}