feat(mobile-app): redesign wallet generation UI with overlay mask and countdown
- Replace full-page loading with overlay mask design - Add 100-second countdown timer with circular progress indicator - Show placeholder data (******) in mnemonic and address cards during loading - Display "正在进行钱包的安全计算" message with security icon - Implement error overlay with retry button - Disable copy buttons and action buttons during loading state - Maintain consistent UI layout between loading and ready states 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
37aab77e7a
commit
823fc5056e
|
|
@ -50,6 +50,10 @@ class _BackupMnemonicPageState extends ConsumerState<BackupMnemonicPage> {
|
|||
|
||||
// 轮询定时器
|
||||
Timer? _pollTimer;
|
||||
// 倒计时定时器
|
||||
Timer? _countdownTimer;
|
||||
// 倒计时剩余秒数 (100秒)
|
||||
int _countdownSeconds = 100;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -60,8 +64,9 @@ class _BackupMnemonicPageState extends ConsumerState<BackupMnemonicPage> {
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
debugPrint('[BackupMnemonicPage] dispose - 取消轮询定时器');
|
||||
debugPrint('[BackupMnemonicPage] dispose - 取消轮询定时器和倒计时');
|
||||
_pollTimer?.cancel();
|
||||
_countdownTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -106,6 +111,7 @@ class _BackupMnemonicPageState extends ConsumerState<BackupMnemonicPage> {
|
|||
_errorMessage = null;
|
||||
});
|
||||
_startPolling();
|
||||
_startCountdown();
|
||||
} else if (response.isFailed) {
|
||||
// 钱包生成失败
|
||||
debugPrint('[BackupMnemonicPage] _loadWalletInfo - 钱包生成失败');
|
||||
|
|
@ -132,6 +138,34 @@ class _BackupMnemonicPageState extends ConsumerState<BackupMnemonicPage> {
|
|||
return '${address.substring(0, 6)}...${address.substring(address.length - 4)}';
|
||||
}
|
||||
|
||||
/// 开始倒计时
|
||||
void _startCountdown() {
|
||||
_countdownTimer?.cancel();
|
||||
_countdownSeconds = 100;
|
||||
debugPrint('[BackupMnemonicPage] _startCountdown - 启动100秒倒计时');
|
||||
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
if (!mounted) {
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
if (_countdownSeconds > 0) {
|
||||
setState(() {
|
||||
_countdownSeconds--;
|
||||
});
|
||||
} else {
|
||||
timer.cancel();
|
||||
debugPrint('[BackupMnemonicPage] _startCountdown - 倒计时结束');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 停止倒计时
|
||||
void _stopCountdown() {
|
||||
_countdownTimer?.cancel();
|
||||
_countdownTimer = null;
|
||||
debugPrint('[BackupMnemonicPage] _stopCountdown - 停止倒计时');
|
||||
}
|
||||
|
||||
/// 开始轮询
|
||||
int _pollCount = 0;
|
||||
void _startPolling() {
|
||||
|
|
@ -162,6 +196,7 @@ class _BackupMnemonicPageState extends ConsumerState<BackupMnemonicPage> {
|
|||
if (response.isReady) {
|
||||
debugPrint('[BackupMnemonicPage] _startPolling - 钱包就绪! 停止轮询 (共轮询 $_pollCount 次)');
|
||||
timer.cancel();
|
||||
_stopCountdown();
|
||||
final wordCount = response.mnemonic?.split(' ').length ?? 0;
|
||||
debugPrint('[BackupMnemonicPage] _startPolling - 助记词数量: $wordCount');
|
||||
setState(() {
|
||||
|
|
@ -176,6 +211,7 @@ class _BackupMnemonicPageState extends ConsumerState<BackupMnemonicPage> {
|
|||
} else if (response.isFailed) {
|
||||
debugPrint('[BackupMnemonicPage] _startPolling - 钱包生成失败,停止轮询');
|
||||
timer.cancel();
|
||||
_stopCountdown();
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
_errorMessage = '钱包生成失败,请稍后重试';
|
||||
|
|
@ -367,15 +403,19 @@ ${DateTime.now().toString()}
|
|||
children: [
|
||||
// 顶部导航栏
|
||||
_buildAppBar(),
|
||||
// 内容区域
|
||||
// 内容区域 - 始终显示内容,加载时覆盖蒙版
|
||||
Expanded(
|
||||
child: _isLoading
|
||||
? _buildLoadingState()
|
||||
: _errorMessage != null
|
||||
? _buildErrorState()
|
||||
: _buildContent(),
|
||||
child: Stack(
|
||||
children: [
|
||||
// 底层:内容(始终显示,加载时显示占位数据)
|
||||
_buildContentWithPlaceholder(),
|
||||
// 顶层:加载蒙版或错误蒙版
|
||||
if (_isLoading) _buildLoadingOverlay(),
|
||||
if (_errorMessage != null) _buildErrorOverlay(),
|
||||
],
|
||||
),
|
||||
),
|
||||
// 底部按钮区域 (非加载状态时显示)
|
||||
// 底部按钮区域 (非加载状态且无错误时显示)
|
||||
if (!_isLoading && _errorMessage == null) _buildBottomButtons(),
|
||||
],
|
||||
),
|
||||
|
|
@ -384,37 +424,133 @@ ${DateTime.now().toString()}
|
|||
);
|
||||
}
|
||||
|
||||
/// 构建加载状态
|
||||
Widget _buildLoadingState() {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
/// 构建加载蒙版 - 覆盖助记词和地址区域,显示倒计时
|
||||
Widget _buildLoadingOverlay() {
|
||||
return Positioned.fill(
|
||||
child: Container(
|
||||
color: const Color(0xCC5D4037), // 半透明深棕色蒙版
|
||||
child: Center(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 32),
|
||||
padding: const EdgeInsets.all(32),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFFDF5),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x33000000),
|
||||
blurRadius: 20,
|
||||
offset: Offset(0, 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// 安全图标
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFF3D6),
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: const Color(0xFFD4A84B),
|
||||
width: 3,
|
||||
),
|
||||
),
|
||||
child: const Center(
|
||||
child: Icon(
|
||||
Icons.security,
|
||||
size: 40,
|
||||
color: Color(0xFFD4A84B),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
// 提示文字
|
||||
const Text(
|
||||
'正在进行钱包的安全计算',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Color(0xFF5D4037),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
'使用多方安全计算技术生成您的钱包',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF8B7355),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
// 倒计时圆环
|
||||
_buildCountdownCircle(),
|
||||
const SizedBox(height: 16),
|
||||
// 倒计时文字
|
||||
Text(
|
||||
'预计剩余 $_countdownSeconds 秒',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF8B7355),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建倒计时圆环
|
||||
Widget _buildCountdownCircle() {
|
||||
final progress = _countdownSeconds / 100.0;
|
||||
return SizedBox(
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 48,
|
||||
height: 48,
|
||||
// 背景圆环
|
||||
SizedBox(
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 3,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFFD4AF37)),
|
||||
value: 1.0,
|
||||
strokeWidth: 8,
|
||||
backgroundColor: const Color(0xFFE8E0D0),
|
||||
valueColor: const AlwaysStoppedAnimation<Color>(Color(0xFFE8E0D0)),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
const Text(
|
||||
'正在生成您的钱包...',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF5D4037),
|
||||
// 进度圆环
|
||||
SizedBox(
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: CircularProgressIndicator(
|
||||
value: progress,
|
||||
strokeWidth: 8,
|
||||
backgroundColor: Colors.transparent,
|
||||
valueColor: const AlwaysStoppedAnimation<Color>(Color(0xFFD4A84B)),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
'请稍候,这可能需要几秒钟',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
// 倒计时数字
|
||||
Text(
|
||||
'$_countdownSeconds',
|
||||
style: const TextStyle(
|
||||
fontSize: 32,
|
||||
fontFamily: 'Inter',
|
||||
color: Color(0xFF8B7355),
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Color(0xFFD4A84B),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -422,75 +558,135 @@ ${DateTime.now().toString()}
|
|||
);
|
||||
}
|
||||
|
||||
/// 构建错误状态
|
||||
Widget _buildErrorState() {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.error_outline,
|
||||
size: 64,
|
||||
color: Color(0xFFCC6B2C),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
_errorMessage!,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Inter',
|
||||
color: Color(0xFF5D4037),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
_errorMessage = null;
|
||||
});
|
||||
_loadWalletInfo();
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFD4AF37),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
/// 构建错误蒙版
|
||||
Widget _buildErrorOverlay() {
|
||||
return Positioned.fill(
|
||||
child: Container(
|
||||
color: const Color(0xCC5D4037), // 半透明深棕色蒙版
|
||||
child: Center(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 32),
|
||||
padding: const EdgeInsets.all(32),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFFDF5),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x33000000),
|
||||
blurRadius: 20,
|
||||
offset: Offset(0, 8),
|
||||
),
|
||||
child: const Text(
|
||||
'重试',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// 错误图标
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFFFFE4E4),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Center(
|
||||
child: Icon(
|
||||
Icons.error_outline,
|
||||
size: 48,
|
||||
color: Color(0xFFCC6B2C),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
// 错误标题
|
||||
const Text(
|
||||
'钱包生成失败',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Color(0xFF5D4037),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// 错误信息
|
||||
Text(
|
||||
_errorMessage ?? '未知错误',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF8B7355),
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
// 重试按钮
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
_errorMessage = null;
|
||||
_countdownSeconds = 100;
|
||||
});
|
||||
_loadWalletInfo();
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFD4A84B),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x33D4A84B),
|
||||
blurRadius: 8,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Text(
|
||||
'重新尝试',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建内容
|
||||
Widget _buildContent() {
|
||||
/// 构建内容(带占位数据)
|
||||
Widget _buildContentWithPlaceholder() {
|
||||
// 使用占位数据或真实数据
|
||||
final displayWords = _mnemonicWords.isNotEmpty
|
||||
? _mnemonicWords
|
||||
: List.generate(12, (i) => '******');
|
||||
final displayKava = _kavaAddress ?? '0x••••••••••••••••••••';
|
||||
final displayDst = _dstAddress ?? '0x••••••••••••••••••••';
|
||||
final displayBsc = _bscAddress ?? '0x••••••••••••••••••••';
|
||||
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
// 助记词卡片
|
||||
if (_mnemonicWords.isNotEmpty) _buildMnemonicCard(),
|
||||
// 助记词卡片 (使用占位或真实数据)
|
||||
_buildMnemonicCardWithData(displayWords),
|
||||
const SizedBox(height: 16),
|
||||
// 警告提示
|
||||
_buildWarningCard(),
|
||||
const SizedBox(height: 16),
|
||||
// 钱包地址卡片
|
||||
_buildAddressCard(),
|
||||
// 钱包地址卡片 (使用占位或真实数据)
|
||||
_buildAddressCardWithData(displayKava, displayDst, displayBsc),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
|
|
@ -710,6 +906,329 @@ ${DateTime.now().toString()}
|
|||
);
|
||||
}
|
||||
|
||||
/// 构建助记词卡片(带数据参数)
|
||||
Widget _buildMnemonicCardWithData(List<String> words) {
|
||||
final isPlaceholder = words.isNotEmpty && words[0] == '******';
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFFDF5),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x0D000000),
|
||||
blurRadius: 4,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 标题和操作按钮
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
'您的助记词',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w700,
|
||||
height: 1.25,
|
||||
letterSpacing: -0.27,
|
||||
color: Color(0xFF5D4037),
|
||||
),
|
||||
),
|
||||
// 仅在非占位状态时显示操作按钮
|
||||
if (!isPlaceholder)
|
||||
Row(
|
||||
children: [
|
||||
// 显示/隐藏按钮
|
||||
GestureDetector(
|
||||
onTap: _toggleVisibility,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Icon(
|
||||
_isHidden ? Icons.visibility : Icons.visibility_off,
|
||||
color: const Color(0xFF8B7355),
|
||||
size: 22,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
// 下载按钮
|
||||
GestureDetector(
|
||||
onTap: _isDownloading ? null : _downloadMnemonic,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: _isDownloading
|
||||
? const SizedBox(
|
||||
width: 22,
|
||||
height: 22,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Color(0xFF8B7355),
|
||||
),
|
||||
),
|
||||
)
|
||||
: const Icon(
|
||||
Icons.file_download_outlined,
|
||||
color: Color(0xFF8B7355),
|
||||
size: 22,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// 助记词网格
|
||||
_buildMnemonicGridWithData(words),
|
||||
const SizedBox(height: 20),
|
||||
// 复制全部按钮(仅在非占位状态时可用)
|
||||
_buildCopyAllButtonWithState(!isPlaceholder),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建助记词网格(带数据参数)
|
||||
Widget _buildMnemonicGridWithData(List<String> words) {
|
||||
return Column(
|
||||
children: [
|
||||
for (int row = 0; row < 4; row++)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
for (int col = 0; col < 3; col++)
|
||||
Expanded(
|
||||
child: _buildMnemonicWordWithData(
|
||||
row * 3 + col + 1,
|
||||
words.length > row * 3 + col ? words[row * 3 + col] : '',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建单个助记词项(带数据参数)
|
||||
Widget _buildMnemonicWordWithData(int index, String word) {
|
||||
final isPlaceholder = word == '******';
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// 序号
|
||||
Text(
|
||||
'$index.',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.43,
|
||||
color: isPlaceholder
|
||||
? const Color(0x668B7355)
|
||||
: const Color(0xCC8B7355),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
// 单词
|
||||
Text(
|
||||
_isHidden && !isPlaceholder ? '******' : word,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.5,
|
||||
color: isPlaceholder
|
||||
? const Color(0x665D4037)
|
||||
: const Color(0xFF5D4037),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建复制全部按钮(带状态)
|
||||
Widget _buildCopyAllButtonWithState(bool enabled) {
|
||||
return GestureDetector(
|
||||
onTap: enabled ? _copyAllMnemonic : null,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: 44,
|
||||
decoration: BoxDecoration(
|
||||
color: enabled
|
||||
? const Color(0xFFF5ECD9)
|
||||
: const Color(0xFFE8E0D0),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'复制全部',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.5,
|
||||
color: enabled
|
||||
? const Color(0xFF8B7355)
|
||||
: const Color(0x668B7355),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建钱包地址卡片(带数据参数)
|
||||
Widget _buildAddressCardWithData(String kava, String dst, String bsc) {
|
||||
final isPlaceholder = kava.contains('••••');
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFFDF5),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x0D000000),
|
||||
blurRadius: 4,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildAddressItemWithData(
|
||||
iconWidget: _buildChainIcon(),
|
||||
label: 'KAVA 地址',
|
||||
address: kava,
|
||||
showBorder: true,
|
||||
isPlaceholder: isPlaceholder,
|
||||
),
|
||||
_buildAddressItemWithData(
|
||||
iconWidget: _buildChainIcon(),
|
||||
label: 'DST 地址',
|
||||
address: dst,
|
||||
showBorder: true,
|
||||
isPlaceholder: isPlaceholder,
|
||||
),
|
||||
_buildAddressItemWithData(
|
||||
iconWidget: _buildChainIcon(),
|
||||
label: 'BSC 地址',
|
||||
address: bsc,
|
||||
showBorder: true,
|
||||
isPlaceholder: isPlaceholder,
|
||||
),
|
||||
_buildAddressItemWithData(
|
||||
iconWidget: _buildSequenceIcon(),
|
||||
label: '序列号',
|
||||
address: widget.userSerialNum.toString(),
|
||||
showBorder: false,
|
||||
isPlaceholder: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建地址项(带占位状态)
|
||||
Widget _buildAddressItemWithData({
|
||||
required Widget iconWidget,
|
||||
required String label,
|
||||
required String address,
|
||||
required bool showBorder,
|
||||
required bool isPlaceholder,
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
border: showBorder
|
||||
? const Border(
|
||||
bottom: BorderSide(
|
||||
color: Color(0x1A5D4037),
|
||||
width: 1,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// 图标
|
||||
iconWidget,
|
||||
const SizedBox(width: 14),
|
||||
// 标签和地址
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.5,
|
||||
color: isPlaceholder
|
||||
? const Color(0x995D4037)
|
||||
: const Color(0xFF5D4037),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
_formatAddress(address),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Inter',
|
||||
height: 1.5,
|
||||
color: isPlaceholder
|
||||
? const Color(0x66756452)
|
||||
: const Color(0x99756452),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// 复制按钮(仅在非占位状态时可用)
|
||||
if (!isPlaceholder)
|
||||
GestureDetector(
|
||||
onTap: () => _copyAddress(address, label),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
child: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.copy_rounded,
|
||||
color: Color(0xCC8B7355),
|
||||
size: 16,
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Text(
|
||||
'复制',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.5,
|
||||
color: Color(0xCC8B7355),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建警告卡片
|
||||
Widget _buildWarningCard() {
|
||||
return Container(
|
||||
|
|
@ -733,7 +1252,7 @@ ${DateTime.now().toString()}
|
|||
);
|
||||
}
|
||||
|
||||
/// 构建钱包地址卡片
|
||||
/// 构建钱包地址卡片 (旧方法,保留向后兼容)
|
||||
Widget _buildAddressCard() {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
|
|
|
|||
Loading…
Reference in New Issue