From 8f3039f99e19cd7f8f3120aaa7db3f7dac886e2a Mon Sep 17 00:00:00 2001 From: insleker Date: Fri, 12 Sep 2025 22:44:00 +0800 Subject: [PATCH] fix: graphic adjust dialog has to show image preview --- lib/app.dart | 7 +- lib/routing/router.dart | 5 ++ .../pdf/widgets/adjustments_panel.dart | 7 -- .../widgets/image_editor_dialog.dart | 69 +++++++++++++++---- .../widgets/rotated_signature_image.dart | 7 ++ .../signature/widgets/signature_drawer.dart | 39 +++++++++-- 6 files changed, 110 insertions(+), 24 deletions(-) rename lib/ui/features/{pdf => signature}/widgets/image_editor_dialog.dart (62%) diff --git a/lib/app.dart b/lib/app.dart index b377ee2..d967cec 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -63,6 +63,7 @@ class MyApp extends StatelessWidget { ], routerConfig: ref.watch(routerProvider), builder: (context, child) { + final router = ref.watch(routerProvider); return Scaffold( appBar: AppBar( title: Text(AppLocalizations.of(context).appTitle), @@ -73,7 +74,11 @@ class MyApp extends StatelessWidget { label: Text(AppLocalizations.of(context).settings), onPressed: () => showDialog( - context: context, + context: + router + .routerDelegate + .navigatorKey + .currentContext!, builder: (_) => const SettingsDialog(), ), ), diff --git a/lib/routing/router.dart b/lib/routing/router.dart index e0ffda3..5b5d9b9 100644 --- a/lib/routing/router.dart +++ b/lib/routing/router.dart @@ -1,4 +1,5 @@ import 'dart:typed_data'; +import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart'; @@ -79,6 +80,9 @@ final routerProvider = Provider((ref) { signatureCardRepositoryProvider.notifier, ); + // Create a navigator key for the router + final navigatorKey = GlobalKey(); + // Create a late variable for the router late final GoRouter router; @@ -90,6 +94,7 @@ final routerProvider = Provider((ref) { final initialLocation = documentNotifier.debugState.loaded ? '/pdf' : '/'; router = GoRouter( + navigatorKey: navigatorKey, routes: [ GoRoute( path: '/', diff --git a/lib/ui/features/pdf/widgets/adjustments_panel.dart b/lib/ui/features/pdf/widgets/adjustments_panel.dart index 8a7396b..11cdd20 100644 --- a/lib/ui/features/pdf/widgets/adjustments_panel.dart +++ b/lib/ui/features/pdf/widgets/adjustments_panel.dart @@ -33,13 +33,6 @@ class AdjustmentsPanel extends StatelessWidget { runSpacing: 8, crossAxisAlignment: WrapCrossAlignment.center, children: [ - Checkbox( - key: const Key('chk_aspect_lock'), - value: aspectLocked, - onChanged: (v) => onAspectLockedChanged(v ?? false), - ), - Text(AppLocalizations.of(context).lockAspectRatio), - const SizedBox(width: 16), Switch( key: const Key('swt_bg_removal'), value: bgRemoval, diff --git a/lib/ui/features/pdf/widgets/image_editor_dialog.dart b/lib/ui/features/signature/widgets/image_editor_dialog.dart similarity index 62% rename from lib/ui/features/pdf/widgets/image_editor_dialog.dart rename to lib/ui/features/signature/widgets/image_editor_dialog.dart index 8c21e2c..714f902 100644 --- a/lib/ui/features/pdf/widgets/image_editor_dialog.dart +++ b/lib/ui/features/signature/widgets/image_editor_dialog.dart @@ -1,22 +1,51 @@ import 'package:flutter/material.dart'; import 'package:pdf_signature/l10n/app_localizations.dart'; -import 'adjustments_panel.dart'; -// No live preview wiring in simplified dialog +import '../../pdf/widgets/adjustments_panel.dart'; +import '../../../../domain/models/model.dart' as domain; +import 'rotated_signature_image.dart'; + +class ImageEditorResult { + final double rotation; + final domain.GraphicAdjust graphicAdjust; + + const ImageEditorResult({ + required this.rotation, + required this.graphicAdjust, + }); +} class ImageEditorDialog extends StatefulWidget { - const ImageEditorDialog({super.key}); + const ImageEditorDialog({ + super.key, + required this.asset, + required this.initialRotation, + required this.initialGraphicAdjust, + }); + + final domain.SignatureAsset asset; + final double initialRotation; + final domain.GraphicAdjust initialGraphicAdjust; @override State createState() => _ImageEditorDialogState(); } class _ImageEditorDialogState extends State { - // Local-only state for demo/tests; no persistence to repositories. - bool _aspectLocked = false; - bool _bgRemoval = false; - double _contrast = 1.0; // 0..2 - double _brightness = 0.0; // -1..1 - double _rotation = 0.0; // -180..180 + late bool _aspectLocked; + late bool _bgRemoval; + late double _contrast; + late double _brightness; + late double _rotation; + + @override + void initState() { + super.initState(); + _aspectLocked = false; // Not persisted in GraphicAdjust + _bgRemoval = widget.initialGraphicAdjust.bgRemoval; + _contrast = widget.initialGraphicAdjust.contrast; + _brightness = widget.initialGraphicAdjust.brightness; + _rotation = widget.initialRotation; + } @override Widget build(BuildContext context) { @@ -37,7 +66,7 @@ class _ImageEditorDialogState extends State { style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 12), - // Preview placeholder; no actual processed bytes wired + // Preview with actual signature image SizedBox( height: 160, child: DecoratedBox( @@ -45,7 +74,13 @@ class _ImageEditorDialogState extends State { border: Border.all(color: Theme.of(context).dividerColor), borderRadius: BorderRadius.circular(8), ), - child: const Center(child: Text('No signature loaded')), + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: RotatedSignatureImage( + bytes: widget.asset.bytes, + rotationDeg: _rotation, + ), + ), ), ), const SizedBox(height: 12), @@ -84,7 +119,17 @@ class _ImageEditorDialogState extends State { children: [ TextButton( key: const Key('btn_image_editor_close'), - onPressed: () => Navigator.of(context).pop(), + onPressed: + () => Navigator.of(context).pop( + ImageEditorResult( + rotation: _rotation, + graphicAdjust: domain.GraphicAdjust( + contrast: _contrast, + brightness: _brightness, + bgRemoval: _bgRemoval, + ), + ), + ), child: Text( MaterialLocalizations.of(context).closeButtonLabel, ), diff --git a/lib/ui/features/signature/widgets/rotated_signature_image.dart b/lib/ui/features/signature/widgets/rotated_signature_image.dart index fddb4de..e753714 100644 --- a/lib/ui/features/signature/widgets/rotated_signature_image.dart +++ b/lib/ui/features/signature/widgets/rotated_signature_image.dart @@ -112,6 +112,13 @@ class _RotatedSignatureImageState extends State { filterQuality: widget.filterQuality, alignment: widget.alignment, semanticLabel: widget.semanticLabel, + errorBuilder: (context, error, stackTrace) { + // Return a placeholder for invalid images + return Container( + color: Colors.grey[300], + child: const Icon(Icons.broken_image, color: Colors.grey), + ); + }, ); if (angle != 0.0) { diff --git a/lib/ui/features/signature/widgets/signature_drawer.dart b/lib/ui/features/signature/widgets/signature_drawer.dart index a3c23d0..1b5584e 100644 --- a/lib/ui/features/signature/widgets/signature_drawer.dart +++ b/lib/ui/features/signature/widgets/signature_drawer.dart @@ -2,11 +2,12 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:pdf_signature/l10n/app_localizations.dart'; -// No direct model construction needed here +// Direct model construction is needed for creating SignatureAssets import 'package:pdf_signature/data/repositories/signature_asset_repository.dart'; import 'package:pdf_signature/data/repositories/signature_card_repository.dart'; -import '../../pdf/widgets/image_editor_dialog.dart'; +import 'package:pdf_signature/domain/models/model.dart' hide SignatureCard; +import 'image_editor_dialog.dart'; import 'signature_card.dart'; /// Data for drag-and-drop is in signature_drag_data.dart @@ -59,10 +60,20 @@ class _SignatureDrawerState extends ConsumerState { .remove(card), onAdjust: () async { if (!mounted) return; - await showDialog( + final result = await showDialog( context: context, - builder: (_) => const ImageEditorDialog(), + builder: + (_) => ImageEditorDialog( + asset: card.asset, + initialRotation: card.rotationDeg, + initialGraphicAdjust: card.graphicAdjust, + ), ); + if (result != null && mounted) { + ref + .read(signatureCardRepositoryProvider.notifier) + .update(card, result.rotation, result.graphicAdjust); + } }, onTap: () { // state = const Rect.fromLTWH(0.2, 0.2, 0.3, 0.15); @@ -107,12 +118,22 @@ class _SignatureDrawerState extends ConsumerState { await widget.onLoadSignatureFromFile(); final b = loaded; if (b != null) { + final asset = SignatureAsset( + bytes: b, + name: 'image', + ); ref .read( signatureAssetRepositoryProvider .notifier, ) .add(b, name: 'image'); + ref + .read( + signatureCardRepositoryProvider + .notifier, + ) + .addWithAsset(asset, 0.0); } }, icon: const Icon(Icons.image_outlined), @@ -127,12 +148,22 @@ class _SignatureDrawerState extends ConsumerState { final drawn = await widget.onOpenDrawCanvas(); final b = drawn; if (b != null) { + final asset = SignatureAsset( + bytes: b, + name: 'drawing', + ); ref .read( signatureAssetRepositoryProvider .notifier, ) .add(b, name: 'drawing'); + ref + .read( + signatureCardRepositoryProvider + .notifier, + ) + .addWithAsset(asset, 0.0); } }, icon: const Icon(Icons.gesture),