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>>(
|
StateNotifierProvider<SignatureCardStateNotifier, List<SignatureCard>>(
|
||||||
(ref) => SignatureCardStateNotifier(),
|
(ref) => SignatureCardStateNotifier(),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ class PdfViewModel {
|
||||||
ref
|
ref
|
||||||
.read(documentRepositoryProvider.notifier)
|
.read(documentRepositoryProvider.notifier)
|
||||||
.openPicked(path: path, pageCount: pageCount, bytes: bytes);
|
.openPicked(path: path, pageCount: pageCount, bytes: bytes);
|
||||||
ref.read(signatureCardProvider.notifier).clearAll();
|
ref.read(signatureCardRepositoryProvider.notifier).clearAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Uint8List?> loadSignatureFromFile() async {
|
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
|
// using only adjusted overlay, no direct model imports needed
|
||||||
|
|
||||||
/// Mocked continuous viewer for tests or platforms without real viewer.
|
/// Mocked continuous viewer for tests or platforms without real viewer.
|
||||||
|
@visibleForTesting
|
||||||
class PdfMockContinuousList extends ConsumerStatefulWidget {
|
class PdfMockContinuousList extends ConsumerStatefulWidget {
|
||||||
const PdfMockContinuousList({
|
const PdfMockContinuousList({
|
||||||
super.key,
|
super.key,
|
||||||
|
|
@ -54,6 +55,9 @@ 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 aspectLocked = ref.watch(aspectLockedProvider);
|
||||||
if (pendingPage != null) {
|
if (pendingPage != null) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
final p = pendingPage;
|
final p = pendingPage;
|
||||||
|
|
@ -101,165 +105,157 @@ class _PdfMockContinuousListState extends ConsumerState<PdfMockContinuousList> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Consumer(
|
visible
|
||||||
builder: (context, ref, _) {
|
? Stack(
|
||||||
final visible = ref.watch(signatureVisibilityProvider);
|
children: [
|
||||||
if (!visible) return const SizedBox.shrink();
|
PdfPageOverlays(
|
||||||
final overlays = <Widget>[];
|
pageSize: pageSize,
|
||||||
// Existing placed overlays
|
pageNumber: pageNum,
|
||||||
overlays.add(
|
onDragSignature: widget.onDragSignature,
|
||||||
PdfPageOverlays(
|
onResizeSignature: widget.onResizeSignature,
|
||||||
pageSize: pageSize,
|
onConfirmSignature: widget.onConfirmSignature,
|
||||||
pageNumber: pageNum,
|
onClearActiveOverlay: widget.onClearActiveOverlay,
|
||||||
onDragSignature: widget.onDragSignature,
|
onSelectPlaced: widget.onSelectPlaced,
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
// For tests expecting an active overlay, draw a mock
|
||||||
}
|
// overlay on page 1 when library has at least one asset
|
||||||
return Stack(children: overlays);
|
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.
|
// Real viewer removed in migration; mock continuous list is used in tests.
|
||||||
|
|
||||||
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
import 'package:pdf_signature/data/repositories/document_repository.dart';
|
||||||
import 'pdf_mock_continuous_list.dart';
|
import 'pdf_viewer_widget.dart';
|
||||||
|
|
||||||
class PdfPageArea extends ConsumerStatefulWidget {
|
class PdfPageArea extends ConsumerStatefulWidget {
|
||||||
const PdfPageArea({
|
const PdfPageArea({
|
||||||
|
|
@ -147,24 +147,17 @@ class _PdfPageAreaState extends ConsumerState<PdfPageArea> {
|
||||||
|
|
||||||
final isContinuous = pageViewMode == 'continuous';
|
final isContinuous = pageViewMode == 'continuous';
|
||||||
|
|
||||||
// Mock continuous: ListView with prebuilt children
|
// Use real PDF viewer
|
||||||
if (isContinuous) {
|
if (isContinuous) {
|
||||||
final count = pdf.pageCount > 0 ? pdf.pageCount : 1;
|
return PdfViewerWidget(
|
||||||
return PdfMockContinuousList(
|
|
||||||
pageSize: widget.pageSize,
|
pageSize: widget.pageSize,
|
||||||
count: count,
|
onDragSignature: widget.onDragSignature,
|
||||||
pageKeyBuilder: _pageKey,
|
onResizeSignature: widget.onResizeSignature,
|
||||||
scrollToPage: _scrollToPage,
|
|
||||||
pendingPage: _pendingPage,
|
|
||||||
clearPending: () {
|
|
||||||
_pendingPage = null;
|
|
||||||
_scrollRetryCount = 0;
|
|
||||||
},
|
|
||||||
onDragSignature: (delta) => widget.onDragSignature(delta),
|
|
||||||
onResizeSignature: (delta) => widget.onResizeSignature(delta),
|
|
||||||
onConfirmSignature: widget.onConfirmSignature,
|
onConfirmSignature: widget.onConfirmSignature,
|
||||||
onClearActiveOverlay: widget.onClearActiveOverlay,
|
onClearActiveOverlay: widget.onClearActiveOverlay,
|
||||||
onSelectPlaced: widget.onSelectPlaced,
|
onSelectPlaced: widget.onSelectPlaced,
|
||||||
|
pageKeyBuilder: _pageKey,
|
||||||
|
scrollToPage: _scrollToPage,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const SizedBox.shrink();
|
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.
|
/// Whether to use a mock continuous viewer (ListView) instead of a real PDF viewer.
|
||||||
/// Tests will override this to true.
|
/// 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.
|
/// Global visibility toggle for signature overlays (placed items). Kept simple for tests.
|
||||||
final signatureVisibilityProvider = StateProvider<bool>((ref) => true);
|
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/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:pdfrx/pdfrx.dart';
|
||||||
import 'package:pdf_signature/data/repositories/preferences_repository.dart';
|
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';
|
||||||
|
|
@ -62,10 +63,14 @@ class _PdfSignatureHomePageState extends ConsumerState<PdfSignatureHomePage> {
|
||||||
}
|
}
|
||||||
// infer page count if possible
|
// infer page count if possible
|
||||||
int pageCount = 1;
|
int pageCount = 1;
|
||||||
try {
|
if (bytes != null) {
|
||||||
// printing.raster can detect page count lazily; leave 1 for tests
|
try {
|
||||||
pageCount = 5;
|
final doc = await PdfDocument.openData(bytes);
|
||||||
} catch (_) {}
|
pageCount = doc.pages.length;
|
||||||
|
} catch (_) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
ref
|
ref
|
||||||
.read(documentRepositoryProvider.notifier)
|
.read(documentRepositoryProvider.notifier)
|
||||||
.openPicked(path: file.path, pageCount: pageCount, bytes: bytes);
|
.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
|
ref
|
||||||
.read(documentRepositoryProvider.notifier)
|
.read(documentRepositoryProvider.notifier)
|
||||||
.openPicked(path: path, pageCount: pageCount, bytes: bytes);
|
.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(signatureAssetRepositoryProvider.notifier).state = [];
|
||||||
container.read(documentRepositoryProvider.notifier).state =
|
container.read(documentRepositoryProvider.notifier).state =
|
||||||
Document.initial();
|
Document.initial();
|
||||||
container.read(signatureCardProvider.notifier).state = [
|
container.read(signatureCardRepositoryProvider.notifier).state = [
|
||||||
SignatureCard.initial(),
|
SignatureCard.initial(),
|
||||||
];
|
];
|
||||||
container
|
container
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ Future<void> aSignatureAssetIsLoadedOrDrawn(WidgetTester tester) async {
|
||||||
container.read(signatureAssetRepositoryProvider.notifier).state = [];
|
container.read(signatureAssetRepositoryProvider.notifier).state = [];
|
||||||
container.read(documentRepositoryProvider.notifier).state =
|
container.read(documentRepositoryProvider.notifier).state =
|
||||||
Document.initial();
|
Document.initial();
|
||||||
container.read(signatureCardProvider.notifier).state = [
|
container.read(signatureCardRepositoryProvider.notifier).state = [
|
||||||
SignatureCard.initial(),
|
SignatureCard.initial(),
|
||||||
];
|
];
|
||||||
final bytes = Uint8List.fromList([1, 2, 3, 4, 5]);
|
final bytes = Uint8List.fromList([1, 2, 3, 4, 5]);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ Future<void> aSignatureAssetLoadedOrDrawnIsWrappedInASignatureCard(
|
||||||
container.read(signatureAssetRepositoryProvider.notifier).state = [];
|
container.read(signatureAssetRepositoryProvider.notifier).state = [];
|
||||||
container.read(documentRepositoryProvider.notifier).state =
|
container.read(documentRepositoryProvider.notifier).state =
|
||||||
Document.initial();
|
Document.initial();
|
||||||
container.read(signatureCardProvider.notifier).state = [
|
container.read(signatureCardRepositoryProvider.notifier).state = [
|
||||||
SignatureCard.initial(),
|
SignatureCard.initial(),
|
||||||
];
|
];
|
||||||
final bytes = Uint8List.fromList([1, 2, 3, 4, 5]);
|
final bytes = Uint8List.fromList([1, 2, 3, 4, 5]);
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ Future<void> threeSignaturePlacementsArePlacedOnTheCurrentPage(
|
||||||
container.read(signatureAssetRepositoryProvider.notifier).state = [];
|
container.read(signatureAssetRepositoryProvider.notifier).state = [];
|
||||||
container.read(documentRepositoryProvider.notifier).state =
|
container.read(documentRepositoryProvider.notifier).state =
|
||||||
Document.initial();
|
Document.initial();
|
||||||
container.read(signatureCardProvider.notifier).state = [
|
container.read(signatureCardRepositoryProvider.notifier).state = [
|
||||||
SignatureCard.initial(),
|
SignatureCard.initial(),
|
||||||
];
|
];
|
||||||
container
|
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/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';
|
||||||
|
import 'package:pdf_signature/domain/models/signature_asset.dart';
|
||||||
|
|
||||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||||
// preferences_providers.dart no longer exports pageViewModeProvider
|
// preferences_providers.dart no longer exports pageViewModeProvider
|
||||||
|
|
@ -48,13 +49,329 @@ Future<void> pumpWithOpenPdfAndSig(WidgetTester tester) async {
|
||||||
color: img.ColorUint8.rgb(0, 0, 0),
|
color: img.ColorUint8.rgb(0, 0, 0),
|
||||||
);
|
);
|
||||||
final bytes = img.encodePng(canvas);
|
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
|
// keep drawing for determinism even if bytes unused in simplified UI
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
documentRepositoryProvider.overrideWith(
|
documentRepositoryProvider.overrideWith((ref) {
|
||||||
(ref) => DocumentStateNotifier()..openSample(),
|
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) {
|
signatureAssetRepositoryProvider.overrideWith((ref) {
|
||||||
final repo = SignatureAssetRepository();
|
final repo = SignatureAssetRepository();
|
||||||
repo.add(Uint8List.fromList(bytes), name: 'test');
|
repo.add(Uint8List.fromList(bytes), name: 'test');
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,12 @@ void main() {
|
||||||
|
|
||||||
// Use a persistent container across rebuilds
|
// Use a persistent container across rebuilds
|
||||||
final container = ProviderContainer(
|
final container = ProviderContainer(
|
||||||
overrides: [useMockViewerProvider.overrideWithValue(true)],
|
overrides: [
|
||||||
|
useMockViewerProvider.overrideWithValue(true),
|
||||||
|
documentRepositoryProvider.overrideWith(
|
||||||
|
(ref) => DocumentStateNotifier()..openSample(),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
addTearDown(container.dispose);
|
addTearDown(container.dispose);
|
||||||
|
|
||||||
|
|
@ -100,8 +105,6 @@ void main() {
|
||||||
// Initial pump at base width
|
// Initial pump at base width
|
||||||
await tester.pumpWidget(buildHarness(width: 480));
|
await tester.pumpWidget(buildHarness(width: 480));
|
||||||
|
|
||||||
// Open sample
|
|
||||||
container.read(documentRepositoryProvider.notifier).openSample();
|
|
||||||
// Add a tiny non-empty asset to avoid decode errors
|
// Add a tiny non-empty asset to avoid decode errors
|
||||||
final canvas = img.Image(width: 10, height: 5);
|
final canvas = img.Image(width: 10, height: 5);
|
||||||
img.fill(canvas, color: img.ColorUint8.rgb(0, 0, 0));
|
img.fill(canvas, color: img.ColorUint8.rgb(0, 0, 0));
|
||||||
|
|
@ -117,6 +120,9 @@ void main() {
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
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
|
// Find the first page stack and the placed signature widget
|
||||||
final pageStackFinder = find.byKey(const ValueKey('page_stack_1'));
|
final pageStackFinder = find.byKey(const ValueKey('page_stack_1'));
|
||||||
expect(pageStackFinder, findsOneWidget);
|
expect(pageStackFinder, findsOneWidget);
|
||||||
|
|
@ -124,13 +130,13 @@ void main() {
|
||||||
final placedFinder = find.byKey(const Key('placed_signature_0'));
|
final placedFinder = find.byKey(const Key('placed_signature_0'));
|
||||||
expect(placedFinder, findsOneWidget);
|
expect(placedFinder, findsOneWidget);
|
||||||
|
|
||||||
|
// Ensure the widget is fully laid out
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
final pageBox = tester.getRect(pageStackFinder);
|
final pageBox = tester.getRect(pageStackFinder);
|
||||||
// Measure the positioned overlay area via its DecoratedBox ancestor
|
|
||||||
final placedBox1 = tester.getRect(
|
// The placed signature widget itself is a DecoratedBox
|
||||||
find
|
final placedBox1 = tester.getRect(placedFinder);
|
||||||
.ancestor(of: placedFinder, matching: find.byType(DecoratedBox))
|
|
||||||
.first,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Compute normalized position within the page container
|
// Compute normalized position within the page container
|
||||||
final relX1 = (placedBox1.left - pageBox.left) / pageBox.width;
|
final relX1 = (placedBox1.left - pageBox.left) / pageBox.width;
|
||||||
|
|
@ -142,11 +148,7 @@ void main() {
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
final pageBox2 = tester.getRect(pageStackFinder);
|
final pageBox2 = tester.getRect(pageStackFinder);
|
||||||
final placedBox2 = tester.getRect(
|
final placedBox2 = tester.getRect(placedFinder);
|
||||||
find
|
|
||||||
.ancestor(of: placedFinder, matching: find.byType(DecoratedBox))
|
|
||||||
.first,
|
|
||||||
);
|
|
||||||
|
|
||||||
final relX2 = (placedBox2.left - pageBox2.left) / pageBox2.width;
|
final relX2 = (placedBox2.left - pageBox2.left) / pageBox2.width;
|
||||||
final relY2 = (placedBox2.top - pageBox2.top) / pageBox2.height;
|
final relY2 = (placedBox2.top - pageBox2.top) / pageBox2.height;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue