fix: graphic adjust dialog has to show image preview

This commit is contained in:
insleker 2025-09-12 22:44:00 +08:00
parent 461c8f6ae5
commit 8f3039f99e
6 changed files with 110 additions and 24 deletions

View File

@ -63,6 +63,7 @@ class MyApp extends StatelessWidget {
], ],
routerConfig: ref.watch(routerProvider), routerConfig: ref.watch(routerProvider),
builder: (context, child) { builder: (context, child) {
final router = ref.watch(routerProvider);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(AppLocalizations.of(context).appTitle), title: Text(AppLocalizations.of(context).appTitle),
@ -73,7 +74,11 @@ class MyApp extends StatelessWidget {
label: Text(AppLocalizations.of(context).settings), label: Text(AppLocalizations.of(context).settings),
onPressed: onPressed:
() => showDialog<bool>( () => showDialog<bool>(
context: context, context:
router
.routerDelegate
.navigatorKey
.currentContext!,
builder: (_) => const SettingsDialog(), builder: (_) => const SettingsDialog(),
), ),
), ),

View File

@ -1,4 +1,5 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart'; import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
@ -79,6 +80,9 @@ final routerProvider = Provider<GoRouter>((ref) {
signatureCardRepositoryProvider.notifier, signatureCardRepositoryProvider.notifier,
); );
// Create a navigator key for the router
final navigatorKey = GlobalKey<NavigatorState>();
// Create a late variable for the router // Create a late variable for the router
late final GoRouter router; late final GoRouter router;
@ -90,6 +94,7 @@ final routerProvider = Provider<GoRouter>((ref) {
final initialLocation = documentNotifier.debugState.loaded ? '/pdf' : '/'; final initialLocation = documentNotifier.debugState.loaded ? '/pdf' : '/';
router = GoRouter( router = GoRouter(
navigatorKey: navigatorKey,
routes: [ routes: [
GoRoute( GoRoute(
path: '/', path: '/',

View File

@ -33,13 +33,6 @@ class AdjustmentsPanel extends StatelessWidget {
runSpacing: 8, runSpacing: 8,
crossAxisAlignment: WrapCrossAlignment.center, crossAxisAlignment: WrapCrossAlignment.center,
children: [ 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( Switch(
key: const Key('swt_bg_removal'), key: const Key('swt_bg_removal'),
value: bgRemoval, value: bgRemoval,

View File

@ -1,22 +1,51 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:pdf_signature/l10n/app_localizations.dart'; import 'package:pdf_signature/l10n/app_localizations.dart';
import 'adjustments_panel.dart'; import '../../pdf/widgets/adjustments_panel.dart';
// No live preview wiring in simplified dialog 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 { 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 @override
State<ImageEditorDialog> createState() => _ImageEditorDialogState(); State<ImageEditorDialog> createState() => _ImageEditorDialogState();
} }
class _ImageEditorDialogState extends State<ImageEditorDialog> { class _ImageEditorDialogState extends State<ImageEditorDialog> {
// Local-only state for demo/tests; no persistence to repositories. late bool _aspectLocked;
bool _aspectLocked = false; late bool _bgRemoval;
bool _bgRemoval = false; late double _contrast;
double _contrast = 1.0; // 0..2 late double _brightness;
double _brightness = 0.0; // -1..1 late double _rotation;
double _rotation = 0.0; // -180..180
@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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -37,7 +66,7 @@ class _ImageEditorDialogState extends State<ImageEditorDialog> {
style: Theme.of(context).textTheme.titleMedium, style: Theme.of(context).textTheme.titleMedium,
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
// Preview placeholder; no actual processed bytes wired // Preview with actual signature image
SizedBox( SizedBox(
height: 160, height: 160,
child: DecoratedBox( child: DecoratedBox(
@ -45,7 +74,13 @@ class _ImageEditorDialogState extends State<ImageEditorDialog> {
border: Border.all(color: Theme.of(context).dividerColor), border: Border.all(color: Theme.of(context).dividerColor),
borderRadius: BorderRadius.circular(8), 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), const SizedBox(height: 12),
@ -84,7 +119,17 @@ class _ImageEditorDialogState extends State<ImageEditorDialog> {
children: [ children: [
TextButton( TextButton(
key: const Key('btn_image_editor_close'), 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( child: Text(
MaterialLocalizations.of(context).closeButtonLabel, MaterialLocalizations.of(context).closeButtonLabel,
), ),

View File

@ -112,6 +112,13 @@ class _RotatedSignatureImageState extends State<RotatedSignatureImage> {
filterQuality: widget.filterQuality, filterQuality: widget.filterQuality,
alignment: widget.alignment, alignment: widget.alignment,
semanticLabel: widget.semanticLabel, 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) { if (angle != 0.0) {

View File

@ -2,11 +2,12 @@ import 'dart:typed_data';
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:pdf_signature/l10n/app_localizations.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_asset_repository.dart';
import 'package:pdf_signature/data/repositories/signature_card_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'; import 'signature_card.dart';
/// Data for drag-and-drop is in signature_drag_data.dart /// Data for drag-and-drop is in signature_drag_data.dart
@ -59,10 +60,20 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
.remove(card), .remove(card),
onAdjust: () async { onAdjust: () async {
if (!mounted) return; if (!mounted) return;
await showDialog( final result = await showDialog<ImageEditorResult>(
context: context, 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: () { onTap: () {
// state = const Rect.fromLTWH(0.2, 0.2, 0.3, 0.15); // state = const Rect.fromLTWH(0.2, 0.2, 0.3, 0.15);
@ -107,12 +118,22 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
await widget.onLoadSignatureFromFile(); await widget.onLoadSignatureFromFile();
final b = loaded; final b = loaded;
if (b != null) { if (b != null) {
final asset = SignatureAsset(
bytes: b,
name: 'image',
);
ref ref
.read( .read(
signatureAssetRepositoryProvider signatureAssetRepositoryProvider
.notifier, .notifier,
) )
.add(b, name: 'image'); .add(b, name: 'image');
ref
.read(
signatureCardRepositoryProvider
.notifier,
)
.addWithAsset(asset, 0.0);
} }
}, },
icon: const Icon(Icons.image_outlined), icon: const Icon(Icons.image_outlined),
@ -127,12 +148,22 @@ class _SignatureDrawerState extends ConsumerState<SignatureDrawer> {
final drawn = await widget.onOpenDrawCanvas(); final drawn = await widget.onOpenDrawCanvas();
final b = drawn; final b = drawn;
if (b != null) { if (b != null) {
final asset = SignatureAsset(
bytes: b,
name: 'drawing',
);
ref ref
.read( .read(
signatureAssetRepositoryProvider signatureAssetRepositoryProvider
.notifier, .notifier,
) )
.add(b, name: 'drawing'); .add(b, name: 'drawing');
ref
.read(
signatureCardRepositoryProvider
.notifier,
)
.addWithAsset(asset, 0.0);
} }
}, },
icon: const Icon(Icons.gesture), icon: const Icon(Icons.gesture),