feat(frontend): extend dark mode support to more pages

- Update asset_page.dart with full dark mode support
- Update c2c_market_page.dart with full dark mode support
- Update login_page.dart with full dark mode support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-19 19:21:40 -08:00
parent b0d1771b66
commit 61203d1baf
3 changed files with 207 additions and 165 deletions

View File

@ -6,6 +6,7 @@ import '../../../core/router/routes.dart';
import '../../../core/utils/format_utils.dart';
import '../../../core/network/price_websocket_service.dart';
import '../../../core/constants/app_constants.dart';
import '../../../core/constants/app_colors.dart';
import '../../../domain/entities/asset_display.dart';
import '../../providers/user_providers.dart';
import '../../providers/asset_providers.dart';
@ -193,7 +194,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
}
return Scaffold(
backgroundColor: const Color(0xFFF5F5F5),
backgroundColor: AppColors.backgroundOf(context),
body: SafeArea(
bottom: false,
child: LayoutBuilder(
@ -220,19 +221,19 @@ class _AssetPageState extends ConsumerState<AssetPage> {
children: [
const SizedBox(height: 8),
// -
_buildTotalAssetCard(asset, isLoading, _calculateTotalAssetValue(asset), _currentShareBalance, perSecondEarning),
_buildTotalAssetCard(context, asset, isLoading, _calculateTotalAssetValue(asset), _currentShareBalance, perSecondEarning),
const SizedBox(height: 24),
//
_buildQuickActions(context),
const SizedBox(height: 24),
// -
_buildAssetList(asset, isLoading, _currentShareBalance, perSecondEarning),
_buildAssetList(context, asset, isLoading, _currentShareBalance, perSecondEarning),
const SizedBox(height: 24),
//
_buildEarningsCard(asset, isLoading),
_buildEarningsCard(context, asset, isLoading),
const SizedBox(height: 24),
//
_buildAccountList(asset, isLoading),
_buildAccountList(context, asset, isLoading),
const SizedBox(height: 24),
],
),
@ -250,24 +251,25 @@ class _AssetPageState extends ConsumerState<AssetPage> {
Widget _buildAppBar(BuildContext context, user) {
return Container(
color: _bgGray.withOpacity(0.9),
color: AppColors.surfaceOf(context).withOpacity(0.9),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
child: const Center(
child: Center(
child: Text(
'我的资产',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF111827),
color: AppColors.textPrimaryOf(context),
),
),
),
);
}
Widget _buildTotalAssetCard(AssetDisplay? asset, bool isLoading, double totalAssetValue, double currentShareBalance, String perSecondEarning) {
Widget _buildTotalAssetCard(BuildContext context, AssetDisplay? asset, bool isLoading, double totalAssetValue, double currentShareBalance, String perSecondEarning) {
// 使 mining-service
final growthPerSecond = double.tryParse(perSecondEarning) ?? 0.0;
final isDark = AppColors.isDark(context);
// 使 +
final displayValue = asset != null && totalAssetValue > 0
@ -282,11 +284,11 @@ class _AssetPageState extends ConsumerState<AssetPage> {
return Container(
decoration: BoxDecoration(
color: Colors.white,
color: AppColors.cardOf(context),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
color: isDark ? Colors.black.withOpacity(0.3) : Colors.black.withOpacity(0.04),
blurRadius: 30,
offset: const Offset(0, 8),
),
@ -302,7 +304,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
width: 128,
height: 128,
decoration: BoxDecoration(
color: _serenade,
color: isDark ? _orange.withOpacity(0.1) : _serenade,
borderRadius: BorderRadius.circular(64),
),
),
@ -334,18 +336,18 @@ class _AssetPageState extends ConsumerState<AssetPage> {
//
Row(
children: [
const Text(
Text(
'总资产估值',
style: TextStyle(
fontSize: 14,
color: _grayText,
color: AppColors.textSecondaryOf(context),
),
),
const SizedBox(width: 8),
Icon(
Icons.visibility_outlined,
size: 14,
color: _grayText.withOpacity(0.5),
color: AppColors.textMutedOf(context),
),
],
),
@ -378,7 +380,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: _feta,
color: isDark ? _green.withOpacity(0.15) : _feta,
borderRadius: BorderRadius.circular(8),
),
child: Row(
@ -410,36 +412,45 @@ class _AssetPageState extends ConsumerState<AssetPage> {
}
Widget _buildQuickActions(BuildContext context) {
final isDark = AppColors.isDark(context);
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildQuickActionItem(
context,
Icons.add,
'接收',
_orange,
() => context.push(Routes.receiveShares),
isDark,
),
_buildQuickActionItem(
context,
Icons.remove,
'发送',
_orange,
() => context.push(Routes.sendShares),
isDark,
),
_buildQuickActionItem(
context,
Icons.people_outline,
'C2C',
_orange,
() => context.push(Routes.c2cMarket),
isDark,
),
],
);
}
Widget _buildQuickActionItem(
BuildContext context,
IconData icon,
String label,
Color color,
VoidCallback onTap,
bool isDark,
) {
return GestureDetector(
onTap: onTap,
@ -450,7 +461,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
width: 48,
height: 48,
decoration: BoxDecoration(
color: _serenade,
color: isDark ? _orange.withOpacity(0.15) : _serenade,
borderRadius: BorderRadius.circular(16),
),
child: Icon(icon, color: color, size: 24),
@ -458,10 +469,10 @@ class _AssetPageState extends ConsumerState<AssetPage> {
const SizedBox(height: 8),
Text(
label,
style: const TextStyle(
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: _riverBed,
color: AppColors.textSecondaryOf(context),
),
),
],
@ -469,7 +480,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
);
}
Widget _buildAssetList(AssetDisplay? asset, bool isLoading, double currentShareBalance, String perSecondEarning) {
Widget _buildAssetList(BuildContext context, AssetDisplay? asset, bool isLoading, double currentShareBalance, String perSecondEarning) {
// 使
final shareBalance = asset != null && currentShareBalance > 0
? currentShareBalance
@ -477,14 +488,16 @@ class _AssetPageState extends ConsumerState<AssetPage> {
final multiplier = double.tryParse(asset?.burnMultiplier ?? '0') ?? 0;
final multipliedAsset = shareBalance * multiplier;
final currentPrice = double.tryParse(asset?.currentPrice ?? '0') ?? 0;
final isDark = AppColors.isDark(context);
return Column(
children: [
// -
_buildAssetItem(
context: context,
icon: Icons.trending_up,
iconColor: _orange,
iconBgColor: _serenade,
iconBgColor: isDark ? _orange.withOpacity(0.15) : _serenade,
title: '积分股',
amount: asset != null ? shareBalance.toString() : null,
isLoading: isLoading,
@ -497,9 +510,10 @@ class _AssetPageState extends ConsumerState<AssetPage> {
const SizedBox(height: 16),
//
_buildAssetItem(
context: context,
icon: Icons.eco,
iconColor: _green,
iconBgColor: _feta,
iconBgColor: isDark ? _green.withOpacity(0.15) : _feta,
title: '积分值',
amount: asset?.cashBalance,
isLoading: isLoading,
@ -508,9 +522,10 @@ class _AssetPageState extends ConsumerState<AssetPage> {
const SizedBox(height: 16),
//
_buildAssetItem(
context: context,
icon: Icons.lock_outline,
iconColor: _orange,
iconBgColor: _serenade,
iconBgColor: isDark ? _orange.withOpacity(0.15) : _serenade,
title: '冻结积分股',
amount: asset?.frozenShares,
isLoading: isLoading,
@ -521,6 +536,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
}
Widget _buildAssetItem({
required BuildContext context,
required IconData icon,
required Color iconColor,
required Color iconBgColor,
@ -535,14 +551,15 @@ class _AssetPageState extends ConsumerState<AssetPage> {
Color? badgeBgColor,
String? subtitle,
}) {
final isDark = AppColors.isDark(context);
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
color: AppColors.cardOf(context),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: isDark ? Colors.black.withOpacity(0.2) : Colors.black.withOpacity(0.05),
blurRadius: 2,
offset: const Offset(0, 1),
),
@ -571,10 +588,10 @@ class _AssetPageState extends ConsumerState<AssetPage> {
children: [
Text(
title,
style: const TextStyle(
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xFF111827),
color: AppColors.textPrimaryOf(context),
),
),
if (badge != null) ...[
@ -582,7 +599,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: badgeBgColor ?? _scandal,
color: badgeBgColor ?? (isDark ? _green.withOpacity(0.2) : _scandal),
borderRadius: BorderRadius.circular(9999),
),
child: Text(
@ -603,10 +620,10 @@ class _AssetPageState extends ConsumerState<AssetPage> {
data: amount != null ? formatAmount(amount) : null,
isLoading: isLoading,
placeholder: '--',
style: const TextStyle(
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF111827),
color: AppColors.textPrimaryOf(context),
),
),
//
@ -615,9 +632,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
data: isLoading ? null : '$valueInCny',
isLoading: isLoading,
placeholder: '≈ ¥--',
style: const TextStyle(
style: TextStyle(
fontSize: 12,
color: Color(0xFF9CA3AF),
color: AppColors.textMutedOf(context),
),
),
//
@ -626,9 +643,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
padding: const EdgeInsets.only(top: 3),
child: Text(
subtitle,
style: const TextStyle(
style: TextStyle(
fontSize: 12,
color: Color(0xFF9CA3AF),
color: AppColors.textMutedOf(context),
),
),
),
@ -642,7 +659,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: _serenade,
color: isDark ? _orange.withOpacity(0.15) : _serenade,
borderRadius: BorderRadius.circular(12),
),
child: DataText(
@ -679,21 +696,22 @@ class _AssetPageState extends ConsumerState<AssetPage> {
),
),
//
Icon(Icons.chevron_right, size: 14, color: _grayText.withOpacity(0.5)),
Icon(Icons.chevron_right, size: 14, color: AppColors.textMutedOf(context)),
],
),
);
}
Widget _buildEarningsCard(AssetDisplay? asset, bool isLoading) {
Widget _buildEarningsCard(BuildContext context, AssetDisplay? asset, bool isLoading) {
final isDark = AppColors.isDark(context);
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
color: AppColors.cardOf(context),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: isDark ? Colors.black.withOpacity(0.2) : Colors.black.withOpacity(0.05),
blurRadius: 2,
offset: const Offset(0, 1),
),
@ -713,12 +731,12 @@ class _AssetPageState extends ConsumerState<AssetPage> {
),
),
const SizedBox(width: 8),
const Text(
Text(
'交易统计',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: _darkText,
color: AppColors.textPrimaryOf(context),
),
),
],
@ -728,6 +746,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
Row(
children: [
_buildEarningsItem(
context,
'累计买入',
asset != null ? formatCompact(asset.totalBought) : null,
_orange,
@ -736,9 +755,10 @@ class _AssetPageState extends ConsumerState<AssetPage> {
Container(
width: 1,
height: 40,
color: _serenade,
color: isDark ? AppColors.borderOf(context) : _serenade,
),
_buildEarningsItem(
context,
'累计卖出',
asset != null ? formatCompact(asset.totalSold) : null,
_green,
@ -747,12 +767,13 @@ class _AssetPageState extends ConsumerState<AssetPage> {
Container(
width: 1,
height: 40,
color: _serenade,
color: isDark ? AppColors.borderOf(context) : _serenade,
),
_buildEarningsItem(
context,
'销毁倍数',
asset != null ? '${formatDecimal(asset.burnMultiplier, 4)}x' : null,
const Color(0xFF9CA3AF),
AppColors.textMutedOf(context),
isLoading,
),
],
@ -762,15 +783,15 @@ class _AssetPageState extends ConsumerState<AssetPage> {
);
}
Widget _buildEarningsItem(String label, String? value, Color valueColor, bool isLoading) {
Widget _buildEarningsItem(BuildContext context, String label, String? value, Color valueColor, bool isLoading) {
return Expanded(
child: Column(
children: [
Text(
label,
style: const TextStyle(
style: TextStyle(
fontSize: 12,
color: _grayText,
color: AppColors.textSecondaryOf(context),
),
textAlign: TextAlign.center,
),
@ -791,11 +812,13 @@ class _AssetPageState extends ConsumerState<AssetPage> {
);
}
Widget _buildAccountList(AssetDisplay? asset, bool isLoading) {
Widget _buildAccountList(BuildContext context, AssetDisplay? asset, bool isLoading) {
final isDark = AppColors.isDark(context);
return Column(
children: [
//
_buildAccountItem(
context: context,
icon: Icons.account_balance_wallet,
iconColor: _orange,
title: '可用积分值',
@ -804,11 +827,12 @@ class _AssetPageState extends ConsumerState<AssetPage> {
unit: '积分值',
status: '可交易',
statusColor: _green,
statusBgColor: _feta,
statusBgColor: isDark ? _green.withOpacity(0.15) : _feta,
),
const SizedBox(height: 16),
//
_buildAccountItem(
context: context,
icon: Icons.lock_outline,
iconColor: _orange,
title: '冻结积分值',
@ -816,8 +840,8 @@ class _AssetPageState extends ConsumerState<AssetPage> {
isLoading: isLoading,
unit: '积分值',
status: '挂单中',
statusColor: const Color(0xFF9CA3AF),
statusBgColor: Colors.white,
statusColor: AppColors.textMutedOf(context),
statusBgColor: AppColors.cardOf(context),
statusBorder: true,
),
],
@ -825,6 +849,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
}
Widget _buildAccountItem({
required BuildContext context,
required IconData icon,
required Color iconColor,
required String title,
@ -836,14 +861,15 @@ class _AssetPageState extends ConsumerState<AssetPage> {
required Color statusBgColor,
bool statusBorder = false,
}) {
final isDark = AppColors.isDark(context);
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
color: AppColors.cardOf(context),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: isDark ? Colors.black.withOpacity(0.2) : Colors.black.withOpacity(0.05),
blurRadius: 2,
offset: const Offset(0, 1),
),
@ -856,7 +882,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
width: 36,
height: 36,
decoration: BoxDecoration(
color: _serenade,
color: isDark ? _orange.withOpacity(0.15) : _serenade,
borderRadius: BorderRadius.circular(18),
),
child: Icon(icon, color: iconColor, size: 20),
@ -869,10 +895,10 @@ class _AssetPageState extends ConsumerState<AssetPage> {
children: [
Text(
title,
style: const TextStyle(
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF111827),
color: AppColors.textPrimaryOf(context),
),
),
const SizedBox(height: 2),
@ -890,9 +916,9 @@ class _AssetPageState extends ConsumerState<AssetPage> {
),
Text(
' $unit',
style: const TextStyle(
style: TextStyle(
fontSize: 12,
color: Color(0xFF9CA3AF),
color: AppColors.textMutedOf(context),
),
),
],
@ -906,7 +932,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
decoration: BoxDecoration(
color: statusBgColor,
borderRadius: BorderRadius.circular(9999),
border: statusBorder ? Border.all(color: const Color(0xFFE5E7EB)) : null,
border: statusBorder ? Border.all(color: AppColors.borderOf(context)) : null,
),
child: Text(
status,
@ -918,7 +944,7 @@ class _AssetPageState extends ConsumerState<AssetPage> {
),
const SizedBox(width: 8),
//
Icon(Icons.chevron_right, size: 14, color: _grayText.withOpacity(0.5)),
Icon(Icons.chevron_right, size: 14, color: AppColors.textMutedOf(context)),
],
),
);

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../../core/router/routes.dart';
import '../../../core/constants/app_colors.dart';
import '../../providers/user_providers.dart';
class LoginPage extends ConsumerStatefulWidget {
@ -106,9 +107,10 @@ class _LoginPageState extends ConsumerState<LoginPage> {
@override
Widget build(BuildContext context) {
final userState = ref.watch(userNotifierProvider);
final isDark = AppColors.isDark(context);
return Scaffold(
backgroundColor: Colors.white,
backgroundColor: AppColors.backgroundOf(context),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
@ -125,7 +127,7 @@ class _LoginPageState extends ConsumerState<LoginPage> {
width: 80,
height: 80,
decoration: BoxDecoration(
color: _orange.withOpacity(0.1),
color: _orange.withOpacity(isDark ? 0.2 : 0.1),
borderRadius: BorderRadius.circular(20),
),
child: const Icon(
@ -138,23 +140,23 @@ class _LoginPageState extends ConsumerState<LoginPage> {
const SizedBox(height: 24),
const Text(
Text(
'欢迎回来',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: _darkText,
color: AppColors.textPrimaryOf(context),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
const Text(
Text(
'登录您的股行账户',
style: TextStyle(
fontSize: 16,
color: _grayText,
color: AppColors.textSecondaryOf(context),
),
textAlign: TextAlign.center,
),
@ -163,9 +165,9 @@ class _LoginPageState extends ConsumerState<LoginPage> {
//
Container(
decoration: const BoxDecoration(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: _borderGray, width: 1),
bottom: BorderSide(color: AppColors.borderOf(context), width: 1),
),
),
child: Row(
@ -189,7 +191,7 @@ class _LoginPageState extends ConsumerState<LoginPage> {
style: TextStyle(
fontSize: 16,
fontWeight: _isPasswordLogin ? FontWeight.bold : FontWeight.normal,
color: _isPasswordLogin ? _orange : _lightGray,
color: _isPasswordLogin ? _orange : AppColors.textMutedOf(context),
),
),
),
@ -214,7 +216,7 @@ class _LoginPageState extends ConsumerState<LoginPage> {
style: TextStyle(
fontSize: 16,
fontWeight: !_isPasswordLogin ? FontWeight.bold : FontWeight.normal,
color: !_isPasswordLogin ? _orange : _lightGray,
color: !_isPasswordLogin ? _orange : AppColors.textMutedOf(context),
),
),
),
@ -230,25 +232,25 @@ class _LoginPageState extends ConsumerState<LoginPage> {
TextFormField(
controller: _phoneController,
keyboardType: TextInputType.phone,
style: const TextStyle(color: _darkText),
style: TextStyle(color: AppColors.textPrimaryOf(context)),
decoration: InputDecoration(
labelText: '手机号',
labelStyle: const TextStyle(color: _grayText),
prefixIcon: const Icon(Icons.phone_outlined, color: _grayText),
labelStyle: TextStyle(color: AppColors.textSecondaryOf(context)),
prefixIcon: Icon(Icons.phone_outlined, color: AppColors.textSecondaryOf(context)),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: _borderGray),
borderSide: BorderSide(color: AppColors.borderOf(context)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: _borderGray),
borderSide: BorderSide(color: AppColors.borderOf(context)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: _orange, width: 2),
),
filled: true,
fillColor: Colors.white,
fillColor: AppColors.cardOf(context),
),
validator: (value) {
if (value == null || value.isEmpty) {
@ -268,32 +270,32 @@ class _LoginPageState extends ConsumerState<LoginPage> {
TextFormField(
controller: _passwordController,
obscureText: _obscurePassword,
style: const TextStyle(color: _darkText),
style: TextStyle(color: AppColors.textPrimaryOf(context)),
decoration: InputDecoration(
labelText: '密码',
labelStyle: const TextStyle(color: _grayText),
prefixIcon: const Icon(Icons.lock_outline, color: _grayText),
labelStyle: TextStyle(color: AppColors.textSecondaryOf(context)),
prefixIcon: Icon(Icons.lock_outline, color: AppColors.textSecondaryOf(context)),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword ? Icons.visibility_off_outlined : Icons.visibility_outlined,
color: _grayText,
color: AppColors.textSecondaryOf(context),
),
onPressed: () => setState(() => _obscurePassword = !_obscurePassword),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: _borderGray),
borderSide: BorderSide(color: AppColors.borderOf(context)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: _borderGray),
borderSide: BorderSide(color: AppColors.borderOf(context)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: _orange, width: 2),
),
filled: true,
fillColor: Colors.white,
fillColor: AppColors.cardOf(context),
),
validator: (value) {
if (value == null || value.isEmpty) {
@ -310,26 +312,26 @@ class _LoginPageState extends ConsumerState<LoginPage> {
controller: _smsCodeController,
keyboardType: TextInputType.number,
maxLength: 6,
style: const TextStyle(color: _darkText),
style: TextStyle(color: AppColors.textPrimaryOf(context)),
decoration: InputDecoration(
labelText: '验证码',
labelStyle: const TextStyle(color: _grayText),
prefixIcon: const Icon(Icons.sms_outlined, color: _grayText),
labelStyle: TextStyle(color: AppColors.textSecondaryOf(context)),
prefixIcon: Icon(Icons.sms_outlined, color: AppColors.textSecondaryOf(context)),
counterText: '',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: _borderGray),
borderSide: BorderSide(color: AppColors.borderOf(context)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: _borderGray),
borderSide: BorderSide(color: AppColors.borderOf(context)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: _orange, width: 2),
),
filled: true,
fillColor: Colors.white,
fillColor: AppColors.cardOf(context),
),
validator: (value) {
if (value == null || value.isEmpty) {
@ -349,8 +351,8 @@ class _LoginPageState extends ConsumerState<LoginPage> {
child: ElevatedButton(
onPressed: _countDown > 0 ? null : _sendSmsCode,
style: ElevatedButton.styleFrom(
backgroundColor: _countDown > 0 ? _bgGray : _orange.withOpacity(0.1),
foregroundColor: _countDown > 0 ? _lightGray : _orange,
backgroundColor: _countDown > 0 ? AppColors.backgroundOf(context) : _orange.withOpacity(isDark ? 0.2 : 0.1),
foregroundColor: _countDown > 0 ? AppColors.textMutedOf(context) : _orange,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
@ -408,9 +410,9 @@ class _LoginPageState extends ConsumerState<LoginPage> {
alignment: Alignment.centerRight,
child: TextButton(
onPressed: () => context.push(Routes.forgotPassword),
child: const Text(
child: Text(
'忘记密码?',
style: TextStyle(color: _grayText),
style: TextStyle(color: AppColors.textSecondaryOf(context)),
),
),
),
@ -421,9 +423,9 @@ class _LoginPageState extends ConsumerState<LoginPage> {
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
Text(
'还没有账号?',
style: TextStyle(color: _grayText),
style: TextStyle(color: AppColors.textSecondaryOf(context)),
),
TextButton(
onPressed: () => context.push(Routes.register),

View File

@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../../core/router/routes.dart';
import '../../../core/utils/format_utils.dart';
import '../../../core/constants/app_colors.dart';
import '../../../data/models/c2c_order_model.dart';
import '../../providers/c2c_providers.dart';
import '../../providers/user_providers.dart';
@ -52,20 +53,20 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
final asset = assetAsync.valueOrNull;
return Scaffold(
backgroundColor: _bgGray,
backgroundColor: AppColors.backgroundOf(context),
appBar: AppBar(
backgroundColor: Colors.white,
backgroundColor: AppColors.cardOf(context),
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: _darkText),
icon: Icon(Icons.arrow_back, color: AppColors.textPrimaryOf(context)),
onPressed: () => context.pop(),
),
title: const Text(
title: Text(
'C2C交易',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: _darkText,
color: AppColors.textPrimaryOf(context),
),
),
centerTitle: true,
@ -78,7 +79,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
bottom: TabBar(
controller: _tabController,
labelColor: _orange,
unselectedLabelColor: _grayText,
unselectedLabelColor: AppColors.textSecondaryOf(context),
indicatorColor: _orange,
indicatorWeight: 3,
tabs: const [
@ -91,15 +92,15 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
body: Column(
children: [
//
_buildAssetOverview(asset),
_buildAssetOverview(context, asset),
//
Expanded(
child: TabBarView(
controller: _tabController,
children: [
_buildOrderList('SELL'), //
_buildOrderList('BUY'), //
_buildMyOrderList(),
_buildOrderList(context, 'SELL'), //
_buildOrderList(context, 'BUY'), //
_buildMyOrderList(context),
],
),
),
@ -108,7 +109,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
);
}
Widget _buildAssetOverview(asset) {
Widget _buildAssetOverview(BuildContext context, asset) {
final availableShares = asset?.availableShares ?? '0';
final availableCash = asset?.availableCash ?? '0';
@ -116,7 +117,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
color: AppColors.cardOf(context),
borderRadius: BorderRadius.circular(12),
),
child: Row(
@ -125,9 +126,9 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
Text(
'可用积分股',
style: TextStyle(fontSize: 12, color: _grayText),
style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context)),
),
const SizedBox(height: 4),
Text(
@ -141,16 +142,16 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
],
),
),
Container(width: 1, height: 40, color: _bgGray),
Container(width: 1, height: 40, color: AppColors.borderOf(context)),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
Text(
'可用积分值',
style: TextStyle(fontSize: 12, color: _grayText),
style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context)),
),
const SizedBox(height: 4),
Text(
@ -170,7 +171,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
);
}
Widget _buildOrderList(String type) {
Widget _buildOrderList(BuildContext context, String type) {
final ordersAsync = ref.watch(c2cOrdersProvider(type));
return ordersAsync.when(
@ -181,9 +182,9 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error_outline, size: 48, color: _grayText),
Icon(Icons.error_outline, size: 48, color: AppColors.textMutedOf(context)),
const SizedBox(height: 8),
Text('加载失败', style: TextStyle(color: _grayText)),
Text('加载失败', style: TextStyle(color: AppColors.textSecondaryOf(context))),
const SizedBox(height: 8),
ElevatedButton(
onPressed: () => ref.invalidate(c2cOrdersProvider(type)),
@ -199,16 +200,16 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.inbox_outlined, size: 64, color: _grayText.withOpacity(0.5)),
Icon(Icons.inbox_outlined, size: 64, color: AppColors.textMutedOf(context)),
const SizedBox(height: 16),
Text(
type == 'SELL' ? '暂无出售广告' : '暂无购买广告',
style: TextStyle(fontSize: 16, color: _grayText),
style: TextStyle(fontSize: 16, color: AppColors.textSecondaryOf(context)),
),
const SizedBox(height: 8),
Text(
'点击右上角发布广告',
style: TextStyle(fontSize: 14, color: _grayText.withOpacity(0.7)),
style: TextStyle(fontSize: 14, color: AppColors.textMutedOf(context)),
),
],
),
@ -224,7 +225,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
itemCount: orders.length,
itemBuilder: (context, index) {
final order = orders[index];
return _buildOrderCard(order, type == 'SELL');
return _buildOrderCard(context, order, type == 'SELL');
},
),
);
@ -232,7 +233,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
);
}
Widget _buildMyOrderList() {
Widget _buildMyOrderList(BuildContext context) {
final ordersAsync = ref.watch(myC2cOrdersProvider);
return ordersAsync.when(
@ -243,9 +244,9 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error_outline, size: 48, color: _grayText),
Icon(Icons.error_outline, size: 48, color: AppColors.textMutedOf(context)),
const SizedBox(height: 8),
Text('加载失败', style: TextStyle(color: _grayText)),
Text('加载失败', style: TextStyle(color: AppColors.textSecondaryOf(context))),
],
),
),
@ -256,11 +257,11 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.history, size: 64, color: _grayText.withOpacity(0.5)),
Icon(Icons.history, size: 64, color: AppColors.textMutedOf(context)),
const SizedBox(height: 16),
Text(
'暂无订单记录',
style: TextStyle(fontSize: 16, color: _grayText),
style: TextStyle(fontSize: 16, color: AppColors.textSecondaryOf(context)),
),
],
),
@ -276,7 +277,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
itemCount: orders.length,
itemBuilder: (context, index) {
final order = orders[index];
return _buildMyOrderCard(order);
return _buildMyOrderCard(context, order);
},
),
);
@ -284,9 +285,10 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
);
}
Widget _buildOrderCard(C2cOrderModel order, bool isBuyAction) {
Widget _buildOrderCard(BuildContext context, C2cOrderModel order, bool isBuyAction) {
final user = ref.read(userNotifierProvider);
final isMyOrder = order.makerAccountSequence == user.accountSequence;
final isDark = AppColors.isDark(context);
return GestureDetector(
onTap: isMyOrder ? null : () => _showTakeOrderDialog(order, isBuyAction),
@ -294,7 +296,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
color: AppColors.cardOf(context),
borderRadius: BorderRadius.circular(12),
),
child: Column(
@ -308,7 +310,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
height: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: (isBuyAction ? _green : _red).withOpacity(0.1),
color: (isBuyAction ? _green : _red).withOpacity(isDark ? 0.2 : 0.1),
),
child: Center(
child: Text(
@ -328,10 +330,10 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
children: [
Text(
order.makerNickname ?? _maskPhone(order.makerPhone ?? ''),
style: const TextStyle(
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: _darkText,
color: AppColors.textPrimaryOf(context),
),
),
if (isMyOrder)
@ -345,7 +347,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: (isBuyAction ? _green : _red).withOpacity(0.1),
color: (isBuyAction ? _green : _red).withOpacity(isDark ? 0.2 : 0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
@ -367,14 +369,14 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('单价', style: TextStyle(fontSize: 12, color: _grayText)),
Text('单价', style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context))),
const SizedBox(height: 4),
Text(
'${formatPrice(order.price)} 积分值',
style: const TextStyle(
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _darkText,
color: AppColors.textPrimaryOf(context),
),
),
],
@ -384,14 +386,14 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('数量', style: TextStyle(fontSize: 12, color: _grayText)),
Text('数量', style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context))),
const SizedBox(height: 4),
Text(
'${formatAmount(order.quantity)} 积分股',
style: const TextStyle(
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _darkText,
color: AppColors.textPrimaryOf(context),
),
),
],
@ -404,13 +406,13 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: _bgGray,
color: isDark ? AppColors.backgroundOf(context) : _bgGray,
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('总金额', style: TextStyle(fontSize: 12, color: _grayText)),
Text('总金额', style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context))),
Text(
'${formatAmount(order.totalAmount)} 积分值',
style: const TextStyle(
@ -426,7 +428,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
const SizedBox(height: 8),
Text(
'备注: ${order.remark}',
style: TextStyle(fontSize: 12, color: _grayText),
style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context)),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
@ -437,14 +439,15 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
);
}
Widget _buildMyOrderCard(C2cOrderModel order) {
Widget _buildMyOrderCard(BuildContext context, C2cOrderModel order) {
final isDark = AppColors.isDark(context);
return GestureDetector(
onTap: () => context.push(Routes.c2cOrderDetail, extra: order.orderNo),
child: Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
color: AppColors.cardOf(context),
borderRadius: BorderRadius.circular(12),
),
child: Column(
@ -459,7 +462,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: (order.isBuy ? _green : _red).withOpacity(0.1),
color: (order.isBuy ? _green : _red).withOpacity(isDark ? 0.2 : 0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
@ -474,11 +477,11 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
const SizedBox(width: 8),
Text(
order.orderNo,
style: const TextStyle(fontSize: 12, color: _grayText),
style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context)),
),
],
),
_buildStatusBadge(order.status),
_buildStatusBadge(context, order.status),
],
),
const SizedBox(height: 12),
@ -488,10 +491,10 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
Expanded(
child: Text(
'${formatAmount(order.quantity)} 积分股',
style: const TextStyle(
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _darkText,
color: AppColors.textPrimaryOf(context),
),
),
),
@ -509,7 +512,7 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
//
Text(
'创建于 ${_formatDateTime(order.createdAt)}',
style: const TextStyle(fontSize: 12, color: _grayText),
style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(context)),
),
],
),
@ -517,39 +520,40 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
);
}
Widget _buildStatusBadge(C2cOrderStatus status) {
Widget _buildStatusBadge(BuildContext context, C2cOrderStatus status) {
final isDark = AppColors.isDark(context);
Color bgColor;
Color textColor;
String text;
switch (status) {
case C2cOrderStatus.pending:
bgColor = _orange.withOpacity(0.1);
bgColor = _orange.withOpacity(isDark ? 0.2 : 0.1);
textColor = _orange;
text = '待接单';
break;
case C2cOrderStatus.matched:
bgColor = Colors.blue.withOpacity(0.1);
bgColor = Colors.blue.withOpacity(isDark ? 0.2 : 0.1);
textColor = Colors.blue;
text = '待付款';
break;
case C2cOrderStatus.paid:
bgColor = Colors.purple.withOpacity(0.1);
bgColor = Colors.purple.withOpacity(isDark ? 0.2 : 0.1);
textColor = Colors.purple;
text = '待确认';
break;
case C2cOrderStatus.completed:
bgColor = _green.withOpacity(0.1);
bgColor = _green.withOpacity(isDark ? 0.2 : 0.1);
textColor = _green;
text = '已完成';
break;
case C2cOrderStatus.cancelled:
bgColor = _grayText.withOpacity(0.1);
textColor = _grayText;
bgColor = AppColors.textMutedOf(context).withOpacity(isDark ? 0.2 : 0.1);
textColor = AppColors.textMutedOf(context);
text = '已取消';
break;
case C2cOrderStatus.expired:
bgColor = _red.withOpacity(0.1);
bgColor = _red.withOpacity(isDark ? 0.2 : 0.1);
textColor = _red;
text = '已过期';
break;
@ -584,37 +588,47 @@ class _C2cMarketPageState extends ConsumerState<C2cMarketPage>
void _showTakeOrderDialog(C2cOrderModel order, bool isBuyAction) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(isBuyAction ? '确认购买' : '确认出售'),
builder: (dialogContext) => AlertDialog(
backgroundColor: AppColors.cardOf(dialogContext),
title: Text(
isBuyAction ? '确认购买' : '确认出售',
style: TextStyle(color: AppColors.textPrimaryOf(dialogContext)),
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('数量: ${formatAmount(order.quantity)} 积分股'),
Text(
'数量: ${formatAmount(order.quantity)} 积分股',
style: TextStyle(color: AppColors.textPrimaryOf(dialogContext)),
),
const SizedBox(height: 8),
Text('单价: ${formatPrice(order.price)} 积分值'),
Text(
'单价: ${formatPrice(order.price)} 积分值',
style: TextStyle(color: AppColors.textPrimaryOf(dialogContext)),
),
const SizedBox(height: 8),
Text(
'总金额: ${formatAmount(order.totalAmount)} 积分值',
style: const TextStyle(fontWeight: FontWeight.bold),
style: TextStyle(fontWeight: FontWeight.bold, color: AppColors.textPrimaryOf(dialogContext)),
),
const SizedBox(height: 16),
Text(
isBuyAction
? '您将使用积分值购买对方的积分股'
: '您将出售积分股换取对方的积分值',
style: TextStyle(fontSize: 12, color: _grayText),
style: TextStyle(fontSize: 12, color: AppColors.textSecondaryOf(dialogContext)),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
onPressed: () => Navigator.pop(dialogContext),
child: Text('取消', style: TextStyle(color: AppColors.textSecondaryOf(dialogContext))),
),
TextButton(
onPressed: () async {
Navigator.pop(context);
Navigator.pop(dialogContext);
await _takeOrder(order);
},
child: Text(