import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import '../../../common/domain/metal_type.dart'; import '../../../common/domain/money.dart'; import '../../../common/widgets/adaptive.dart'; import '../application/exchange_calculator_controller.dart'; import '../application/market_controller.dart'; import '../data/market_models.dart'; class MarketPage extends ConsumerWidget { const MarketPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final market = ref.watch(marketControllerProvider); final exchange = ref.watch(exchangeCalculatorControllerProvider); final cs = ShadTheme.of(context).colorScheme; return SafeArea( top: false, child: ListView( padding: const EdgeInsets.fromLTRB(18, 18, 18, 24), children: [ Center( child: ConstrainedBox( constraints: const BoxConstraints( maxWidth: Adaptive.contentMaxWidth, ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SegmentedButton( segments: [ for (final metal in MetalType.values) ButtonSegment(value: metal, label: Text(metal.label)), ], selected: {market.selectedMetal}, onSelectionChanged: (next) { final metal = next.first; ref .read(marketControllerProvider.notifier) .selectMetal(metal); ref .read(exchangeCalculatorControllerProvider.notifier) .selectMetal(metal); }, ), const SizedBox(height: 12), _Card( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(market.quote.basisLabel, style: _mutedStyle(cs)), const SizedBox(height: 8), Text( '${formatCny(market.quote.spotPrice)} / 克', style: TextStyle( fontSize: 30, fontWeight: FontWeight.w800, color: cs.foreground, ), ), const SizedBox(height: 6), Text( '${market.quote.changeAmount >= 0 ? '+' : ''}${market.quote.changeAmount.toStringAsFixed(2)} · ${market.quote.changePercent.toStringAsFixed(2)}%', style: TextStyle( color: market.quote.changeAmount >= 0 ? const Color(0xFFC0392B) : const Color(0xFF2E8B6F), ), ), const SizedBox(height: 14), Wrap( spacing: 8, children: [ for (final period in MarketPeriod.values) ChoiceChip( label: Text(period.label), selected: period == market.period, onSelected: (_) => ref .read(marketControllerProvider.notifier) .selectPeriod(period), ), ], ), const SizedBox(height: 14), Text( '走势图 mock 点位:${market.points.length} 个', style: _mutedStyle(cs), ), ], ), ), const SizedBox(height: 12), _Card( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '兑换试算', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, color: cs.foreground, ), ), const SizedBox(height: 10), Text( '${formatGram(exchange.gram)} ${exchange.metal.label} = ${formatCny(exchange.amount)}', style: TextStyle(fontSize: 20, color: cs.foreground), ), const SizedBox(height: 8), Text( '1 克 ${exchange.metal.label} = ${formatCny(exchange.selectedChannel.pricePerGram)} · ${exchange.selectedChannel.source}', style: _mutedStyle(cs), ), const SizedBox(height: 10), Wrap( spacing: 8, runSpacing: 8, children: [ for (final channel in exchange.channels) ChoiceChip( label: Text(channel.name), selected: channel.id == exchange.selectedChannel.id, onSelected: (_) => ref .read( exchangeCalculatorControllerProvider .notifier, ) .selectChannel(channel.id), ), ], ), const SizedBox(height: 8), Text( exchange.selectedChannel.hint, style: _mutedStyle(cs), ), ], ), ), ], ), ), ), ], ), ); } } class _Card extends StatelessWidget { const _Card({required this.child}); final Widget child; @override Widget build(BuildContext context) { final cs = ShadTheme.of(context).colorScheme; return DecoratedBox( decoration: BoxDecoration( color: cs.card, border: Border.all(color: cs.border), borderRadius: BorderRadius.circular(12), ), child: Padding(padding: const EdgeInsets.all(16), child: child), ); } } TextStyle _mutedStyle(ShadColorScheme cs) { return TextStyle(fontSize: 13, color: cs.mutedForeground, letterSpacing: 0); }