style(transfer): 树转让 3 页面从暗夜主题改为 App 标准浅色棕金主题

问题:树转让的 transfer_list_page、transfer_initiate_page、
transfer_detail_page 三个页面使用了深色暗夜主题(#1A1A2E 背景 +
#16213E 卡片),与 App 其余 40+ 功能页面的浅色棕金主题不一致。

修改内容(3 个文件,纯样式重写,零业务逻辑变更):

1. 页面背景:#1A1A2E → 渐变 #FFF7E6 → #EAE0C8(与 planting_quantity_page 一致)
2. 卡片背景:#16213E → #99FFFFFF 半透明白 + boxShadow(与认种页一致)
3. AppBar:深色背景白字 → 透明背景 + 金色返回键(#D4AF37) + 棕色标题(#5D4037)
4. 正文文字:Colors.white/white70 → #5D4037 棕色 / #745D43 次级棕色
5. 输入框:#16213E 填充 → #99FFFFFF 填充 + #EAE0C8 边框
6. 按钮:ElevatedButton → GestureDetector+Container(高度 56,与全局一致)
7. 分割线:Colors.white24 → #EAE0C8
8. Tab 栏:暗色系 → 半透明白卡片容器 + 金色指示器
9. Saga 时间线未完成节点:white 20% → #EAE0C8 暖色
10. 对话框:系统默认 → #FFF7E6 背景 + 棕色文字

样式参考基准:planting_quantity_page.dart(现有认种数量页)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-23 20:25:25 -08:00
parent a11e4d0261
commit e690a55c8e
3 changed files with 605 additions and 229 deletions

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../../../core/di/injection_container.dart'; import '../../../../core/di/injection_container.dart';
import '../../../../core/services/transfer_service.dart'; import '../../../../core/services/transfer_service.dart';
@ -98,16 +99,36 @@ class _TransferDetailPageState extends ConsumerState<TransferDetailPage> {
builder: (ctx) { builder: (ctx) {
String text = ''; String text = '';
return AlertDialog( return AlertDialog(
title: const Text('取消转让'), backgroundColor: const Color(0xFFFFF7E6),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
title: const Text(
'取消转让',
style: TextStyle(color: Color(0xFF5D4037), fontWeight: FontWeight.w700),
),
content: TextField( content: TextField(
onChanged: (v) => text = v, onChanged: (v) => text = v,
decoration: const InputDecoration(hintText: '取消原因(可选)'), style: const TextStyle(color: Color(0xFF5D4037)),
decoration: InputDecoration(
hintText: '取消原因(可选)',
hintStyle: const TextStyle(color: Color(0xFF745D43)),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: Color(0xFFEAE0C8)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: Color(0xFFD4AF37)),
),
),
), ),
actions: [ actions: [
TextButton(onPressed: () => Navigator.pop(ctx), child: const Text('返回')), TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('返回', style: TextStyle(color: Color(0xFF745D43))),
),
TextButton( TextButton(
onPressed: () => Navigator.pop(ctx, text.isEmpty ? '用户主动取消' : text), onPressed: () => Navigator.pop(ctx, text.isEmpty ? '用户主动取消' : text),
child: const Text('确认取消', style: TextStyle(color: Colors.red)), child: const Text('确认取消', style: TextStyle(color: Color(0xFFE65100))),
), ),
], ],
); );
@ -135,16 +156,78 @@ class _TransferDetailPageState extends ConsumerState<TransferDetailPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: const Color(0xFF1A1A2E), body: Container(
appBar: AppBar( width: double.infinity,
backgroundColor: const Color(0xFF1A1A2E), height: double.infinity,
foregroundColor: Colors.white, decoration: const BoxDecoration(
title: const Text( gradient: LinearGradient(
'转让详情', begin: Alignment.topCenter,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Colors.white), end: Alignment.bottomCenter,
colors: [
Color(0xFFFFF7E6),
Color(0xFFEAE0C8),
],
),
),
child: SafeArea(
child: Column(
children: [
_buildHeader(),
Expanded(child: _buildBody()),
],
),
), ),
), ),
body: _buildBody(), );
}
Widget _buildHeader() {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
GestureDetector(
onTap: () => context.pop(),
child: Container(
width: 32,
height: 32,
alignment: Alignment.center,
child: const Icon(
Icons.arrow_back_ios,
color: Color(0xFFD4AF37),
size: 20,
),
),
),
const SizedBox(width: 4),
GestureDetector(
onTap: () => context.pop(),
child: const Text(
'返回',
style: TextStyle(
fontSize: 16,
fontFamily: 'Inter',
height: 1.5,
color: Color(0xFFD4AF37),
),
),
),
const SizedBox(width: 42),
const Expanded(
child: Text(
'转让详情',
style: TextStyle(
fontSize: 18,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
letterSpacing: -0.27,
color: Color(0xFF5D4037),
),
),
),
],
),
); );
} }
@ -157,8 +240,21 @@ class _TransferDetailPageState extends ConsumerState<TransferDetailPage> {
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text('加载失败', style: TextStyle(color: Colors.white.withValues(alpha: 0.7))), const Icon(Icons.error_outline, color: Color(0xFFE65100), size: 32),
TextButton(onPressed: _loadData, child: const Text('重试', style: TextStyle(color: Color(0xFFD4AF37)))), const SizedBox(height: 8),
const Text('加载失败', style: TextStyle(color: Color(0xFF5D4037), fontSize: 16)),
const SizedBox(height: 8),
GestureDetector(
onTap: _loadData,
child: const Text(
'点击重试',
style: TextStyle(
color: Color(0xFFD4AF37),
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
], ],
), ),
); );
@ -191,9 +287,20 @@ class _TransferDetailPageState extends ConsumerState<TransferDetailPage> {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF16213E), color: const Color(0x99FFFFFF),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.white.withValues(alpha: 0.08)), boxShadow: const [
BoxShadow(
color: Color(0x1A000000),
blurRadius: 6,
offset: Offset(0, 4),
),
BoxShadow(
color: Color(0x1A000000),
blurRadius: 4,
offset: Offset(0, 2),
),
],
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -205,7 +312,7 @@ class _TransferDetailPageState extends ConsumerState<TransferDetailPage> {
Expanded( Expanded(
child: Text( child: Text(
order.transferOrderNo, order.transferOrderNo,
style: const TextStyle(fontSize: 14, color: Colors.white70, fontFamily: 'Inter'), style: const TextStyle(fontSize: 14, color: Color(0xFF745D43), fontFamily: 'Inter'),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
), ),
@ -236,16 +343,14 @@ class _TransferDetailPageState extends ConsumerState<TransferDetailPage> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(label, style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: 0.5))), Text(label, style: const TextStyle(fontSize: 13, color: Color(0xFF745D43))),
Text(value, style: const TextStyle(fontSize: 13, color: Colors.white, fontFamily: 'Inter')), Text(value, style: const TextStyle(fontSize: 13, color: Color(0xFF5D4037), fontFamily: 'Inter')),
], ],
), ),
); );
} }
Widget _buildSagaTimeline(TransferOrderDetail order) { Widget _buildSagaTimeline(TransferOrderDetail order) {
final currentStatusStr = order.status.name.toUpperCase();
// Map enum name to API status string
final statusApiMap = { final statusApiMap = {
'pending': 'PENDING', 'pending': 'PENDING',
'sellerConfirmed': 'SELLER_CONFIRMED', 'sellerConfirmed': 'SELLER_CONFIRMED',
@ -257,7 +362,7 @@ class _TransferDetailPageState extends ConsumerState<TransferDetailPage> {
'paymentSettled': 'PAYMENT_SETTLED', 'paymentSettled': 'PAYMENT_SETTLED',
'completed': 'COMPLETED', 'completed': 'COMPLETED',
}; };
final apiStatus = statusApiMap[order.status.name] ?? currentStatusStr; final apiStatus = statusApiMap[order.status.name] ?? order.status.name.toUpperCase();
final currentIdx = _sagaSteps.indexWhere((s) => s['status'] == apiStatus); final currentIdx = _sagaSteps.indexWhere((s) => s['status'] == apiStatus);
final isFailed = order.status == TransferOrderStatus.failed || final isFailed = order.status == TransferOrderStatus.failed ||
order.status == TransferOrderStatus.rollingBack || order.status == TransferOrderStatus.rollingBack ||
@ -266,16 +371,27 @@ class _TransferDetailPageState extends ConsumerState<TransferDetailPage> {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF16213E), color: const Color(0x99FFFFFF),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.white.withValues(alpha: 0.08)), boxShadow: const [
BoxShadow(
color: Color(0x1A000000),
blurRadius: 6,
offset: Offset(0, 4),
),
BoxShadow(
color: Color(0x1A000000),
blurRadius: 4,
offset: Offset(0, 2),
),
],
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( const Text(
'Saga 进度', 'Saga 进度',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white), style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF5D4037)),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
...List.generate(_sagaSteps.length, (idx) { ...List.generate(_sagaSteps.length, (idx) {
@ -284,8 +400,8 @@ class _TransferDetailPageState extends ConsumerState<TransferDetailPage> {
final isCurrent = idx == currentIdx && !order.isTerminal && !isFailed; final isCurrent = idx == currentIdx && !order.isTerminal && !isFailed;
final isFailedStep = isFailed && idx == currentIdx; final isFailedStep = isFailed && idx == currentIdx;
Color dotColor = Colors.white.withValues(alpha: 0.2); Color dotColor = const Color(0xFFEAE0C8);
Color dotBorder = Colors.white.withValues(alpha: 0.2); Color dotBorder = const Color(0xFFEAE0C8);
if (isCompleted) { if (isCompleted) {
dotColor = const Color(0xFF52C41A); dotColor = const Color(0xFF52C41A);
dotBorder = const Color(0xFF52C41A); dotBorder = const Color(0xFF52C41A);
@ -330,7 +446,7 @@ class _TransferDetailPageState extends ConsumerState<TransferDetailPage> {
height: 20, height: 20,
color: isCompleted color: isCompleted
? const Color(0xFF52C41A).withValues(alpha: 0.4) ? const Color(0xFF52C41A).withValues(alpha: 0.4)
: Colors.white.withValues(alpha: 0.1), : const Color(0xFFEAE0C8),
), ),
], ],
), ),
@ -345,14 +461,14 @@ class _TransferDetailPageState extends ConsumerState<TransferDetailPage> {
fontSize: 14, fontSize: 14,
fontWeight: (isCurrent || isCompleted) ? FontWeight.w600 : FontWeight.w400, fontWeight: (isCurrent || isCompleted) ? FontWeight.w600 : FontWeight.w400,
color: (isCurrent || isCompleted) color: (isCurrent || isCompleted)
? Colors.white ? const Color(0xFF5D4037)
: Colors.white.withValues(alpha: 0.4), : const Color(0xFF745D43),
), ),
), ),
if (logTime != null) if (logTime != null)
Text( Text(
_formatDateTime(logTime), _formatDateTime(logTime),
style: TextStyle(fontSize: 11, color: Colors.white.withValues(alpha: 0.3)), style: const TextStyle(fontSize: 11, color: Color(0xFF745D43)),
), ),
], ],
), ),
@ -370,16 +486,27 @@ class _TransferDetailPageState extends ConsumerState<TransferDetailPage> {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF16213E), color: const Color(0x99FFFFFF),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.white.withValues(alpha: 0.08)), boxShadow: const [
BoxShadow(
color: Color(0x1A000000),
blurRadius: 6,
offset: Offset(0, 4),
),
BoxShadow(
color: Color(0x1A000000),
blurRadius: 4,
offset: Offset(0, 2),
),
],
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( const Text(
'状态变更日志', '状态变更日志',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white), style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF5D4037)),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
...order.statusLogs.map((log) => Padding( ...order.statusLogs.map((log) => Padding(
@ -390,13 +517,13 @@ class _TransferDetailPageState extends ConsumerState<TransferDetailPage> {
flex: 2, flex: 2,
child: Text( child: Text(
'${log.fromStatus}${log.toStatus}', '${log.fromStatus}${log.toStatus}',
style: const TextStyle(fontSize: 12, color: Colors.white70, fontFamily: 'Inter'), style: const TextStyle(fontSize: 12, color: Color(0xFF745D43), fontFamily: 'Inter'),
), ),
), ),
Expanded( Expanded(
child: Text( child: Text(
_formatDateTime(log.createdAt), _formatDateTime(log.createdAt),
style: TextStyle(fontSize: 11, color: Colors.white.withValues(alpha: 0.4)), style: const TextStyle(fontSize: 11, color: Color(0xFF745D43)),
textAlign: TextAlign.right, textAlign: TextAlign.right,
), ),
), ),
@ -416,30 +543,64 @@ class _TransferDetailPageState extends ConsumerState<TransferDetailPage> {
// //
if (order.isPendingSellerConfirm) if (order.isPendingSellerConfirm)
Expanded( Expanded(
child: ElevatedButton( child: GestureDetector(
onPressed: _isActionLoading ? null : _handleSellerConfirm, onTap: _isActionLoading ? null : _handleSellerConfirm,
style: ElevatedButton.styleFrom( child: Container(
backgroundColor: const Color(0xFFD4AF37), height: 56,
foregroundColor: Colors.white, decoration: BoxDecoration(
padding: const EdgeInsets.symmetric(vertical: 14), color: _isActionLoading
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ? const Color(0xFFD4AF37).withValues(alpha: 0.5)
: const Color(0xFFD4AF37),
borderRadius: BorderRadius.circular(12),
boxShadow: !_isActionLoading
? const [
BoxShadow(
color: Color(0x1A000000),
blurRadius: 15,
offset: Offset(0, 10),
),
]
: null,
),
child: Center(
child: Text(
_isActionLoading ? '处理中...' : '确认转让',
style: const TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
),
), ),
child: Text(_isActionLoading ? '处理中...' : '确认转让'),
), ),
), ),
if (order.isPendingSellerConfirm) const SizedBox(width: 12), if (order.isPendingSellerConfirm) const SizedBox(width: 12),
// //
if (order.isCancellable) if (order.isCancellable)
Expanded( Expanded(
child: OutlinedButton( child: GestureDetector(
onPressed: _isActionLoading ? null : _handleCancel, onTap: _isActionLoading ? null : _handleCancel,
style: OutlinedButton.styleFrom( child: Container(
foregroundColor: Colors.red, height: 56,
side: const BorderSide(color: Colors.red), decoration: BoxDecoration(
padding: const EdgeInsets.symmetric(vertical: 14), color: const Color(0x99FFFFFF),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFE65100)),
),
child: Center(
child: const Text(
'取消转让',
style: TextStyle(
fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
color: Color(0xFFE65100),
),
),
),
), ),
child: const Text('取消转让'),
), ),
), ),
], ],

View File

@ -69,7 +69,12 @@ class _TransferInitiatePageState extends ConsumerState<TransferInitiatePage> {
final confirmed = await showDialog<bool>( final confirmed = await showDialog<bool>(
context: context, context: context,
builder: (ctx) => AlertDialog( builder: (ctx) => AlertDialog(
title: const Text('确认发起转让'), backgroundColor: const Color(0xFFFFF7E6),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
title: const Text(
'确认发起转让',
style: TextStyle(color: Color(0xFF5D4037), fontWeight: FontWeight.w700),
),
content: Text( content: Text(
'买方账号: $buyer\n' '买方账号: $buyer\n'
'转让棵数: $_treeCount\n' '转让棵数: $_treeCount\n'
@ -78,12 +83,20 @@ class _TransferInitiatePageState extends ConsumerState<TransferInitiatePage> {
'手续费: ${_feeAmount.toStringAsFixed(2)} USDT (${(_feeRate * 100).toStringAsFixed(0)}%)\n' '手续费: ${_feeAmount.toStringAsFixed(2)} USDT (${(_feeRate * 100).toStringAsFixed(0)}%)\n'
'您将收到: ${_sellerReceive.toStringAsFixed(2)} USDT\n\n' '您将收到: ${_sellerReceive.toStringAsFixed(2)} USDT\n\n'
'转让发起后需要卖方确认,确认后 Saga 流程将自动执行。', '转让发起后需要卖方确认,确认后 Saga 流程将自动执行。',
style: const TextStyle(color: Color(0xFF5D4037), fontSize: 14, height: 1.6),
), ),
actions: [ actions: [
TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('取消')), TextButton(
onPressed: () => Navigator.pop(ctx, false),
child: const Text('取消', style: TextStyle(color: Color(0xFF745D43))),
),
ElevatedButton( ElevatedButton(
onPressed: () => Navigator.pop(ctx, true), onPressed: () => Navigator.pop(ctx, true),
style: ElevatedButton.styleFrom(backgroundColor: const Color(0xFFD4AF37)), style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFD4AF37),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
child: const Text('确认发起'), child: const Text('确认发起'),
), ),
], ],
@ -128,140 +141,242 @@ class _TransferInitiatePageState extends ConsumerState<TransferInitiatePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: const Color(0xFF1A1A2E), body: Container(
appBar: AppBar( width: double.infinity,
backgroundColor: const Color(0xFF1A1A2E), height: double.infinity,
foregroundColor: Colors.white, decoration: const BoxDecoration(
title: const Text( gradient: LinearGradient(
'发起转让', begin: Alignment.topCenter,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Colors.white), end: Alignment.bottomCenter,
colors: [
Color(0xFFFFF7E6),
Color(0xFFEAE0C8),
],
),
),
child: SafeArea(
child: Column(
children: [
_buildHeader(),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFD4AF37).withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFD4AF37).withValues(alpha: 0.3)),
),
child: const Row(
children: [
Icon(Icons.info_outline, color: Color(0xFFD4AF37), size: 20),
SizedBox(width: 8),
Expanded(
child: Text(
'转让已认种的果树所有权。发起后需卖方确认,系统将自动完成资金冻结、所有权变更、算力调整等流程。',
style: TextStyle(fontSize: 13, color: Color(0xFF8B5A2B)),
),
),
],
),
),
const SizedBox(height: 24),
//
_buildLabel('买方账号'),
const SizedBox(height: 8),
_buildTextField(
controller: _buyerController,
hintText: '输入买方账号(如 D25121400002',
keyboardType: TextInputType.text,
),
const SizedBox(height: 20),
//
_buildLabel('转让棵数'),
const SizedBox(height: 8),
_buildTextField(
controller: _treeCountController,
hintText: '输入棵数',
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
onChanged: (_) => setState(() {}),
),
const SizedBox(height: 20),
//
_buildLabel('每棵售价 (USDT)'),
const SizedBox(height: 8),
_buildTextField(
controller: _priceController,
hintText: '输入每棵售价',
keyboardType: const TextInputType.numberWithOptions(decimal: true),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[\d.]'))],
onChanged: (_) => setState(() {}),
),
const SizedBox(height: 24),
//
if (_pricePerTree > 0) ...[
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0x99FFFFFF),
borderRadius: BorderRadius.circular(12),
boxShadow: const [
BoxShadow(
color: Color(0x1A000000),
blurRadius: 6,
offset: Offset(0, 4),
),
],
),
child: Column(
children: [
_feeRow('总价', '${_totalPrice.toStringAsFixed(0)} USDT'),
_feeRow('手续费 (${(_feeRate * 100).toStringAsFixed(0)}%)', '${_feeAmount.toStringAsFixed(2)} USDT'),
const Divider(color: Color(0xFFEAE0C8)),
_feeRow('您将收到', '${_sellerReceive.toStringAsFixed(2)} USDT',
valueColor: const Color(0xFFD4AF37)),
],
),
),
const SizedBox(height: 24),
],
//
if (_errorMessage != null) ...[
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFFFF3E0),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: const Color(0xFFE65100)),
),
child: Row(
children: [
const Icon(Icons.error_outline, color: Color(0xFFE65100), size: 16),
const SizedBox(width: 8),
Expanded(
child: Text(_errorMessage!, style: const TextStyle(color: Color(0xFFE65100), fontSize: 13)),
),
],
),
),
const SizedBox(height: 16),
],
//
GestureDetector(
onTap: _isSubmitting ? null : _handleSubmit,
child: Container(
width: double.infinity,
height: 56,
decoration: BoxDecoration(
color: _isSubmitting
? const Color(0xFFD4AF37).withValues(alpha: 0.5)
: const Color(0xFFD4AF37),
borderRadius: BorderRadius.circular(12),
boxShadow: !_isSubmitting
? const [
BoxShadow(
color: Color(0x1A000000),
blurRadius: 15,
offset: Offset(0, 10),
),
BoxShadow(
color: Color(0x1A000000),
blurRadius: 6,
offset: Offset(0, 4),
),
]
: null,
),
child: Center(
child: _isSubmitting
? const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Text(
'发起转让',
style: TextStyle(
fontSize: 18,
fontFamily: 'Inter',
fontWeight: FontWeight.w500,
height: 1.56,
color: Colors.white,
),
),
),
),
),
],
),
),
),
],
),
), ),
), ),
body: SingleChildScrollView( );
padding: const EdgeInsets.all(16), }
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, Widget _buildHeader() {
children: [ return Container(
// padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
Container( child: Row(
padding: const EdgeInsets.all(16), children: [
decoration: BoxDecoration( GestureDetector(
color: const Color(0xFFD4AF37).withValues(alpha: 0.1), onTap: () => context.pop(),
borderRadius: BorderRadius.circular(12), child: Container(
border: Border.all(color: const Color(0xFFD4AF37).withValues(alpha: 0.3)), width: 32,
), height: 32,
child: const Row( alignment: Alignment.center,
children: [ child: const Icon(
Icon(Icons.info_outline, color: Color(0xFFD4AF37), size: 20), Icons.arrow_back_ios,
SizedBox(width: 8), color: Color(0xFFD4AF37),
Expanded( size: 20,
child: Text(
'转让已认种的果树所有权。发起后需卖方确认,系统将自动完成资金冻结、所有权变更、算力调整等流程。',
style: TextStyle(fontSize: 13, color: Color(0xFFD4AF37)),
),
),
],
), ),
), ),
const SizedBox(height: 24), ),
const SizedBox(width: 4),
// GestureDetector(
_buildLabel('买方账号'), onTap: () => context.pop(),
const SizedBox(height: 8), child: const Text(
_buildTextField( '返回',
controller: _buyerController, style: TextStyle(
hintText: '输入买方账号(如 D25121400002', fontSize: 16,
keyboardType: TextInputType.text, fontFamily: 'Inter',
), height: 1.5,
const SizedBox(height: 20), color: Color(0xFFD4AF37),
//
_buildLabel('转让棵数'),
const SizedBox(height: 8),
_buildTextField(
controller: _treeCountController,
hintText: '输入棵数',
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
onChanged: (_) => setState(() {}),
),
const SizedBox(height: 20),
//
_buildLabel('每棵售价 (USDT)'),
const SizedBox(height: 8),
_buildTextField(
controller: _priceController,
hintText: '输入每棵售价',
keyboardType: const TextInputType.numberWithOptions(decimal: true),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'[\d.]'))],
onChanged: (_) => setState(() {}),
),
const SizedBox(height: 24),
//
if (_pricePerTree > 0) ...[
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF16213E),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.white.withValues(alpha: 0.08)),
),
child: Column(
children: [
_feeRow('总价', '${_totalPrice.toStringAsFixed(0)} USDT'),
_feeRow('手续费 (${(_feeRate * 100).toStringAsFixed(0)}%)', '${_feeAmount.toStringAsFixed(2)} USDT'),
const Divider(color: Colors.white24),
_feeRow('您将收到', '${_sellerReceive.toStringAsFixed(2)} USDT',
valueColor: const Color(0xFFD4AF37)),
],
),
),
const SizedBox(height: 24),
],
//
if (_errorMessage != null) ...[
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.red.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
const Icon(Icons.error_outline, color: Colors.red, size: 16),
const SizedBox(width: 8),
Expanded(
child: Text(_errorMessage!, style: const TextStyle(color: Colors.red, fontSize: 13)),
),
],
),
),
const SizedBox(height: 16),
],
//
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _isSubmitting ? null : _handleSubmit,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFD4AF37),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
disabledBackgroundColor: const Color(0xFFD4AF37).withValues(alpha: 0.4),
),
child: Text(
_isSubmitting ? '提交中...' : '发起转让',
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
), ),
), ),
], ),
), const SizedBox(width: 42),
const Expanded(
child: Text(
'发起转让',
style: TextStyle(
fontSize: 18,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
letterSpacing: -0.27,
color: Color(0xFF5D4037),
),
),
),
],
), ),
); );
} }
@ -270,9 +385,11 @@ class _TransferInitiatePageState extends ConsumerState<TransferInitiatePage> {
return Text( return Text(
text, text,
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 16,
fontFamily: 'Inter',
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Colors.white, height: 1.5,
color: Color(0xFF5D4037),
), ),
); );
} }
@ -289,23 +406,23 @@ class _TransferInitiatePageState extends ConsumerState<TransferInitiatePage> {
keyboardType: keyboardType, keyboardType: keyboardType,
inputFormatters: inputFormatters, inputFormatters: inputFormatters,
onChanged: onChanged, onChanged: onChanged,
style: const TextStyle(color: Colors.white, fontSize: 15, fontFamily: 'Inter'), style: const TextStyle(color: Color(0xFF5D4037), fontSize: 15, fontFamily: 'Inter'),
decoration: InputDecoration( decoration: InputDecoration(
hintText: hintText, hintText: hintText,
hintStyle: TextStyle(color: Colors.white.withValues(alpha: 0.3)), hintStyle: const TextStyle(color: Color(0xFF745D43)),
filled: true, filled: true,
fillColor: const Color(0xFF16213E), fillColor: const Color(0x99FFFFFF),
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.white.withValues(alpha: 0.1)), borderSide: const BorderSide(color: Color(0xFFEAE0C8)),
), ),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.white.withValues(alpha: 0.1)), borderSide: const BorderSide(color: Color(0xFFEAE0C8)),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFFD4AF37)), borderSide: const BorderSide(color: Color(0xFFD4AF37), width: 2),
), ),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
), ),
@ -318,13 +435,13 @@ class _TransferInitiatePageState extends ConsumerState<TransferInitiatePage> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(label, style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: 0.5))), Text(label, style: const TextStyle(fontSize: 13, color: Color(0xFF745D43))),
Text( Text(
value, value,
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: valueColor ?? Colors.white, color: valueColor ?? const Color(0xFF5D4037),
fontFamily: 'Inter', fontFamily: 'Inter',
), ),
), ),

