feat: enrich product visual depth

This commit is contained in:
2026-06-12 02:49:46 +08:00
parent 185492395e
commit e7e4c78865
5 changed files with 1075 additions and 424 deletions
@@ -37,17 +37,16 @@ class JourneyOverviewPanel extends StatelessWidget {
return SurfacePanel(
elevated: true,
padding: EdgeInsets.zero,
color: scheme.surfaceContainerLowest,
borderColor: scheme.outlineVariant.withValues(alpha: 0.45),
child: Container(
padding: const EdgeInsets.fromLTRB(18, 16, 16, 18),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
scheme.primary.withValues(alpha: 0.12),
scheme.surfaceContainerLowest,
scheme.secondary.withValues(alpha: 0.07),
scheme.primary.withValues(alpha: 0.20),
scheme.surfaceContainerLowest.withValues(alpha: 0.96),
scheme.secondary.withValues(alpha: 0.14),
],
),
),
@@ -64,23 +63,46 @@ class JourneyOverviewPanel extends StatelessWidget {
onReflect: onReflect,
);
if (compact) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
return Stack(
children: [
const Align(
alignment: Alignment.centerRight,
child: SeedIllustration(size: 88),
const Positioned(
right: -18,
top: -20,
child: Opacity(
opacity: 0.58,
child: SeedIllustration(size: 128, hero: true),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 17, 106, 18),
child: textContent,
),
const SizedBox(height: 8),
textContent,
],
);
}
return Row(
return Stack(
children: [
Expanded(child: textContent),
const SizedBox(width: 12),
const SeedIllustration(size: 112),
Positioned(
right: -28,
top: -22,
bottom: -18,
child: Opacity(
opacity: 0.96,
child: SeedIllustration(
size: constraints.maxWidth.clamp(146, 184).toDouble(),
hero: true,
),
),
),
Positioned(
right: 92,
bottom: 18,
child: _ProgressBadge(completed: completed),
),
Padding(
padding: const EdgeInsets.fromLTRB(18, 17, 168, 18),
child: textContent,
),
],
);
},
@@ -140,16 +162,9 @@ class _OverviewText extends StatelessWidget {
color: scheme.onSurfaceVariant,
),
),
const SizedBox(height: 14),
ClipRRect(
borderRadius: BorderRadius.circular(99),
child: LinearProgressIndicator(
minHeight: 8,
value: progressValue,
backgroundColor: scheme.surfaceContainerHigh,
),
),
const SizedBox(height: 10),
const SizedBox(height: 16),
_ProgressSeeds(completed: completed, progressValue: progressValue),
const SizedBox(height: 12),
Row(
children: [
Expanded(
@@ -172,3 +187,86 @@ class _OverviewText extends StatelessWidget {
);
}
}
class _ProgressSeeds extends StatelessWidget {
const _ProgressSeeds({required this.completed, required this.progressValue});
final int completed;
final double progressValue;
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Semantics(
value: '${(progressValue * 100).round()}%',
child: Row(
children: List.generate(4, (index) {
final filled = index < completed;
return Expanded(
child: AnimatedContainer(
duration: const Duration(milliseconds: 220),
curve: Curves.easeOutCubic,
height: 10,
margin: EdgeInsets.only(right: index == 3 ? 0 : 7),
decoration: BoxDecoration(
color:
filled
? scheme.primary
: scheme.surfaceContainerHigh.withValues(alpha: 0.82),
borderRadius: BorderRadius.circular(99),
boxShadow:
filled
? [
BoxShadow(
color: scheme.primary.withValues(alpha: 0.28),
blurRadius: 10,
offset: const Offset(0, 4),
),
]
: null,
),
),
);
}),
),
);
}
}
class _ProgressBadge extends StatelessWidget {
const _ProgressBadge({required this.completed});
final int completed;
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
final theme = Theme.of(context);
return DecoratedBox(
decoration: BoxDecoration(
color: scheme.surfaceContainerLowest.withValues(alpha: 0.82),
borderRadius: BorderRadius.circular(99),
border: Border.all(
color: scheme.outlineVariant.withValues(alpha: 0.65),
),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.10),
blurRadius: 14,
offset: const Offset(0, 7),
),
],
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 11, vertical: 7),
child: Text(
'$completed/4',
style: theme.textTheme.labelLarge?.copyWith(
color: scheme.primary,
fontWeight: FontWeight.w900,
),
),
),
);
}
}