151 lines
5.0 KiB
Dart
151 lines
5.0 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import '../../../core/theme/app_spacing.dart';
|
|
import '../../../core/theme/app_theme.dart';
|
|
import '../../../core/event/app_event_bus.dart';
|
|
import '../../../providers/asset_provider.dart';
|
|
import 'components/action_buttons_row.dart';
|
|
import 'components/asset_dialogs.dart';
|
|
import 'components/balance_card.dart';
|
|
import 'components/holdings_section.dart';
|
|
import 'components/records_link_row.dart';
|
|
import '../orders/fund_orders_page.dart';
|
|
import 'transfer_page.dart';
|
|
|
|
/// 资产页面 - Matching .pen design spec (CMcqs)
|
|
class AssetPage extends StatefulWidget {
|
|
const AssetPage({super.key});
|
|
|
|
@override
|
|
State<AssetPage> createState() => _AssetPageState();
|
|
}
|
|
|
|
class _AssetPageState extends State<AssetPage> with AutomaticKeepAliveClientMixin {
|
|
StreamSubscription<AppEvent>? _eventSub;
|
|
|
|
@override
|
|
bool get wantKeepAlive => true;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
_loadData();
|
|
_listenEvents();
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_eventSub?.cancel();
|
|
super.dispose();
|
|
}
|
|
|
|
void _listenEvents() {
|
|
final eventBus = context.read<AppEventBus>();
|
|
_eventSub = eventBus.on(AppEventType.assetChanged, (_) {
|
|
if (mounted) {
|
|
context.read<AssetProvider>().refreshAll(force: true);
|
|
}
|
|
});
|
|
}
|
|
|
|
void _loadData() {
|
|
context.read<AssetProvider>().refreshAll(force: true);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
super.build(context);
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
return Scaffold(
|
|
backgroundColor: colorScheme.background,
|
|
body: Consumer<AssetProvider>(
|
|
builder: (context, provider, _) {
|
|
return RefreshIndicator(
|
|
onRefresh: () => provider.refreshAll(force: true),
|
|
color: colorScheme.primary,
|
|
backgroundColor: colorScheme.surfaceContainerHighest,
|
|
child: SingleChildScrollView(
|
|
physics: const AlwaysScrollableScrollPhysics(),
|
|
padding: const EdgeInsets.fromLTRB(AppSpacing.md, AppSpacing.md + 8, AppSpacing.md, 32),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Page title
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 16, bottom: 8),
|
|
child: Text(
|
|
'资产',
|
|
style: AppTextStyles.displaySmall(context),
|
|
),
|
|
),
|
|
const SizedBox(height: AppSpacing.sm),
|
|
// 资金账户 + 交易账户 左右并排
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: BalanceCard(
|
|
provider: provider,
|
|
label: '资金账户',
|
|
balance: provider.fundAccount?.balance ?? provider.overview?.fundBalance ?? '0.00',
|
|
),
|
|
),
|
|
const SizedBox(width: AppSpacing.sm),
|
|
Expanded(
|
|
child: BalanceCard(
|
|
provider: provider,
|
|
label: '交易账户',
|
|
balance: _calculateTradeTotal(provider),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: AppSpacing.md),
|
|
// Action buttons row — matching .pen pIpHe (gap 12)
|
|
ActionButtonsRow(
|
|
onDeposit: () => showDepositDialog(context),
|
|
onWithdraw: () => showWithdrawDialog(context, provider.fundAccount?.balance),
|
|
onTransfer: () => _navigateToTransfer(context),
|
|
),
|
|
const SizedBox(height: AppSpacing.md),
|
|
// Records link row — matching .pen fLHtq (cornerRadius lg, padding [14,16], stroke)
|
|
RecordsLinkRow(
|
|
onTap: () => Navigator.push(
|
|
context,
|
|
MaterialPageRoute(builder: (_) => const FundOrdersPage()),
|
|
),
|
|
),
|
|
const SizedBox(height: AppSpacing.md),
|
|
// Holdings section
|
|
HoldingsSection(holdings: provider.holdings),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
String _calculateTradeTotal(AssetProvider provider) {
|
|
double total = 0;
|
|
for (var h in provider.holdings) {
|
|
total += double.tryParse(h.currentValue?.toString() ?? '0') ?? 0;
|
|
}
|
|
return total.toStringAsFixed(2);
|
|
}
|
|
|
|
void _navigateToTransfer(BuildContext context) async {
|
|
final result = await Navigator.push<bool>(
|
|
context,
|
|
MaterialPageRoute(builder: (_) => const TransferPage()),
|
|
);
|
|
if (result == true && context.mounted) {
|
|
context.read<AssetProvider>().refreshAll(force: true);
|
|
}
|
|
}
|
|
}
|