View File

@ -84,31 +84,112 @@ class _TransferListPageState extends ConsumerState<TransferListPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: const Color(0xFF1A1A2E), body: Container(
appBar: AppBar( width: double.infinity,
backgroundColor: const Color(0xFF1A1A2E), height: double.infinity,
foregroundColor: Colors.white, decoration: const BoxDecoration(
title: const Text( gradient: LinearGradient(
'转让记录', begin: Alignment.topCenter,
style: TextStyle( end: Alignment.bottomCenter,
fontSize: 18, colors: [
fontWeight: FontWeight.w600, Color(0xFFFFF7E6),
color: Colors.white, Color(0xFFEAE0C8),
],
), ),
), ),
bottom: TabBar( child: SafeArea(
controller: _tabController, child: Column(
indicatorColor: const Color(0xFFD4AF37), children: [
labelColor: const Color(0xFFD4AF37), _buildHeader(),
unselectedLabelColor: Colors.white54, _buildTabBar(),
tabs: const [ Expanded(child: _buildBody()),
Tab(text: '全部'), ],
Tab(text: '转出'), ),
Tab(text: '转入'),
],
), ),
), ),
body: _buildBody(), );
}
Widget _buildHeader() {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
GestureDetector(
onTap: () => context.pop(),
child: Container(
width: 32,
height: 32,
alignment: Alignment.center,
child: const Icon(
Icons.arrow_back_ios,
color: Color(0xFFD4AF37),
size: 20,
),
),
),
const SizedBox(width: 4),
GestureDetector(
onTap: () => context.pop(),
child: const Text(
'返回',
style: TextStyle(
fontSize: 16,
fontFamily: 'Inter',
height: 1.5,
color: Color(0xFFD4AF37),
),
),
),
const SizedBox(width: 42),
const Expanded(
child: Text(
'转让记录',
style: TextStyle(
fontSize: 18,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.25,
letterSpacing: -0.27,
color: Color(0xFF5D4037),
),
),
),
],
),
);
}
Widget _buildTabBar() {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: const Color(0x99FFFFFF),
borderRadius: BorderRadius.circular(12),
),
child: TabBar(
controller: _tabController,
indicatorColor: const Color(0xFFD4AF37),
indicatorSize: TabBarIndicatorSize.label,
labelColor: const Color(0xFFD4AF37),
unselectedLabelColor: const Color(0xFF745D43),
labelStyle: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w600,
),
unselectedLabelStyle: const TextStyle(
fontSize: 14,
fontFamily: 'Inter',
fontWeight: FontWeight.w400,
),
dividerColor: Colors.transparent,
tabs: const [
Tab(text: '全部'),
Tab(text: '转出'),
Tab(text: '转入'),
],
),
); );
} }
@ -124,14 +205,23 @@ class _TransferListPageState extends ConsumerState<TransferListPage>
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Icon(Icons.error_outline, color: Color(0xFFE65100), size: 32),
const SizedBox(height: 8),
Text( Text(
'加载失败', '加载失败',
style: TextStyle(color: Colors.white.withValues(alpha: 0.7), fontSize: 16), style: const TextStyle(color: Color(0xFF5D4037), fontSize: 16),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
TextButton( GestureDetector(
onPressed: _loadData, onTap: _loadData,
child: const Text('重试', style: TextStyle(color: Color(0xFFD4AF37))), child: const Text(
'点击重试',
style: TextStyle(
color: Color(0xFFD4AF37),
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
), ),
], ],
), ),
@ -142,7 +232,7 @@ class _TransferListPageState extends ConsumerState<TransferListPage>
return Center( return Center(
child: Text( child: Text(
'暂无转让记录', '暂无转让记录',
style: TextStyle(color: Colors.white.withValues(alpha: 0.5), fontSize: 16), style: const TextStyle(color: Color(0xFF745D43), fontSize: 16),
), ),
); );
} }
@ -169,12 +259,20 @@ class _TransferListPageState extends ConsumerState<TransferListPage>
margin: const EdgeInsets.only(bottom: 12), margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF16213E), color: const Color(0x99FFFFFF),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all( boxShadow: const [
color: Colors.white.withValues(alpha: 0.08), BoxShadow(
width: 1, color: Color(0x1A000000),
), blurRadius: 6,
offset: Offset(0, 4),
),
BoxShadow(
color: Color(0x1A000000),
blurRadius: 4,
offset: Offset(0, 2),
),
],
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -189,7 +287,7 @@ class _TransferListPageState extends ConsumerState<TransferListPage>
style: const TextStyle( style: const TextStyle(
fontSize: 13, fontSize: 13,
fontFamily: 'Inter', fontFamily: 'Inter',
color: Colors.white70, color: Color(0xFF745D43),
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@ -217,7 +315,7 @@ class _TransferListPageState extends ConsumerState<TransferListPage>
children: [ children: [
Text( Text(
order.sellerAccountSequence, order.sellerAccountSequence,
style: const TextStyle(fontSize: 14, color: Colors.white, fontFamily: 'Inter'), style: const TextStyle(fontSize: 14, color: Color(0xFF5D4037), fontFamily: 'Inter'),
), ),
const Padding( const Padding(
padding: EdgeInsets.symmetric(horizontal: 8), padding: EdgeInsets.symmetric(horizontal: 8),
@ -225,7 +323,7 @@ class _TransferListPageState extends ConsumerState<TransferListPage>
), ),
Text( Text(
order.buyerAccountSequence, order.buyerAccountSequence,
style: const TextStyle(fontSize: 14, color: Colors.white, fontFamily: 'Inter'), style: const TextStyle(fontSize: 14, color: Color(0xFF5D4037), fontFamily: 'Inter'),
), ),
], ],
), ),
@ -236,7 +334,7 @@ class _TransferListPageState extends ConsumerState<TransferListPage>
children: [ children: [
Text( Text(
'${order.treeCount}', '${order.treeCount}',
style: const TextStyle(fontSize: 14, color: Colors.white70), style: const TextStyle(fontSize: 14, color: Color(0xFF745D43)),
), ),
Text( Text(
'${order.totalPrice.toStringAsFixed(0)} USDT', '${order.totalPrice.toStringAsFixed(0)} USDT',
@ -253,7 +351,7 @@ class _TransferListPageState extends ConsumerState<TransferListPage>
// //
Text( Text(
_formatDateTime(order.createdAt), _formatDateTime(order.createdAt),
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: 0.4)), style: const TextStyle(fontSize: 12, color: Color(0xFF745D43)),
), ),
], ],
), ),