feat: partially implement new view of UI
This commit is contained in:
parent
189bc7e6e6
commit
4d2cd09adf
|
|
@ -37,7 +37,7 @@ class SignatureCardStateNotifier extends StateNotifier<List<SignatureCard>> {
|
|||
}
|
||||
}
|
||||
|
||||
final signatureCardProvider =
|
||||
final signatureCardRepositoryProvider =
|
||||
StateNotifierProvider<SignatureCardStateNotifier, List<SignatureCard>>(
|
||||
(ref) => SignatureCardStateNotifier(),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class PdfViewModel {
|
|||
ref
|
||||
.read(documentRepositoryProvider.notifier)
|
||||
.openPicked(path: path, pageCount: pageCount, bytes: bytes);
|
||||
ref.read(signatureCardProvider.notifier).clearAll();
|
||||
ref.read(signatureCardRepositoryProvider.notifier).clearAll();
|
||||
}
|
||||
|
||||
Future<Uint8List?> loadSignatureFromFile() async {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import 'package:pdf_signature/data/repositories/signature_asset_repository.dart'
|
|||
// using only adjusted overlay, no direct model imports needed
|
||||
|
||||
/// Mocked continuous viewer for tests or platforms without real viewer.
|
||||
@visibleForTesting
|
||||
class PdfMockContinuousList extends ConsumerStatefulWidget {
|
||||
const PdfMockContinuousList({
|
||||
super.key,
|
||||
|
|
@ -54,6 +55,9 @@ class _PdfMockContinuousListState extends ConsumerState<PdfMockContinuousList> {
|
|||
final pendingPage = widget.pendingPage;
|
||||
final scrollToPage = widget.scrollToPage;
|
||||
final clearPending = widget.clearPending;
|
||||
final visible = ref.watch(signatureVisibilityProvider);
|
||||
final assets = ref.watch(signatureAssetRepositoryProvider);
|
||||
final aspectLocked = ref.watch(aspectLockedProvider);
|
||||
if (pendingPage != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final p = pendingPage;
|
||||
|
|
@ -101,165 +105,157 @@ class _PdfMockContinuousListState extends ConsumerState<PdfMockContinuousList> {
|
|||
),
|
||||
),
|
||||
),
|
||||
Consumer(
|
||||
builder: (context, ref, _) {
|
||||
final visible = ref.watch(signatureVisibilityProvider);
|
||||
if (!visible) return const SizedBox.shrink();
|
||||
final overlays = <Widget>[];
|
||||
// Existing placed overlays
|
||||
overlays.add(
|
||||
PdfPageOverlays(
|
||||
pageSize: pageSize,
|
||||
pageNumber: pageNum,
|
||||
onDragSignature: widget.onDragSignature,
|
||||
onResizeSignature: widget.onResizeSignature,
|
||||
onConfirmSignature: widget.onConfirmSignature,
|
||||
onClearActiveOverlay: widget.onClearActiveOverlay,
|
||||
onSelectPlaced: widget.onSelectPlaced,
|
||||
),
|
||||
);
|
||||
// For tests expecting an active overlay, draw a mock
|
||||
// overlay on page 1 when library has at least one asset
|
||||
if (pageNum == 1 &&
|
||||
(ref
|
||||
.watch(signatureAssetRepositoryProvider)
|
||||
.isNotEmpty)) {
|
||||
overlays.add(
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final left =
|
||||
_activeRect.left * constraints.maxWidth;
|
||||
final top =
|
||||
_activeRect.top * constraints.maxHeight;
|
||||
final width =
|
||||
_activeRect.width * constraints.maxWidth;
|
||||
final height =
|
||||
_activeRect.height * constraints.maxHeight;
|
||||
final aspectLocked = ref.watch(
|
||||
aspectLockedProvider,
|
||||
);
|
||||
// Publish rect for tests/other UI to observe
|
||||
WidgetsBinding.instance.addPostFrameCallback((
|
||||
_,
|
||||
) {
|
||||
if (!mounted) return;
|
||||
ref.read(activeRectProvider.notifier).state =
|
||||
_activeRect;
|
||||
});
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
left: left,
|
||||
top: top,
|
||||
width: width,
|
||||
height: height,
|
||||
child: GestureDetector(
|
||||
key: const Key('signature_overlay'),
|
||||
onPanUpdate: (d) {
|
||||
final dx =
|
||||
d.delta.dx / constraints.maxWidth;
|
||||
final dy =
|
||||
d.delta.dy /
|
||||
constraints.maxHeight;
|
||||
setState(() {
|
||||
double l = (_activeRect.left + dx)
|
||||
.clamp(0.0, 1.0);
|
||||
double t = (_activeRect.top + dy)
|
||||
.clamp(0.0, 1.0);
|
||||
// clamp so it stays within page
|
||||
l = l.clamp(
|
||||
0.0,
|
||||
1.0 - _activeRect.width,
|
||||
);
|
||||
t = t.clamp(
|
||||
0.0,
|
||||
1.0 - _activeRect.height,
|
||||
);
|
||||
_activeRect = Rect.fromLTWH(
|
||||
l,
|
||||
t,
|
||||
_activeRect.width,
|
||||
_activeRect.height,
|
||||
);
|
||||
});
|
||||
},
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Colors.red,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: const SizedBox.expand(),
|
||||
),
|
||||
),
|
||||
),
|
||||
// resize handle bottom-right
|
||||
Positioned(
|
||||
left: left + width - 14,
|
||||
top: top + height - 14,
|
||||
width: 14,
|
||||
height: 14,
|
||||
child: GestureDetector(
|
||||
key: const Key('signature_handle'),
|
||||
onPanUpdate: (d) {
|
||||
final dx =
|
||||
d.delta.dx / constraints.maxWidth;
|
||||
final dy =
|
||||
d.delta.dy /
|
||||
constraints.maxHeight;
|
||||
setState(() {
|
||||
double newW = (_activeRect.width +
|
||||
dx)
|
||||
.clamp(0.05, 1.0);
|
||||
double newH = (_activeRect.height +
|
||||
dy)
|
||||
.clamp(0.05, 1.0);
|
||||
if (aspectLocked) {
|
||||
final ratio =
|
||||
_activeRect.width /
|
||||
_activeRect.height;
|
||||
// keep ratio; prefer width change driving height
|
||||
newH = (newW /
|
||||
(ratio == 0 ? 1 : ratio))
|
||||
.clamp(0.05, 1.0);
|
||||
}
|
||||
// clamp to page bounds
|
||||
newW = newW.clamp(
|
||||
0.05,
|
||||
1.0 - _activeRect.left,
|
||||
);
|
||||
newH = newH.clamp(
|
||||
0.05,
|
||||
1.0 - _activeRect.top,
|
||||
);
|
||||
_activeRect = Rect.fromLTWH(
|
||||
_activeRect.left,
|
||||
_activeRect.top,
|
||||
newW,
|
||||
newH,
|
||||
);
|
||||
});
|
||||
},
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.all(
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
visible
|
||||
? Stack(
|
||||
children: [
|
||||
PdfPageOverlays(
|
||||
pageSize: pageSize,
|
||||
pageNumber: pageNum,
|
||||
onDragSignature: widget.onDragSignature,
|
||||
onResizeSignature: widget.onResizeSignature,
|
||||
onConfirmSignature: widget.onConfirmSignature,
|
||||
onClearActiveOverlay: widget.onClearActiveOverlay,
|
||||
onSelectPlaced: widget.onSelectPlaced,
|
||||
),
|
||||
);
|
||||
}
|
||||
return Stack(children: overlays);
|
||||
},
|
||||
),
|
||||
// For tests expecting an active overlay, draw a mock
|
||||
// overlay on page 1 when library has at least one asset
|
||||
if (pageNum == 1 && assets.isNotEmpty)
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final left =
|
||||
_activeRect.left * constraints.maxWidth;
|
||||
final top =
|
||||
_activeRect.top * constraints.maxHeight;
|
||||
final width =
|
||||
_activeRect.width * constraints.maxWidth;
|
||||
final height =
|
||||
_activeRect.height *
|
||||
constraints.maxHeight;
|
||||
// Publish rect for tests/other UI to observe
|
||||
WidgetsBinding.instance.addPostFrameCallback((
|
||||
_,
|
||||
) {
|
||||
if (!mounted) return;
|
||||
ref
|
||||
.read(activeRectProvider.notifier)
|
||||
.state = _activeRect;
|
||||
});
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
left: left,
|
||||
top: top,
|
||||
width: width,
|
||||
height: height,
|
||||
child: GestureDetector(
|
||||
key: const Key('signature_overlay'),
|
||||
onPanUpdate: (d) {
|
||||
final dx =
|
||||
d.delta.dx /
|
||||
constraints.maxWidth;
|
||||
final dy =
|
||||
d.delta.dy /
|
||||
constraints.maxHeight;
|
||||
setState(() {
|
||||
double l = (_activeRect.left + dx)
|
||||
.clamp(0.0, 1.0);
|
||||
double t = (_activeRect.top + dy)
|
||||
.clamp(0.0, 1.0);
|
||||
// clamp so it stays within page
|
||||
l = l.clamp(
|
||||
0.0,
|
||||
1.0 - _activeRect.width,
|
||||
);
|
||||
t = t.clamp(
|
||||
0.0,
|
||||
1.0 - _activeRect.height,
|
||||
);
|
||||
_activeRect = Rect.fromLTWH(
|
||||
l,
|
||||
t,
|
||||
_activeRect.width,
|
||||
_activeRect.height,
|
||||
);
|
||||
});
|
||||
},
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Colors.red,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: const SizedBox.expand(),
|
||||
),
|
||||
),
|
||||
),
|
||||
// resize handle bottom-right
|
||||
Positioned(
|
||||
left: left + width - 14,
|
||||
top: top + height - 14,
|
||||
width: 14,
|
||||
height: 14,
|
||||
child: GestureDetector(
|
||||
key: const Key('signature_handle'),
|
||||
onPanUpdate: (d) {
|
||||
final dx =
|
||||
d.delta.dx /
|
||||
constraints.maxWidth;
|
||||
final dy =
|
||||
d.delta.dy /
|
||||
constraints.maxHeight;
|
||||
setState(() {
|
||||
double newW = (_activeRect.width +
|
||||
dx)
|
||||
.clamp(0.05, 1.0);
|
||||
double newH =
|
||||
(_activeRect.height + dy)
|
||||
.clamp(0.05, 1.0);
|
||||
if (aspectLocked) {
|
||||
final ratio =
|
||||
_activeRect.width /
|
||||
_activeRect.height;
|
||||
// keep ratio; prefer width change driving height
|
||||
newH = (newW /
|
||||
(ratio == 0
|
||||
? 1
|
||||
: ratio))
|
||||
.clamp(0.05, 1.0);
|
||||
}
|
||||
// clamp to page bounds
|
||||
newW = newW.clamp(
|
||||
0.05,
|
||||
1.0 - _activeRect.left,
|
||||
);
|
||||
newH = newH.clamp(
|
||||
0.05,
|
||||
1.0 - _activeRect.top,
|
||||
);
|
||||
_activeRect = Rect.fromLTWH(
|
||||
_activeRect.left,
|
||||
_activeRect.top,
|
||||
newW,
|
||||
newH,
|
||||
);
|
||||
});
|
||||
},
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.all(
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import 'package:pdf_signature/l10n/app_localizations.dart';
|
|||
// Real viewer removed in migration; mock continuous list is used in tests.
|
||||
|
||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||
import 'pdf_mock_continuous_list.dart';
|
||||
import 'pdf_viewer_widget.dart';
|
||||
|
||||
class PdfPageArea extends ConsumerStatefulWidget {
|
||||
const PdfPageArea({
|
||||
|
|
@ -147,24 +147,17 @@ class _PdfPageAreaState extends ConsumerState<PdfPageArea> {
|
|||
|
||||
final isContinuous = pageViewMode == 'continuous';
|
||||
|
||||
// Mock continuous: ListView with prebuilt children
|
||||
// Use real PDF viewer
|
||||
if (isContinuous) {
|
||||
final count = pdf.pageCount > 0 ? pdf.pageCount : 1;
|
||||
return PdfMockContinuousList(
|
||||
return PdfViewerWidget(
|
||||
pageSize: widget.pageSize,
|
||||
count: count,
|
||||
pageKeyBuilder: _pageKey,
|
||||
scrollToPage: _scrollToPage,
|
||||
pendingPage: _pendingPage,
|
||||
clearPending: () {
|
||||
_pendingPage = null;
|
||||
_scrollRetryCount = 0;
|
||||
},
|
||||
onDragSignature: (delta) => widget.onDragSignature(delta),
|
||||
onResizeSignature: (delta) => widget.onResizeSignature(delta),
|
||||
onDragSignature: widget.onDragSignature,
|
||||
onResizeSignature: widget.onResizeSignature,
|
||||
onConfirmSignature: widget.onConfirmSignature,
|
||||
onClearActiveOverlay: widget.onClearActiveOverlay,
|
||||
onSelectPlaced: widget.onSelectPlaced,
|
||||
pageKeyBuilder: _pageKey,
|
||||
scrollToPage: _scrollToPage,
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ import 'package:flutter_riverpod/flutter_riverpod.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) => 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);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:file_selector/file_selector.dart' as fs;
|
|||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:pdfrx/pdfrx.dart';
|
||||
import 'package:pdf_signature/data/repositories/preferences_repository.dart';
|
||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||
import 'package:multi_split_view/multi_split_view.dart';
|
||||
|
|
@ -62,10 +63,14 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
|||
}
|
||||
// infer page count if possible
|
||||
int pageCount = 1;
|
||||
try {
|
||||
// printing.raster can detect page count lazily; leave 1 for tests
|
||||
pageCount = 5;
|
||||
} catch (_) {}
|
||||
if (bytes != null) {
|
||||
try {
|
||||
final doc = await PdfDocument.openData(bytes);
|
||||
pageCount = doc.pages.length;
|
||||
} catch (_) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
ref
|
||||
.read(documentRepositoryProvider.notifier)
|
||||
.openPicked(path: file.path, pageCount: pageCount, bytes: bytes);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,143 @@
|
|||
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 'package:pdf_signature/l10n/app_localizations.dart';
|
||||
import 'pdf_page_overlays.dart';
|
||||
import 'pdf_providers.dart';
|
||||
import './pdf_mock_continuous_list.dart';
|
||||
|
||||
class PdfViewerWidget extends ConsumerStatefulWidget {
|
||||
const PdfViewerWidget({
|
||||
super.key,
|
||||
required this.pageSize,
|
||||
required this.onDragSignature,
|
||||
required this.onResizeSignature,
|
||||
required this.onConfirmSignature,
|
||||
required this.onClearActiveOverlay,
|
||||
required this.onSelectPlaced,
|
||||
this.pageKeyBuilder,
|
||||
this.scrollToPage,
|
||||
});
|
||||
|
||||
final Size pageSize;
|
||||
final ValueChanged<Offset> onDragSignature;
|
||||
final ValueChanged<Offset> onResizeSignature;
|
||||
final VoidCallback onConfirmSignature;
|
||||
final VoidCallback onClearActiveOverlay;
|
||||
final ValueChanged<int?> onSelectPlaced;
|
||||
final GlobalKey Function(int page)? pageKeyBuilder;
|
||||
final void Function(int page)? scrollToPage;
|
||||
|
||||
@override
|
||||
ConsumerState<PdfViewerWidget> createState() => _PdfViewerWidgetState();
|
||||
}
|
||||
|
||||
class _PdfViewerWidgetState extends ConsumerState<PdfViewerWidget> {
|
||||
PdfViewerController? _controller;
|
||||
PdfDocumentRef? _documentRef;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = PdfViewerController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// PdfViewerController doesn't have dispose method
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final document = ref.watch(documentRepositoryProvider);
|
||||
final useMock = ref.watch(useMockViewerProvider);
|
||||
|
||||
// Update document ref when document changes
|
||||
if (document.loaded && document.pickedPdfBytes != null) {
|
||||
if (_documentRef == null) {
|
||||
_documentRef = PdfDocumentRefData(
|
||||
document.pickedPdfBytes!,
|
||||
sourceName: 'document.pdf',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
_documentRef = null;
|
||||
}
|
||||
|
||||
if (_documentRef == null && !useMock) {
|
||||
String text;
|
||||
try {
|
||||
text = AppLocalizations.of(context).noPdfLoaded;
|
||||
} catch (_) {
|
||||
text = 'No PDF loaded';
|
||||
}
|
||||
return Center(child: Text(text));
|
||||
}
|
||||
|
||||
if (useMock) {
|
||||
return PdfMockContinuousList(
|
||||
pageSize: widget.pageSize,
|
||||
count: document.pageCount,
|
||||
pageKeyBuilder:
|
||||
widget.pageKeyBuilder ??
|
||||
(page) => GlobalKey(debugLabel: 'page_$page'),
|
||||
scrollToPage: widget.scrollToPage ?? (page) {},
|
||||
onDragSignature: widget.onDragSignature,
|
||||
onResizeSignature: widget.onResizeSignature,
|
||||
onConfirmSignature: widget.onConfirmSignature,
|
||||
onClearActiveOverlay: widget.onClearActiveOverlay,
|
||||
onSelectPlaced: widget.onSelectPlaced,
|
||||
);
|
||||
}
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
PdfViewer(
|
||||
_documentRef!,
|
||||
key: const Key(
|
||||
'pdf_continuous_mock_list',
|
||||
), // Keep the same key for test compatibility
|
||||
controller: _controller,
|
||||
params: PdfViewerParams(
|
||||
onViewerReady: (document, controller) {
|
||||
// Update page count in repository
|
||||
ref
|
||||
.read(documentRepositoryProvider.notifier)
|
||||
.setPageCount(document.pages.length);
|
||||
},
|
||||
onPageChanged: (page) {
|
||||
// Update current page in repository
|
||||
if (page != null) {
|
||||
ref.read(documentRepositoryProvider.notifier).jumpTo(page);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
// Add signature overlays on top
|
||||
Positioned.fill(
|
||||
child: Consumer(
|
||||
builder: (context, ref, _) {
|
||||
final visible = ref.watch(signatureVisibilityProvider);
|
||||
if (!visible) return const SizedBox.shrink();
|
||||
|
||||
// For now, just add a simple overlay for the first page
|
||||
// This is a simplified version - in a real implementation you'd need
|
||||
// to handle overlays for each page properly
|
||||
return PdfPageOverlays(
|
||||
pageSize: widget.pageSize,
|
||||
pageNumber: document.currentPage,
|
||||
onDragSignature: widget.onDragSignature,
|
||||
onResizeSignature: widget.onResizeSignature,
|
||||
onConfirmSignature: widget.onConfirmSignature,
|
||||
onClearActiveOverlay: widget.onClearActiveOverlay,
|
||||
onSelectPlaced: widget.onSelectPlaced,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ class WelcomeViewModel {
|
|||
ref
|
||||
.read(documentRepositoryProvider.notifier)
|
||||
.openPicked(path: path, pageCount: pageCount, bytes: bytes);
|
||||
ref.read(signatureCardProvider.notifier).clearAll();
|
||||
ref.read(signatureCardRepositoryProvider.notifier).clearAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Future<void> aMultipageDocumentIsOpen(WidgetTester tester) async {
|
|||
container.read(signatureAssetRepositoryProvider.notifier).state = [];
|
||||
container.read(documentRepositoryProvider.notifier).state =
|
||||
Document.initial();
|
||||
container.read(signatureCardProvider.notifier).state = [
|
||||
container.read(signatureCardRepositoryProvider.notifier).state = [
|
||||
SignatureCard.initial(),
|
||||
];
|
||||
container
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Future<void> aSignatureAssetIsLoadedOrDrawn(WidgetTester tester) async {
|
|||
container.read(signatureAssetRepositoryProvider.notifier).state = [];
|
||||
container.read(documentRepositoryProvider.notifier).state =
|
||||
Document.initial();
|
||||
container.read(signatureCardProvider.notifier).state = [
|
||||
container.read(signatureCardRepositoryProvider.notifier).state = [
|
||||
SignatureCard.initial(),
|
||||
];
|
||||
final bytes = Uint8List.fromList([1, 2, 3, 4, 5]);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Future<void> aSignatureAssetLoadedOrDrawnIsWrappedInASignatureCard(
|
|||
container.read(signatureAssetRepositoryProvider.notifier).state = [];
|
||||
container.read(documentRepositoryProvider.notifier).state =
|
||||
Document.initial();
|
||||
container.read(signatureCardProvider.notifier).state = [
|
||||
container.read(signatureCardRepositoryProvider.notifier).state = [
|
||||
SignatureCard.initial(),
|
||||
];
|
||||
final bytes = Uint8List.fromList([1, 2, 3, 4, 5]);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ Future<void> threeSignaturePlacementsArePlacedOnTheCurrentPage(
|
|||
container.read(signatureAssetRepositoryProvider.notifier).state = [];
|
||||
container.read(documentRepositoryProvider.notifier).state =
|
||||
Document.initial();
|
||||
container.read(signatureCardProvider.notifier).state = [
|
||||
container.read(signatureCardRepositoryProvider.notifier).state = [
|
||||
SignatureCard.initial(),
|
||||
];
|
||||
container
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import 'package:pdf_signature/ui/features/pdf/widgets/pdf_providers.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/signature_asset_repository.dart';
|
||||
import 'package:pdf_signature/domain/models/signature_asset.dart';
|
||||
|
||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||
// preferences_providers.dart no longer exports pageViewModeProvider
|
||||
|
|
@ -48,13 +49,329 @@ Future<void> pumpWithOpenPdfAndSig(WidgetTester tester) async {
|
|||
color: img.ColorUint8.rgb(0, 0, 0),
|
||||
);
|
||||
final bytes = img.encodePng(canvas);
|
||||
|
||||
// Create minimal PDF bytes for testing (this is a very basic PDF structure)
|
||||
// This is just enough to make the PDF viewer work in tests
|
||||
final pdfBytes = Uint8List.fromList([
|
||||
0x25, 0x50, 0x44, 0x46, 0x2D, 0x31, 0x2E, 0x34, 0x0A, // %PDF-1.4
|
||||
0x31, 0x20, 0x30, 0x20, 0x6F, 0x62, 0x6A, 0x0A, // 1 0 obj
|
||||
0x3C,
|
||||
0x3C,
|
||||
0x2F,
|
||||
0x54,
|
||||
0x79,
|
||||
0x70,
|
||||
0x65,
|
||||
0x20,
|
||||
0x2F,
|
||||
0x43,
|
||||
0x61,
|
||||
0x74,
|
||||
0x61,
|
||||
0x6C,
|
||||
0x6F,
|
||||
0x67,
|
||||
0x20,
|
||||
0x2F,
|
||||
0x50,
|
||||
0x61,
|
||||
0x67,
|
||||
0x65,
|
||||
0x73,
|
||||
0x20,
|
||||
0x32,
|
||||
0x20,
|
||||
0x30,
|
||||
0x20,
|
||||
0x52,
|
||||
0x3E,
|
||||
0x3E,
|
||||
0x0A,
|
||||
0x65, 0x6E, 0x64, 0x6F, 0x62, 0x6A, 0x0A,
|
||||
0x32, 0x20, 0x30, 0x20, 0x6F, 0x62, 0x6A, 0x0A,
|
||||
0x3C,
|
||||
0x3C,
|
||||
0x2F,
|
||||
0x54,
|
||||
0x79,
|
||||
0x70,
|
||||
0x65,
|
||||
0x20,
|
||||
0x2F,
|
||||
0x50,
|
||||
0x61,
|
||||
0x67,
|
||||
0x65,
|
||||
0x73,
|
||||
0x20,
|
||||
0x2F,
|
||||
0x43,
|
||||
0x6F,
|
||||
0x75,
|
||||
0x6E,
|
||||
0x74,
|
||||
0x20,
|
||||
0x31,
|
||||
0x20,
|
||||
0x2F,
|
||||
0x4B,
|
||||
0x69,
|
||||
0x64,
|
||||
0x73,
|
||||
0x20,
|
||||
0x5B,
|
||||
0x33,
|
||||
0x20,
|
||||
0x30,
|
||||
0x20,
|
||||
0x52,
|
||||
0x5D,
|
||||
0x3E,
|
||||
0x3E,
|
||||
0x0A,
|
||||
0x65, 0x6E, 0x64, 0x6F, 0x62, 0x6A, 0x0A,
|
||||
0x33, 0x20, 0x30, 0x20, 0x6F, 0x62, 0x6A, 0x0A,
|
||||
0x3C,
|
||||
0x3C,
|
||||
0x2F,
|
||||
0x54,
|
||||
0x79,
|
||||
0x70,
|
||||
0x65,
|
||||
0x20,
|
||||
0x2F,
|
||||
0x50,
|
||||
0x61,
|
||||
0x67,
|
||||
0x65,
|
||||
0x20,
|
||||
0x2F,
|
||||
0x50,
|
||||
0x61,
|
||||
0x72,
|
||||
0x65,
|
||||
0x6E,
|
||||
0x74,
|
||||
0x20,
|
||||
0x32,
|
||||
0x20,
|
||||
0x30,
|
||||
0x20,
|
||||
0x52,
|
||||
0x20,
|
||||
0x2F,
|
||||
0x4D,
|
||||
0x65,
|
||||
0x64,
|
||||
0x69,
|
||||
0x61,
|
||||
0x42,
|
||||
0x6F,
|
||||
0x78,
|
||||
0x20,
|
||||
0x5B,
|
||||
0x30,
|
||||
0x20,
|
||||
0x30,
|
||||
0x20,
|
||||
0x36,
|
||||
0x31,
|
||||
0x32,
|
||||
0x20,
|
||||
0x37,
|
||||
0x39,
|
||||
0x32,
|
||||
0x5D,
|
||||
0x20,
|
||||
0x2F,
|
||||
0x43,
|
||||
0x6F,
|
||||
0x6E,
|
||||
0x74,
|
||||
0x65,
|
||||
0x6E,
|
||||
0x74,
|
||||
0x73,
|
||||
0x20,
|
||||
0x34,
|
||||
0x20,
|
||||
0x30,
|
||||
0x20,
|
||||
0x52,
|
||||
0x3E,
|
||||
0x3E,
|
||||
0x0A,
|
||||
0x65, 0x6E, 0x64, 0x6F, 0x62, 0x6A, 0x0A,
|
||||
0x34, 0x20, 0x30, 0x20, 0x6F, 0x62, 0x6A, 0x0A,
|
||||
0x3C,
|
||||
0x3C,
|
||||
0x2F,
|
||||
0x4C,
|
||||
0x65,
|
||||
0x6E,
|
||||
0x67,
|
||||
0x74,
|
||||
0x68,
|
||||
0x20,
|
||||
0x34,
|
||||
0x34,
|
||||
0x3E,
|
||||
0x3E,
|
||||
0x0A,
|
||||
0x73, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x0A,
|
||||
0x42, 0x54, 0x0A, // BT
|
||||
0x2F, 0x46, 0x31, 0x20, 0x32, 0x34, 0x20, 0x54, 0x66, 0x0A, // /F1 24 Tf
|
||||
0x31,
|
||||
0x30,
|
||||
0x30,
|
||||
0x20,
|
||||
0x37,
|
||||
0x30,
|
||||
0x30,
|
||||
0x20,
|
||||
0x54,
|
||||
0x64,
|
||||
0x0A, // 100 700 Td
|
||||
0x28,
|
||||
0x54,
|
||||
0x65,
|
||||
0x73,
|
||||
0x74,
|
||||
0x20,
|
||||
0x50,
|
||||
0x44,
|
||||
0x46,
|
||||
0x29,
|
||||
0x20,
|
||||
0x54,
|
||||
0x6A,
|
||||
0x0A, // (Test PDF) Tj
|
||||
0x45, 0x54, 0x0A, // ET
|
||||
0x65, 0x6E, 0x64, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x0A,
|
||||
0x65, 0x6E, 0x64, 0x6F, 0x62, 0x6A, 0x0A,
|
||||
0x78, 0x72, 0x65, 0x66, 0x0A,
|
||||
0x30, 0x20, 0x35, 0x0A,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x20,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x20,
|
||||
0x6E,
|
||||
0x20,
|
||||
0x0A,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x31,
|
||||
0x20,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x20,
|
||||
0x6E,
|
||||
0x20,
|
||||
0x0A,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x32,
|
||||
0x20,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x20,
|
||||
0x6E,
|
||||
0x20,
|
||||
0x0A,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x33,
|
||||
0x20,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x20,
|
||||
0x6E,
|
||||
0x20,
|
||||
0x0A,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x34,
|
||||
0x20,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x30,
|
||||
0x20,
|
||||
0x6E,
|
||||
0x20,
|
||||
0x0A,
|
||||
0x74, 0x72, 0x61, 0x69, 0x6C, 0x65, 0x72, 0x0A,
|
||||
0x3C,
|
||||
0x3C,
|
||||
0x2F,
|
||||
0x53,
|
||||
0x69,
|
||||
0x7A,
|
||||
0x65,
|
||||
0x20,
|
||||
0x35,
|
||||
0x20,
|
||||
0x2F,
|
||||
0x52,
|
||||
0x6F,
|
||||
0x6F,
|
||||
0x74,
|
||||
0x20,
|
||||
0x31,
|
||||
0x20,
|
||||
0x30,
|
||||
0x20,
|
||||
0x52,
|
||||
0x3E,
|
||||
0x3E,
|
||||
0x0A,
|
||||
0x73, 0x74, 0x61, 0x72, 0x74, 0x78, 0x72, 0x65, 0x66, 0x0A,
|
||||
0x35, 0x35, 0x39, 0x0A,
|
||||
0x25, 0x25, 0x45, 0x4F, 0x46, 0x0A, // %%EOF
|
||||
]);
|
||||
|
||||
// keep drawing for determinism even if bytes unused in simplified UI
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
documentRepositoryProvider.overrideWith(
|
||||
(ref) => DocumentStateNotifier()..openSample(),
|
||||
),
|
||||
documentRepositoryProvider.overrideWith((ref) {
|
||||
final notifier = DocumentStateNotifier()..openSample();
|
||||
// Set PDF bytes so the viewer can display something
|
||||
notifier.state = notifier.state.copyWith(pickedPdfBytes: pdfBytes);
|
||||
// Add a signature placement on page 1
|
||||
notifier.addPlacement(
|
||||
page: 1,
|
||||
rect: const Rect.fromLTWH(0.1, 0.1, 0.3, 0.2),
|
||||
asset: SignatureAsset(bytes: Uint8List.fromList(bytes)),
|
||||
);
|
||||
return notifier;
|
||||
}),
|
||||
signatureAssetRepositoryProvider.overrideWith((ref) {
|
||||
final repo = SignatureAssetRepository();
|
||||
repo.add(Uint8List.fromList(bytes), name: 'test');
|
||||
|
|
|
|||
|
|
@ -69,7 +69,12 @@ void main() {
|
|||
|
||||
// Use a persistent container across rebuilds
|
||||
final container = ProviderContainer(
|
||||
overrides: [useMockViewerProvider.overrideWithValue(true)],
|
||||
overrides: [
|
||||
useMockViewerProvider.overrideWithValue(true),
|
||||
documentRepositoryProvider.overrideWith(
|
||||
(ref) => DocumentStateNotifier()..openSample(),
|
||||
),
|
||||
],
|
||||
);
|
||||
addTearDown(container.dispose);
|
||||
|
||||
|
|
@ -100,8 +105,6 @@ void main() {
|
|||
// Initial pump at base width
|
||||
await tester.pumpWidget(buildHarness(width: 480));
|
||||
|
||||
// Open sample
|
||||
container.read(documentRepositoryProvider.notifier).openSample();
|
||||
// Add a tiny non-empty asset to avoid decode errors
|
||||
final canvas = img.Image(width: 10, height: 5);
|
||||
img.fill(canvas, color: img.ColorUint8.rgb(0, 0, 0));
|
||||
|
|
@ -117,6 +120,9 @@ void main() {
|
|||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify we're using the mock viewer
|
||||
expect(find.byKey(const Key('pdf_continuous_mock_list')), findsOneWidget);
|
||||
|
||||
// Find the first page stack and the placed signature widget
|
||||
final pageStackFinder = find.byKey(const ValueKey('page_stack_1'));
|
||||
expect(pageStackFinder, findsOneWidget);
|
||||
|
|
@ -124,13 +130,13 @@ void main() {
|
|||
final placedFinder = find.byKey(const Key('placed_signature_0'));
|
||||
expect(placedFinder, findsOneWidget);
|
||||
|
||||
// Ensure the widget is fully laid out
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final pageBox = tester.getRect(pageStackFinder);
|
||||
// Measure the positioned overlay area via its DecoratedBox ancestor
|
||||
final placedBox1 = tester.getRect(
|
||||
find
|
||||
.ancestor(of: placedFinder, matching: find.byType(DecoratedBox))
|
||||
.first,
|
||||
);
|
||||
|
||||
// The placed signature widget itself is a DecoratedBox
|
||||
final placedBox1 = tester.getRect(placedFinder);
|
||||
|
||||
// Compute normalized position within the page container
|
||||
final relX1 = (placedBox1.left - pageBox.left) / pageBox.width;
|
||||
|
|
@ -142,11 +148,7 @@ void main() {
|
|||
await tester.pumpAndSettle();
|
||||
|
||||
final pageBox2 = tester.getRect(pageStackFinder);
|
||||
final placedBox2 = tester.getRect(
|
||||
find
|
||||
.ancestor(of: placedFinder, matching: find.byType(DecoratedBox))
|
||||
.first,
|
||||
);
|
||||
final placedBox2 = tester.getRect(placedFinder);
|
||||
|
||||
final relX2 = (placedBox2.left - pageBox2.left) / pageBox2.width;
|
||||
final relY2 = (placedBox2.top - pageBox2.top) / pageBox2.height;
|
||||
|
|
|
|||
Loading…
Reference in New Issue