diff --git a/lib/features/dashboard/widgets/account_editor_overlay/account_editor_overlay.dart b/lib/features/dashboard/widgets/account_editor_overlay/account_editor_overlay.dart index 222920d..d1aed00 100644 --- a/lib/features/dashboard/widgets/account_editor_overlay/account_editor_overlay.dart +++ b/lib/features/dashboard/widgets/account_editor_overlay/account_editor_overlay.dart @@ -226,32 +226,6 @@ class _AccountEditorOverlayState extends State { ), ), ), - Positioned( - top: cardTop - 28, - left: 0, - right: 0, - child: Center( - child: _HeightHandle( - onDrag: (delta) { - final newHeight = cardHeight - delta * 2; - ref.read(cardHeightProvider.notifier).set(newHeight); - }, - ), - ), - ), - Positioned( - top: cardTop + cardHeight + 6, - left: 0, - right: 0, - child: Center( - child: _HeightHandle( - onDrag: (delta) { - final newHeight = cardHeight + delta * 2; - ref.read(cardHeightProvider.notifier).set(newHeight); - }, - ), - ), - ), Positioned( top: editorPanelTop, left: 20, @@ -495,36 +469,3 @@ class _AccountEditorOverlayState extends State { ); } } - -class _HeightHandle extends StatefulWidget { - final ValueChanged onDrag; - - const _HeightHandle({required this.onDrag}); - - @override - State<_HeightHandle> createState() => _HeightHandleState(); -} - -class _HeightHandleState extends State<_HeightHandle> { - bool _active = false; - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - return GestureDetector( - onVerticalDragStart: (_) => setState(() => _active = true), - onVerticalDragUpdate: (details) => widget.onDrag(details.delta.dy), - onVerticalDragEnd: (_) => setState(() => _active = false), - behavior: HitTestBehavior.opaque, - child: AnimatedContainer( - duration: const Duration(milliseconds: 150), - width: _active ? 56 : 44, - height: _active ? 6 : 4, - decoration: BoxDecoration( - color: theme.colorScheme.onSurface.withOpacity(_active ? 0.5 : 0.25), - borderRadius: BorderRadius.circular(3), - ), - ), - ); - } -} diff --git a/lib/features/dashboard/widgets/color_editor_overlay.dart b/lib/features/dashboard/widgets/color_editor_overlay.dart index f85c81f..39bf753 100644 --- a/lib/features/dashboard/widgets/color_editor_overlay.dart +++ b/lib/features/dashboard/widgets/color_editor_overlay.dart @@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/l10n/app_strings.dart'; import '../../../core/l10n/locale_provider.dart'; import '../../../core/services/card_color_service.dart'; +import '../../../core/services/haptic_service.dart'; import '../../../core/utils/card_layout.dart'; import '../../settings/provider.dart'; import '../provider.dart'; @@ -41,7 +42,9 @@ class _FullScreenBlurOverlayState extends State { return Consumer( builder: (context, ref, _) { final cardHeight = ref.watch(cardHeightProvider); - final panelTop = cardTop + cardHeight + layout.cardPreviewGap; + final heightDelta = (kBalanceCardHeight - cardHeight) / 2; + final adjustedCardTop = cardTop + heightDelta; + final panelTop = adjustedCardTop + cardHeight + layout.cardPreviewGap; final panelHeight = layout.colorPanelHeight(mq, panelTop); return Material( @@ -63,7 +66,7 @@ class _FullScreenBlurOverlayState extends State { ), ), Positioned( - top: cardTop, + top: adjustedCardTop, left: 0, right: 0, child: FractionallySizedBox( @@ -72,19 +75,38 @@ class _FullScreenBlurOverlayState extends State { height: cardHeight, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), - child: Consumer( - builder: (ctx, ref, _) => BalanceCard( - balance: ref.read(totalBalanceProvider), - currencyInfo: ref.read(currencyProvider), - onLongPress: null, - previewPrimary: dash.tempPrimary, - previewSecondary: dash.tempSecondary, - previewGradientType: - Theme.of(widget.context).brightness == Brightness.dark - ? dash.tempDarkGradientType - : dash.tempLightGradientType, - cardHeight: cardHeight, - ), + child: Stack( + clipBehavior: Clip.none, + children: [ + Consumer( + builder: (ctx, ref, _) => BalanceCard( + balance: ref.read(totalBalanceProvider), + currencyInfo: ref.read(currencyProvider), + onLongPress: null, + previewPrimary: dash.tempPrimary, + previewSecondary: dash.tempSecondary, + previewGradientType: + Theme.of(widget.context).brightness == Brightness.dark + ? dash.tempDarkGradientType + : dash.tempLightGradientType, + cardHeight: cardHeight, + ), + ), + Positioned( + left: 8, + right: 8, + bottom: -20, + child: _HeightResizeHandle( + cardHeight: cardHeight, + onHeightChanged: (newHeight) { + ref.read(cardHeightProvider.notifier).set(newHeight); + if (ref.read(hapticEnabledProvider)) { + HapticService.light(); + } + }, + ), + ), + ], ), ), ), @@ -790,3 +812,89 @@ class PanelTab extends StatelessWidget { ); } } + +class _HeightResizeHandle extends StatefulWidget { + final double cardHeight; + final ValueChanged onHeightChanged; + + const _HeightResizeHandle({ + required this.cardHeight, + required this.onHeightChanged, + }); + + @override + State<_HeightResizeHandle> createState() => _HeightResizeHandleState(); +} + +class _HeightResizeHandleState extends State<_HeightResizeHandle> { + bool _dragging = false; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return GestureDetector( + onVerticalDragStart: (_) => setState(() => _dragging = true), + onVerticalDragUpdate: (details) { + final newHeight = widget.cardHeight + details.delta.dy * 2; + widget.onHeightChanged(newHeight); + }, + onVerticalDragEnd: (_) => setState(() => _dragging = false), + behavior: HitTestBehavior.opaque, + child: Container( + height: 32, + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CustomPaint( + size: const Size(double.infinity, 1), + painter: _DashedLinePainter( + color: theme.colorScheme.onSurface.withOpacity(_dragging ? 0.4 : 0.2), + ), + ), + const SizedBox(height: 8), + AnimatedContainer( + duration: const Duration(milliseconds: 150), + width: _dragging ? 48 : 36, + height: _dragging ? 5 : 4, + decoration: BoxDecoration( + color: theme.colorScheme.onSurface.withOpacity(_dragging ? 0.5 : 0.25), + borderRadius: BorderRadius.circular(3), + ), + ), + ], + ), + ), + ); + } +} + +class _DashedLinePainter extends CustomPainter { + final Color color; + + const _DashedLinePainter({required this.color}); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = color + ..strokeWidth = 1 + ..style = PaintingStyle.stroke; + + const dashWidth = 6.0; + const dashSpace = 4.0; + double x = 0; + while (x < size.width) { + canvas.drawLine( + Offset(x, 0), + Offset(x + dashWidth, 0), + paint, + ); + x += dashWidth + dashSpace; + } + } + + @override + bool shouldRepaint(covariant _DashedLinePainter oldDelegate) => + color != oldDelegate.color; +} diff --git a/lib/features/settings/provider.dart b/lib/features/settings/provider.dart index e2d7c83..92990d3 100644 --- a/lib/features/settings/provider.dart +++ b/lib/features/settings/provider.dart @@ -109,8 +109,8 @@ final cardHeightProvider = NotifierProvider( class CardHeightNotifier extends Notifier { static const _key = 'card_height'; - static const _minHeight = 160.0; - static const _maxHeight = 280.0; + static const _minHeight = 140.0; + static const _maxHeight = 200.0; static const _defaultHeight = 200.0; @override