From 34b895b6884f261c8ae1eb8364a1747a8048dc12 Mon Sep 17 00:00:00 2001 From: PiscesXD Date: Wed, 12 Aug 2026 00:21:50 +0800 Subject: [PATCH] fix: card --- .../pages/report_detail_page.dart | 98 +++++++++++-------- pubspec.lock | 4 +- 2 files changed, 61 insertions(+), 41 deletions(-) diff --git a/lib/features/earthquake/presentation/pages/report_detail_page.dart b/lib/features/earthquake/presentation/pages/report_detail_page.dart index c34f6c552..bc79b0632 100644 --- a/lib/features/earthquake/presentation/pages/report_detail_page.dart +++ b/lib/features/earthquake/presentation/pages/report_detail_page.dart @@ -412,6 +412,10 @@ class _ReportSheetState extends State<_ReportSheet> { final DraggableScrollableController _dragController = DraggableScrollableController(); + /// [_onNotification] + ScrollController? _scrollController; + bool _wasExpanded = false; + @override void dispose() { _extent.dispose(); @@ -437,11 +441,12 @@ class _ReportSheetState extends State<_ReportSheet> { child: NotificationListener( onNotification: (notification) { _extent.value = notification.extent; - // Runs from the scroll-notification dispatch, not mid-build, so - // it's safe to update the page-level notifier the floating back - // button listens to. - widget.expandedNotifier.value = - notification.extent >= _ReportSheet._switchThreshold; + final expanded = notification.extent >= _ReportSheet._switchThreshold; + widget.expandedNotifier.value = expanded; + if (_wasExpanded && !expanded) { + _scrollController?.jumpTo(0); + } + _wasExpanded = expanded; return false; }, child: DraggableScrollableSheet( @@ -451,6 +456,7 @@ class _ReportSheetState extends State<_ReportSheet> { maxChildSize: _ReportSheet._expanded, snap: true, builder: (context, scrollController) { + _scrollController = scrollController; return DecoratedBox( decoration: BoxDecoration( color: colors.surface, @@ -469,17 +475,14 @@ class _ReportSheetState extends State<_ReportSheet> { valueListenable: _extent, builder: (context, extent, _) { final expanded = extent >= _ReportSheet._switchThreshold; - // Flush with the screen top at full extent — the status - // bar/notch inset has to come back as padding on the - // expanded header, or it renders underneath it. final atTop = extent >= _ReportSheet._expanded - 0.02; + final revealProgress = + ((extent - _ReportSheet.peek) / + (_ReportSheet._expanded - _ReportSheet.peek)) + .clamp(0.0, 1.0); + final peekOpacity = 1 - revealProgress; return Column( children: [ - // Pinned above the scroll body, not a scrolling item: - // the "地震報告" bar (back button + title) stays while - // the 詳細資訊 content scrolls underneath it. Keep the - // ListView's horizontal inset on the header too, so it - // sits where it did when it was the list's first child. if (expanded) Padding( padding: const EdgeInsets.symmetric( @@ -500,9 +503,28 @@ class _ReportSheetState extends State<_ReportSheet> { AppSpacing.xl + MediaQuery.paddingOf(context).bottom, ), - children: expanded - ? _expandedContent(context, report) - : _peekContent(context, report), + children: [ + Stack( + alignment: Alignment.topCenter, + children: [ + Opacity( + opacity: revealProgress, + child: IgnorePointer( + ignoring: !expanded, + child: _expandedContent(context, report), + ), + ), + if (peekOpacity > 0) + Opacity( + opacity: peekOpacity, + child: IgnorePointer( + ignoring: expanded, + child: _peekContent(context, report), + ), + ), + ], + ), + ], ), ), ], @@ -517,30 +539,27 @@ class _ReportSheetState extends State<_ReportSheet> { ); } - List _peekContent(BuildContext context, EarthquakeReport report) => [ - _ReportPeekSummary(report: report), - ]; + Widget _peekContent(BuildContext context, EarthquakeReport report) => + _ReportPeekSummary(report: report); - List _expandedContent(BuildContext context, EarthquakeReport report) { + Widget _expandedContent(BuildContext context, EarthquakeReport report) { final l10n = AppLocalizations.of(context); - return [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _ReportHeader(report: report), - const SizedBox(height: AppSpacing.lg), - SectionHeader(l10n.reportDetailInfo), - _ReportInfoCard(report: report), - _LocalIntensitySection(report: report), - if (report.list.isNotEmpty) _AreaIntensitySection(report: report), - SectionHeader(l10n.reportDetailImage), - _ReportImageCard(report: report), - ], - ), + return Padding( + padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _ReportHeader(report: report), + const SizedBox(height: AppSpacing.lg), + SectionHeader(l10n.reportDetailInfo), + _ReportInfoCard(report: report), + _LocalIntensitySection(report: report), + if (report.list.isNotEmpty) _AreaIntensitySection(report: report), + SectionHeader(l10n.reportDetailImage), + _ReportImageCard(report: report), + ], ), - ]; + ); } } @@ -698,8 +717,8 @@ class _PeekStat extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, @@ -707,6 +726,7 @@ class _PeekStat extends StatelessWidget { color: theme.colorScheme.onSurfaceVariant, ), ), + const SizedBox(height: AppSpacing.xs), Text( value, style: theme.textTheme.bodyLarge?.copyWith( diff --git a/pubspec.lock b/pubspec.lock index a53ef4d30..29e4f24ba 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -850,10 +850,10 @@ packages: dependency: transitive description: name: posix - sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07" + sha256: bc1bad54ad2b735816e31f8d4600cfde6c7839975085ddfbca48b6c9f7c4044e url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.5.2" provider: dependency: "direct main" description: