- 清理大量废弃的 barrel 导出文件,移除冗余的中间导出层 - 修复所有相对路径导入错误,统一调整为扁平化模块引用 - 更新多平台 pubspec 版本号与依赖库版本 - 补充后端功能问题管理后台与脚本工具 - 调整部分页面的快捷方式文案适配新功能 - 更新部分翻译覆盖率与API文档
718 lines
23 KiB
Dart
718 lines
23 KiB
Dart
/// ============================================================
|
||
/// 闲言APP — 用户调试信息页面
|
||
/// 创建时间: 2026-05-01
|
||
/// 更新时间: 2026-05-13
|
||
/// 作用: 调试页面 — 显示API接口返回的所有用户信息/面板/调试/统计/热力图/设备/金币数据
|
||
/// 上次更新: 增加设备列表+金币记录接口数据展示
|
||
/// ============================================================
|
||
|
||
import 'dart:convert';
|
||
|
||
import 'package:flutter/cupertino.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
|
||
import '../../../core/theme/app_theme.dart';
|
||
import '../../../core/theme/app_spacing.dart';
|
||
import '../../../core/theme/app_typography.dart';
|
||
import '../../../core/theme/app_radius.dart';
|
||
import '../../../shared/widgets/adaptive/adaptive_back_button.dart';
|
||
import '../../../shared/widgets/containers/glass_container.dart';
|
||
import '../services/user_center_service.dart';
|
||
|
||
class UserDebugPage extends ConsumerStatefulWidget {
|
||
const UserDebugPage({super.key});
|
||
|
||
@override
|
||
ConsumerState<UserDebugPage> createState() => _UserDebugPageState();
|
||
}
|
||
|
||
class _UserDebugPageState extends ConsumerState<UserDebugPage> {
|
||
Map<String, dynamic>? _userInfoData;
|
||
Map<String, dynamic>? _debugData;
|
||
Map<String, dynamic>? _dashboardData;
|
||
Map<String, dynamic>? _statsData;
|
||
Map<String, dynamic>? _heatmapData;
|
||
Map<String, dynamic>? _signinCalendarData;
|
||
Map<String, dynamic>? _devicesData;
|
||
Map<String, dynamic>? _coinData;
|
||
bool _isLoading = true;
|
||
String? _error;
|
||
String _selectedTab = 'all';
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_loadAllData();
|
||
}
|
||
|
||
Future<void> _loadAllData() async {
|
||
setState(() {
|
||
_isLoading = true;
|
||
_error = null;
|
||
});
|
||
|
||
try {
|
||
final results = await Future.wait([
|
||
UserCenterService.getUserInfo(),
|
||
UserCenterService.getDebugInfo(),
|
||
UserCenterService.getDashboard(),
|
||
UserCenterService.getStats(),
|
||
UserCenterService.getHeatmap(),
|
||
UserCenterService.getSigninCalendar(),
|
||
UserCenterService.devices(action: 'list'),
|
||
UserCenterService.getCoinLog(),
|
||
]);
|
||
|
||
if (mounted) {
|
||
setState(() {
|
||
_userInfoData = results[0];
|
||
_debugData = results[1];
|
||
_dashboardData = results[2];
|
||
_statsData = results[3];
|
||
_heatmapData = results[4];
|
||
_signinCalendarData = results[5];
|
||
_devicesData = results[6];
|
||
_coinData = results[7];
|
||
_isLoading = false;
|
||
});
|
||
}
|
||
} catch (e) {
|
||
if (mounted) {
|
||
setState(() {
|
||
_error = e.toString();
|
||
_isLoading = false;
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final ext = AppTheme.ext(context);
|
||
|
||
return CupertinoPageScaffold(
|
||
backgroundColor: ext.bgPrimary,
|
||
navigationBar: CupertinoNavigationBar(
|
||
middle: Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Icon(CupertinoIcons.ant_fill, size: 18, color: ext.textPrimary),
|
||
const SizedBox(width: 6),
|
||
Text(
|
||
'调试信息',
|
||
style: AppTypography.title3.copyWith(color: ext.textPrimary),
|
||
),
|
||
],
|
||
),
|
||
backgroundColor: ext.bgPrimary.withValues(alpha: 0.85),
|
||
border: null,
|
||
leading: const AdaptiveBackButton(),
|
||
trailing: CupertinoButton(
|
||
padding: EdgeInsets.zero,
|
||
onPressed: _isLoading ? null : _loadAllData,
|
||
child: _isLoading
|
||
? const CupertinoActivityIndicator()
|
||
: Icon(CupertinoIcons.refresh, color: ext.accent),
|
||
),
|
||
),
|
||
child: SafeArea(bottom: false, child: _buildBody(ext)),
|
||
);
|
||
}
|
||
|
||
Widget _buildBody(AppThemeExtension ext) {
|
||
if (_isLoading) {
|
||
return const Center(child: CupertinoActivityIndicator());
|
||
}
|
||
|
||
if (_error != null) {
|
||
return Center(
|
||
child: Padding(
|
||
padding: const EdgeInsets.all(AppSpacing.md),
|
||
child: GlassContainer(
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
const Icon(CupertinoIcons.xmark_circle, size: 32, color: CupertinoColors.systemRed),
|
||
const SizedBox(height: AppSpacing.sm),
|
||
Text(
|
||
'加载失败',
|
||
style: AppTypography.title3.copyWith(color: ext.textPrimary),
|
||
),
|
||
const SizedBox(height: AppSpacing.xs),
|
||
Text(
|
||
_error!,
|
||
style: AppTypography.caption1.copyWith(
|
||
color: ext.textSecondary,
|
||
),
|
||
textAlign: TextAlign.center,
|
||
),
|
||
const SizedBox(height: AppSpacing.md),
|
||
CupertinoButton(
|
||
color: ext.accent,
|
||
padding: const EdgeInsets.symmetric(
|
||
horizontal: 16,
|
||
vertical: 8,
|
||
),
|
||
onPressed: _loadAllData,
|
||
child: const Text('重试'),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
return Column(
|
||
children: [
|
||
_buildTabBar(ext),
|
||
Expanded(
|
||
child: ListView(
|
||
padding: const EdgeInsets.symmetric(
|
||
horizontal: AppSpacing.md,
|
||
vertical: AppSpacing.sm,
|
||
),
|
||
children: _buildSections(ext),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
Widget _buildTabBar(AppThemeExtension ext) {
|
||
final tabs = [
|
||
('all', '全部'),
|
||
('user', '用户'),
|
||
('dashboard', '面板'),
|
||
('stats', '统计'),
|
||
('calendar', '日历'),
|
||
('devices', '设备'),
|
||
('coin', '金币'),
|
||
];
|
||
|
||
return SizedBox(
|
||
height: 44,
|
||
child: ListView(
|
||
scrollDirection: Axis.horizontal,
|
||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||
children: tabs.map((tab) {
|
||
final isSelected = _selectedTab == tab.$1;
|
||
return Padding(
|
||
padding: const EdgeInsets.only(right: AppSpacing.sm),
|
||
child: GestureDetector(
|
||
onTap: () => setState(() => _selectedTab = tab.$1),
|
||
child: Container(
|
||
padding: const EdgeInsets.symmetric(
|
||
horizontal: AppSpacing.md,
|
||
vertical: AppSpacing.xs,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color: isSelected
|
||
? ext.accent.withValues(alpha: 0.15)
|
||
: ext.bgSecondary.withValues(alpha: 0.5),
|
||
borderRadius: AppRadius.pillBorder,
|
||
),
|
||
child: Center(
|
||
child: Text(
|
||
tab.$2,
|
||
style: AppTypography.caption1.copyWith(
|
||
color: isSelected ? ext.accent : ext.textSecondary,
|
||
fontWeight: isSelected
|
||
? FontWeight.w700
|
||
: FontWeight.w500,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}).toList(),
|
||
),
|
||
);
|
||
}
|
||
|
||
List<Widget> _buildSections(AppThemeExtension ext) {
|
||
final sections = <Widget>[];
|
||
|
||
if (_selectedTab == 'all' || _selectedTab == 'user') {
|
||
sections.addAll([
|
||
_buildSection(
|
||
ext,
|
||
icon: CupertinoIcons.person,
|
||
title: '用户信息 (getUserInfo)',
|
||
endpoint: 'GET /api/user_center/index',
|
||
data: _userInfoData,
|
||
),
|
||
const SizedBox(height: AppSpacing.sm),
|
||
_buildSection(
|
||
ext,
|
||
icon: CupertinoIcons.ant_fill,
|
||
title: '调试信息 (getDebugInfo)',
|
||
endpoint: 'GET /api/user_center/debug',
|
||
data: _debugData,
|
||
),
|
||
const SizedBox(height: AppSpacing.sm),
|
||
_buildEditableFieldsInfo(ext),
|
||
const SizedBox(height: AppSpacing.sm),
|
||
]);
|
||
}
|
||
|
||
if (_selectedTab == 'all' || _selectedTab == 'dashboard') {
|
||
sections.addAll([
|
||
_buildSection(
|
||
ext,
|
||
icon: CupertinoIcons.chart_bar,
|
||
title: '数据面板 (getDashboard)',
|
||
endpoint: 'GET /api/user_center/dashboard',
|
||
data: _dashboardData,
|
||
),
|
||
const SizedBox(height: AppSpacing.sm),
|
||
]);
|
||
}
|
||
|
||
if (_selectedTab == 'all' || _selectedTab == 'stats') {
|
||
sections.addAll([
|
||
_buildSection(
|
||
ext,
|
||
icon: CupertinoIcons.chart_bar_square,
|
||
title: '学习统计 (getStats)',
|
||
endpoint: 'GET /api/user_center/stats?type=overview',
|
||
data: _statsData,
|
||
),
|
||
const SizedBox(height: AppSpacing.sm),
|
||
_buildSection(
|
||
ext,
|
||
icon: CupertinoIcons.flame,
|
||
title: '活跃热力图 (getHeatmap)',
|
||
endpoint: 'GET /api/user_center/heatmap',
|
||
data: _heatmapData,
|
||
),
|
||
const SizedBox(height: AppSpacing.sm),
|
||
]);
|
||
}
|
||
|
||
if (_selectedTab == 'all' || _selectedTab == 'calendar') {
|
||
sections.addAll([
|
||
_buildSection(
|
||
ext,
|
||
icon: CupertinoIcons.calendar,
|
||
title: '签到日历 (getSigninCalendar)',
|
||
endpoint: 'GET /api/user_center/signin_calendar',
|
||
data: _signinCalendarData,
|
||
),
|
||
const SizedBox(height: AppSpacing.sm),
|
||
]);
|
||
}
|
||
|
||
if (_selectedTab == 'all' || _selectedTab == 'devices') {
|
||
sections.addAll([
|
||
_buildSection(
|
||
ext,
|
||
icon: CupertinoIcons.device_laptop,
|
||
title: '设备列表 (devices)',
|
||
endpoint: 'POST /api/user_center/devices?action=list',
|
||
data: _devicesData,
|
||
),
|
||
const SizedBox(height: AppSpacing.sm),
|
||
]);
|
||
}
|
||
|
||
if (_selectedTab == 'all' || _selectedTab == 'coin') {
|
||
sections.addAll([
|
||
_buildSection(
|
||
ext,
|
||
icon: CupertinoIcons.money_dollar_circle,
|
||
title: '金币记录 (coin)',
|
||
endpoint: 'GET /api/user_center/coin',
|
||
data: _coinData,
|
||
),
|
||
const SizedBox(height: AppSpacing.sm),
|
||
]);
|
||
}
|
||
|
||
sections.add(const SizedBox(height: AppSpacing.xl));
|
||
return sections;
|
||
}
|
||
|
||
Widget _buildSection(
|
||
AppThemeExtension ext, {
|
||
required IconData icon,
|
||
required String title,
|
||
required String endpoint,
|
||
required Map<String, dynamic>? data,
|
||
}) {
|
||
return GlassContainer(
|
||
depth: GlassDepth.elevated,
|
||
padding: EdgeInsets.zero,
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(
|
||
AppSpacing.md,
|
||
AppSpacing.md,
|
||
AppSpacing.md,
|
||
AppSpacing.xs,
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Icon(icon, size: 18, color: ext.accent),
|
||
const SizedBox(width: 6),
|
||
Expanded(
|
||
child: Text(
|
||
title,
|
||
style: AppTypography.headline.copyWith(
|
||
color: ext.textPrimary,
|
||
fontWeight: FontWeight.w700,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||
child: Container(
|
||
padding: const EdgeInsets.symmetric(
|
||
horizontal: AppSpacing.sm,
|
||
vertical: 4,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color: ext.accent.withValues(alpha: 0.08),
|
||
borderRadius: AppRadius.smBorder,
|
||
),
|
||
child: Text(
|
||
endpoint,
|
||
style: AppTypography.caption2.copyWith(
|
||
color: ext.accent,
|
||
fontFamily: 'monospace',
|
||
),
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(height: AppSpacing.sm),
|
||
if (data != null)
|
||
...data.entries.map((entry) => _buildDataRow(ext, entry))
|
||
else
|
||
Padding(
|
||
padding: const EdgeInsets.all(AppSpacing.md),
|
||
child: Text(
|
||
'暂无数据',
|
||
style: AppTypography.subhead.copyWith(color: ext.textHint),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildDataRow(AppThemeExtension ext, MapEntry<String, dynamic> entry) {
|
||
final value = entry.value;
|
||
final displayValue = value is Map || value is List
|
||
? const JsonEncoder.withIndent(' ').convert(value)
|
||
: value?.toString() ?? 'null';
|
||
|
||
final isComplex = value is Map || value is List;
|
||
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Divider(
|
||
height: 0.5,
|
||
thickness: 0.5,
|
||
color: ext.textHint.withValues(alpha: 0.15),
|
||
),
|
||
Padding(
|
||
padding: const EdgeInsets.symmetric(vertical: AppSpacing.xs),
|
||
child: Row(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
SizedBox(
|
||
width: 110,
|
||
child: Text(
|
||
entry.key,
|
||
style: AppTypography.caption1.copyWith(
|
||
color: ext.accent,
|
||
fontWeight: FontWeight.w600,
|
||
fontFamily: 'monospace',
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(width: AppSpacing.sm),
|
||
Expanded(
|
||
child: isComplex
|
||
? Container(
|
||
width: double.infinity,
|
||
padding: const EdgeInsets.all(AppSpacing.sm),
|
||
decoration: BoxDecoration(
|
||
color: ext.bgSecondary.withValues(alpha: 0.5),
|
||
borderRadius: AppRadius.smBorder,
|
||
),
|
||
child: Text(
|
||
displayValue,
|
||
style: AppTypography.caption1.copyWith(
|
||
color: ext.textSecondary,
|
||
fontFamily: 'monospace',
|
||
height: 1.4,
|
||
),
|
||
),
|
||
)
|
||
: Text(
|
||
displayValue,
|
||
style: AppTypography.caption1.copyWith(
|
||
color: ext.textPrimary,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildEditableFieldsInfo(AppThemeExtension ext) {
|
||
return GlassContainer(
|
||
depth: GlassDepth.elevated,
|
||
padding: EdgeInsets.zero,
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(
|
||
AppSpacing.md,
|
||
AppSpacing.md,
|
||
AppSpacing.md,
|
||
AppSpacing.xs,
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Icon(CupertinoIcons.pencil, size: 18, color: ext.accent),
|
||
const SizedBox(width: 6),
|
||
Text(
|
||
'可编辑字段总览',
|
||
style: AppTypography.headline.copyWith(
|
||
color: ext.textPrimary,
|
||
fontWeight: FontWeight.w700,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||
child: Container(
|
||
padding: const EdgeInsets.symmetric(
|
||
horizontal: AppSpacing.sm,
|
||
vertical: 4,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color: CupertinoColors.systemGreen.withValues(alpha: 0.08),
|
||
borderRadius: AppRadius.smBorder,
|
||
),
|
||
child: Text(
|
||
'POST /api/user_center/profile',
|
||
style: AppTypography.caption2.copyWith(
|
||
color: CupertinoColors.systemGreen,
|
||
fontFamily: 'monospace',
|
||
),
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(height: AppSpacing.sm),
|
||
_buildEditableRow(
|
||
ext,
|
||
'username',
|
||
'用户名',
|
||
'3-30字符,字母/数字/下划线/中文',
|
||
'profile',
|
||
),
|
||
_buildEditableRow(ext, 'nickname', '昵称', '1-50字符', 'profile'),
|
||
_buildEditableRow(ext, 'bio', '个人简介', '0-500字符', 'profile'),
|
||
_buildEditableRow(ext, 'avatar', '头像URL', '1-500字符', 'profile'),
|
||
Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||
child: Divider(
|
||
height: 0.5,
|
||
thickness: 0.5,
|
||
color: ext.textHint.withValues(alpha: 0.15),
|
||
),
|
||
),
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(
|
||
AppSpacing.md,
|
||
AppSpacing.sm,
|
||
AppSpacing.md,
|
||
AppSpacing.xs,
|
||
),
|
||
child: Container(
|
||
padding: const EdgeInsets.symmetric(
|
||
horizontal: AppSpacing.sm,
|
||
vertical: 4,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color: CupertinoColors.systemOrange.withValues(alpha: 0.08),
|
||
borderRadius: AppRadius.smBorder,
|
||
),
|
||
child: Text(
|
||
'安全接口 /api/user_security/* (需回执验证)',
|
||
style: AppTypography.caption2.copyWith(
|
||
color: CupertinoColors.systemOrange,
|
||
fontFamily: 'monospace',
|
||
),
|
||
),
|
||
),
|
||
),
|
||
_buildEditableRow(
|
||
ext,
|
||
'email',
|
||
'邮箱',
|
||
'5-100字符,需回执(changeemail)',
|
||
'security',
|
||
),
|
||
_buildEditableRow(
|
||
ext,
|
||
'mobile',
|
||
'手机号',
|
||
'11位,需回执(changemobile)',
|
||
'security',
|
||
),
|
||
_buildEditableRow(
|
||
ext,
|
||
'password',
|
||
'密码',
|
||
'6-30字符,需回执(changepwd)',
|
||
'security',
|
||
),
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(
|
||
AppSpacing.md,
|
||
AppSpacing.sm,
|
||
AppSpacing.md,
|
||
AppSpacing.md,
|
||
),
|
||
child: Container(
|
||
padding: const EdgeInsets.all(AppSpacing.sm),
|
||
decoration: BoxDecoration(
|
||
color: CupertinoColors.systemBlue.withValues(alpha: 0.08),
|
||
borderRadius: AppRadius.smBorder,
|
||
),
|
||
child: Row(
|
||
children: [
|
||
const Icon(CupertinoIcons.lightbulb, size: 14, color: CupertinoColors.systemBlue),
|
||
const SizedBox(width: AppSpacing.xs),
|
||
Expanded(
|
||
child: Text(
|
||
'回执(Receipt)验证: HMAC-SHA256签名,密钥Xy7kP9mL2qR4wS8v,有效期300秒',
|
||
style: AppTypography.caption1.copyWith(
|
||
color: CupertinoColors.systemBlue,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildEditableRow(
|
||
AppThemeExtension ext,
|
||
String field,
|
||
String label,
|
||
String desc,
|
||
String apiType,
|
||
) {
|
||
final isProfile = apiType == 'profile';
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Divider(
|
||
height: 0.5,
|
||
thickness: 0.5,
|
||
color: ext.textHint.withValues(alpha: 0.15),
|
||
),
|
||
Padding(
|
||
padding: const EdgeInsets.symmetric(vertical: AppSpacing.xs),
|
||
child: Row(
|
||
children: [
|
||
SizedBox(
|
||
width: 110,
|
||
child: Row(
|
||
children: [
|
||
Text(
|
||
field,
|
||
style: AppTypography.caption1.copyWith(
|
||
color: isProfile
|
||
? CupertinoColors.systemGreen
|
||
: CupertinoColors.systemOrange,
|
||
fontWeight: FontWeight.w600,
|
||
fontFamily: 'monospace',
|
||
),
|
||
),
|
||
const SizedBox(width: 4),
|
||
Container(
|
||
padding: const EdgeInsets.symmetric(
|
||
horizontal: 3,
|
||
vertical: 1,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color:
|
||
(isProfile
|
||
? CupertinoColors.systemGreen
|
||
: CupertinoColors.systemOrange)
|
||
.withValues(alpha: 0.1),
|
||
borderRadius: AppRadius.pillBorder,
|
||
),
|
||
child: Text(
|
||
apiType,
|
||
style: AppTypography.caption2.copyWith(
|
||
color: isProfile
|
||
? CupertinoColors.systemGreen
|
||
: CupertinoColors.systemOrange,
|
||
fontSize: 8,
|
||
fontWeight: FontWeight.w700,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
const SizedBox(width: AppSpacing.sm),
|
||
Expanded(
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
label,
|
||
style: AppTypography.caption1.copyWith(
|
||
color: ext.textPrimary,
|
||
fontWeight: FontWeight.w600,
|
||
),
|
||
),
|
||
Text(
|
||
desc,
|
||
style: AppTypography.caption2.copyWith(
|
||
color: ext.textHint,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|