Files
wushu/lib/views/profile/components/jinrishici_sdk_page.dart
2026-04-09 02:23:27 +08:00

1172 lines
38 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// 今日诗词 SDK 信息展示页面
///
/// 创建时间2026-04-08
/// 作用:展示今日诗词安卓 SDK 的使用方法和 API 接口信息
/// 最后更新2026-04-08 - 添加复制JSON数据按钮功能
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import '../../../models/scenario/jinrishici_sdk_config.dart';
import '../../../models/colors/app_colors.dart';
import '../../../services/get/theme_controller.dart';
import '../../../services/jinrishici_service.dart';
class JinrishiciSdkPage extends StatefulWidget {
const JinrishiciSdkPage({super.key});
@override
State<JinrishiciSdkPage> createState() => _JinrishiciSdkPageState();
}
class _JinrishiciSdkPageState extends State<JinrishiciSdkPage>
with TickerProviderStateMixin {
final ThemeController _themeController = Get.find<ThemeController>();
final JinrishiciService _jinrishiciService = JinrishiciService();
late AnimationController _fadeController;
late Animation<double> _fadeAnimation;
Map<String, dynamic>? _poetryData;
bool _isLoading = false;
String? _errorMessage;
Map<String, dynamic>? _userInfo;
@override
void initState() {
super.initState();
_fadeController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_fadeAnimation = CurvedAnimation(
parent: _fadeController,
curve: Curves.easeIn,
);
_fadeController.forward();
_loadPoetry();
}
/// 加载今日诗词
Future<void> _loadPoetry() async {
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
final poetry = await _jinrishiciService.getTodayPoetry();
setState(() {
_poetryData = poetry;
_isLoading = false;
});
// 同时加载用户信息
try {
final userInfo = await _jinrishiciService.getUserInfo();
setState(() {
_userInfo = userInfo;
});
} catch (e) {
print('加载用户信息失败: $e');
// 用户信息加载失败不影响诗词显示
}
} catch (e) {
setState(() {
_errorMessage = e.toString();
_isLoading = false;
});
}
}
/// 复制当前诗词数据为JSON格式
Future<void> _copyPoetryJson() async {
if (_poetryData == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('暂无数据可复制')),
);
return;
}
try {
final jsonString = jsonEncode(_poetryData);
await Clipboard.setData(ClipboardData(text: jsonString));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('JSON数据已复制到剪贴板')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('复制失败: $e')),
);
}
}
@override
void dispose() {
_fadeController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Obx(() {
final isDark = _themeController.isDarkMode;
final primaryColor = _themeController.currentThemeColor;
return Scaffold(
backgroundColor: isDark ? const Color(0xFF1A1A1A) : Colors.grey[50],
appBar: AppBar(
title: Text(
'今日诗词 SDK',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
color: primaryColor,
),
),
backgroundColor: isDark ? const Color(0xFF2A2A2A) : Colors.white,
foregroundColor: primaryColor,
elevation: 0,
centerTitle: true,
),
body: FadeTransition(
opacity: _fadeAnimation,
child: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildPoetryCard(isDark, primaryColor),
if (_userInfo != null) ...[
const SizedBox(height: 16),
_buildUserInfoCard(isDark, primaryColor),
],
const SizedBox(height: 16),
_buildInfoCard(isDark, primaryColor),
const SizedBox(height: 16),
_buildSectionCard('SDK 基本信息', Icons.info_outline, [
_buildInfoRow('SDK 名称', JinrishiciSdkConfig.sdkName, isDark),
_buildInfoRow('包名', JinrishiciSdkConfig.packageName, isDark),
_buildInfoRow('许可证', JinrishiciSdkConfig.license, isDark),
_buildClickableRow(
'GitHub',
JinrishiciSdkConfig.githubUrl,
isDark,
primaryColor,
),
], isDark, primaryColor),
const SizedBox(height: 16),
_buildSectionCard('API 接口', Icons.api, [
_buildClickableRow(
'Token 获取',
JinrishiciSdkConfig.tokenUrl,
isDark,
primaryColor,
),
_buildClickableRow(
'获取诗词',
JinrishiciSdkConfig.sentenceUrl,
isDark,
primaryColor,
),
], isDark, primaryColor),
const SizedBox(height: 16),
_buildSectionCard('安装方式', Icons.download, [
_buildCodeBlock('Gradle', JinrishiciSdkWorkflow.gradleDependency, isDark),
const SizedBox(height: 12),
_buildCodeBlock('Maven', JinrishiciSdkWorkflow.mavenDependency, isDark),
], isDark, primaryColor),
const SizedBox(height: 16),
_buildSectionCard('初始化', Icons.play_arrow, [
_buildCodeBlock('初始化方法', JinrishiciSdkUsage.initMethod, isDark),
], isDark, primaryColor),
const SizedBox(height: 16),
_buildSectionCard('获取诗词', Icons.book, [
_buildCodeBlock('异步方法', JinrishiciSdkUsage.asyncMethod, isDark),
const SizedBox(height: 12),
_buildCodeBlock('同步方法', JinrishiciSdkUsage.syncMethod, isDark),
], isDark, primaryColor),
const SizedBox(height: 16),
_buildSectionCard('自定义控件', Icons.widgets, [
_buildCodeBlock('XML 布局', JinrishiciSdkUsage.customWidget, isDark),
], isDark, primaryColor),
const SizedBox(height: 16),
_buildSectionCard('工作流程', Icons.timeline, JinrishiciSdkWorkflow.steps
.map((step) => _buildStepItem(step, isDark, primaryColor))
.toList(), isDark, primaryColor),
const SizedBox(height: 16),
_buildSectionCard('数据模型', Icons.data_object, [
_buildDataModelCard('PoetySentence', JinrishiciSdkData.poetySentenceFields, isDark),
const SizedBox(height: 12),
_buildDataModelCard('DataBean', JinrishiciSdkData.dataBeanFields, isDark),
const SizedBox(height: 12),
_buildDataModelCard('OriginBean', JinrishiciSdkData.originBeanFields, isDark),
const SizedBox(height: 12),
_buildDataModelCard('PoetyToken', JinrishiciSdkData.poetyTokenFields, isDark),
], isDark, primaryColor),
const SizedBox(height: 100),
],
),
),
);
});
}
Widget _buildPoetryCard(bool isDark, Color primaryColor) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
primaryColor.withAlpha(30),
primaryColor.withAlpha(10),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: primaryColor.withAlpha(50),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: primaryColor.withAlpha(20),
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: Text(
'📜',
style: const TextStyle(fontSize: 20),
),
),
),
const SizedBox(width: 12),
Text(
'今日诗词',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black87,
),
),
],
),
Row(
children: [
IconButton(
onPressed: _poetryData == null ? null : _copyPoetryJson,
icon: Icon(
Icons.code,
color: _poetryData == null
? (isDark ? Colors.grey[600] : Colors.grey[400])
: primaryColor,
),
tooltip: '复制JSON',
),
IconButton(
onPressed: _isLoading ? null : _loadPoetry,
icon: _isLoading
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(primaryColor),
),
)
: Icon(
Icons.refresh,
color: primaryColor,
),
),
],
),
],
),
const SizedBox(height: 16),
if (_isLoading)
Center(
child: Column(
children: [
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(primaryColor),
),
const SizedBox(height: 12),
Text(
'正在加载诗词...',
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
],
),
)
else if (_errorMessage != null)
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.red.withAlpha(10),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Colors.red.withAlpha(30),
),
),
child: Row(
children: [
Icon(
Icons.error_outline,
color: Colors.red,
size: 20,
),
const SizedBox(width: 8),
Expanded(
child: Text(
_errorMessage!,
style: TextStyle(
fontSize: 13,
color: isDark ? Colors.red[300] : Colors.red[700],
),
),
),
],
),
)
else if (_poetryData != null)
_buildPoetryContent(isDark, primaryColor)
else
Center(
child: Text(
'点击刷新按钮获取今日诗词',
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
),
],
),
);
}
Widget _buildPoetryContent(bool isDark, Color primaryColor) {
print('完整诗词数据: $_poetryData');
final data = _poetryData!['data'] as Map<String, dynamic>?;
print('Data 字段: $data');
final origin = data?['origin'] as Map<String, dynamic>?;
print('Origin 字段: $origin');
print('标签数据: ${data?['matchTags']}');
if (data == null) {
return Text(
'数据格式错误',
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (origin != null) ...[
Text(
origin['title'] ?? '未知标题',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black87,
),
),
const SizedBox(height: 8),
Row(
children: [
Text(
origin['dynasty'] ?? '未知朝代',
style: TextStyle(
fontSize: 14,
color: primaryColor,
fontWeight: FontWeight.w500,
),
),
const SizedBox(width: 8),
Text(
'·',
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
const SizedBox(width: 8),
Text(
origin['author'] ?? '未知作者',
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[300] : Colors.grey[700],
),
),
],
),
const SizedBox(height: 16),
],
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF1A1A1A).withAlpha(50) : Colors.white.withAlpha(50),
borderRadius: BorderRadius.circular(12),
),
child: Text(
data['content'] ?? '暂无内容',
style: TextStyle(
fontSize: 16,
color: isDark ? Colors.white : Colors.black87,
height: 1.8,
letterSpacing: 0.5,
),
textAlign: TextAlign.center,
),
),
if (origin != null && origin['content'] != null) ...[
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF1A1A1A).withValues(alpha: 0.3) : Colors.grey[100]!.withValues(alpha: 0.5),
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'完整诗词',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: primaryColor,
),
),
const SizedBox(height: 8),
...(origin['content'] as List).map<Widget>((line) => Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
line.toString(),
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[300] : Colors.grey[700],
height: 1.6,
),
textAlign: TextAlign.center,
),
)),
],
),
),
],
if (origin != null && origin['translate'] != null) ...[
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF1A1A1A).withValues(alpha: 0.3) : Colors.grey[100]!.withValues(alpha: 0.5),
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'翻译',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: primaryColor,
),
),
const SizedBox(height: 8),
...(origin['translate'] as List).map<Widget>((line) => Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
line.toString(),
style: TextStyle(
fontSize: 13,
color: isDark ? Colors.grey[400] : Colors.grey[600],
height: 1.6,
),
),
)),
],
),
),
],
if (data['matchTags'] != null) ...[
const SizedBox(height: 12),
Wrap(
spacing: 8,
runSpacing: 8,
children: (data['matchTags'] as List)
.map<Widget>((tag) => Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: primaryColor.withAlpha(15),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: primaryColor.withAlpha(30),
),
),
child: Text(
tag.toString(),
style: TextStyle(
fontSize: 12,
color: primaryColor,
fontWeight: FontWeight.w500,
),
),
))
.toList(),
),
],
const SizedBox(height: 16),
_buildPoetryMetadata(data, isDark, primaryColor),
],
);
}
Widget _buildPoetryMetadata(Map<String, dynamic> data, bool isDark, Color primaryColor) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF1A1A1A).withValues(alpha: 0.3) : Colors.grey[100]!.withValues(alpha: 0.5),
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'诗词信息',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: primaryColor,
),
),
const SizedBox(height: 8),
if (data['id'] != null)
_buildMetadataRow('ID', data['id'].toString(), isDark),
if (data['popularity'] != null)
_buildMetadataRow('流行度', _formatNumber(data['popularity']), isDark),
if (data['cacheAt'] != null)
_buildMetadataRow('缓存时间', data['cacheAt'].toString(), isDark),
if (data['recommendedReason'] != null && data['recommendedReason'].toString().isNotEmpty)
_buildMetadataRow('推荐理由', data['recommendedReason'].toString(), isDark),
if (_poetryData!['token'] != null)
_buildMetadataRow('Token', _poetryData!['token'].toString().substring(0, 20) + '...', isDark),
if (_poetryData!['ipAddress'] != null)
_buildMetadataRow('IP 地址', _poetryData!['ipAddress'].toString(), isDark),
],
),
);
}
Widget _buildMetadataRow(String label, String value, bool isDark) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 80,
child: Text(
label,
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
),
Expanded(
child: Text(
value,
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[300] : Colors.grey[700],
),
),
),
],
),
);
}
String _formatNumber(dynamic number) {
if (number is int) {
if (number >= 10000) {
return '${(number / 10000).toStringAsFixed(1)}';
}
return number.toString();
}
return number.toString();
}
Widget _buildUserInfoCard(bool isDark, Color primaryColor) {
final data = _userInfo?['data'] as Map<String, dynamic>?;
if (data == null) return const SizedBox.shrink();
final weatherData = data['weatherData'] as Map<String, dynamic>?;
final tags = data['tags'] as List<dynamic>?;
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
primaryColor.withAlpha(25),
primaryColor.withAlpha(10),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: primaryColor.withAlpha(40),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: primaryColor.withAlpha(20),
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: Text(
'🌤️',
style: const TextStyle(fontSize: 20),
),
),
),
const SizedBox(width: 12),
Text(
'环境信息',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black87,
),
),
],
),
const SizedBox(height: 16),
_buildUserInfoRow('IP 地址', data['ip']?.toString() ?? '未知', isDark),
_buildUserInfoRow('地区', data['region']?.toString() ?? '未知', isDark),
if (data['beijingTime'] != null)
_buildUserInfoRow('北京时间', data['beijingTime'].toString(), isDark),
if (weatherData != null) ...[
const SizedBox(height: 12),
_buildWeatherInfo(weatherData, isDark, primaryColor),
],
if (tags != null && tags.isNotEmpty) ...[
const SizedBox(height: 12),
_buildTags(tags, isDark, primaryColor),
],
],
),
);
}
Widget _buildUserInfoRow(String label, String value, bool isDark) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 80,
child: Text(
label,
style: TextStyle(
fontSize: 13,
color: isDark ? Colors.grey[400] : Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
),
Expanded(
child: Text(
value,
style: TextStyle(
fontSize: 13,
color: isDark ? Colors.white : Colors.black87,
),
),
),
],
),
);
}
Widget _buildWeatherInfo(Map<String, dynamic> weatherData, bool isDark, Color primaryColor) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF1A1A1A).withAlpha(50) : Colors.white.withAlpha(50),
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.cloud, color: primaryColor, size: 16),
const SizedBox(width: 6),
Text(
'天气信息',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : Colors.black87,
),
),
],
),
const SizedBox(height: 8),
Wrap(
spacing: 12,
runSpacing: 8,
children: [
_buildWeatherItem('温度', '${weatherData['temperature']?.toString() ?? '-'}°C', isDark),
_buildWeatherItem('天气', weatherData['weather']?.toString() ?? '-', isDark),
_buildWeatherItem('湿度', '${weatherData['humidity']?.toString() ?? '-'}%', isDark),
_buildWeatherItem('风向', weatherData['windDirection']?.toString() ?? '-', isDark),
_buildWeatherItem('风力', '${weatherData['windPower']?.toString() ?? '-'}', isDark),
_buildWeatherItem('能见度', weatherData['visibility']?.toString() ?? '-', isDark),
if (weatherData['rainfall'] != null)
_buildWeatherItem('降雨量', '${weatherData['rainfall']}mm', isDark),
if (weatherData['pm25'] != null)
_buildWeatherItem('PM2.5', '${weatherData['pm25']}', isDark),
],
),
],
),
);
}
Widget _buildWeatherItem(String label, String value, bool isDark) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'$label: ',
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
Text(
value,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: isDark ? Colors.white : Colors.black87,
),
),
],
);
}
Widget _buildTags(List<dynamic> tags, bool isDark, Color primaryColor) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.local_offer, color: primaryColor, size: 16),
const SizedBox(width: 6),
Text(
'推荐标签',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : Colors.black87,
),
),
],
),
const SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
children: tags.map<Widget>((tag) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: primaryColor.withAlpha(15),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: primaryColor.withAlpha(30)),
),
child: Text(
tag.toString(),
style: TextStyle(
fontSize: 12,
color: primaryColor,
fontWeight: FontWeight.w500,
),
),
);
}).toList(),
),
],
);
}
Widget _buildInfoCard(bool isDark, Color primaryColor) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
primaryColor.withAlpha(30),
primaryColor.withAlpha(10),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: primaryColor.withAlpha(50),
width: 1,
),
),
child: Row(
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: primaryColor.withAlpha(20),
borderRadius: BorderRadius.circular(16),
),
child: Center(
child: Text(
'📜',
style: const TextStyle(fontSize: 28),
),
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
JinrishiciSdkConfig.sdkName,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black87,
),
),
const SizedBox(height: 4),
Text(
'每日一首诗词,感受传统文化之美',
style: TextStyle(
fontSize: 13,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
],
),
),
],
),
);
}
Widget _buildSectionCard(
String title,
IconData icon,
List<Widget> children,
bool isDark,
Color primaryColor,
) {
return Container(
decoration: BoxDecoration(
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: isDark
? Colors.black.withAlpha(76)
: Colors.black.withAlpha(26),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Icon(icon, color: primaryColor, size: 20),
const SizedBox(width: 8),
Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : Colors.black87,
),
),
],
),
),
...children,
],
),
);
}
Widget _buildInfoRow(String label, String value, bool isDark) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 80,
child: Text(
label,
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[400] : Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
),
Expanded(
child: Text(
value,
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.white : Colors.black87,
),
),
),
],
),
);
}
Widget _buildClickableRow(
String label,
String url,
bool isDark,
Color primaryColor,
) {
return InkWell(
onTap: () {
Clipboard.setData(ClipboardData(text: url));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('已复制: $label')),
);
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 80,
child: Text(
label,
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[400] : Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
),
Expanded(
child: Text(
url,
style: TextStyle(
fontSize: 14,
color: primaryColor,
decoration: TextDecoration.underline,
),
),
),
Icon(
Icons.content_copy,
size: 16,
color: isDark ? Colors.grey[500] : Colors.grey[400],
),
],
),
),
);
}
Widget _buildCodeBlock(String title, String code, bool isDark) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text(
title,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: isDark ? Colors.grey[300] : Colors.grey[700],
),
),
),
Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF1A1A1A) : Colors.grey[100],
borderRadius: BorderRadius.circular(8),
),
child: InkWell(
onTap: () {
Clipboard.setData(ClipboardData(text: code));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('代码已复制')),
);
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
code.trim(),
style: TextStyle(
fontSize: 12,
fontFamily: 'monospace',
color: isDark ? Colors.grey[300] : Colors.grey[700],
height: 1.5,
),
),
),
const SizedBox(width: 8),
Icon(
Icons.content_copy,
size: 16,
color: isDark ? Colors.grey[500] : Colors.grey[400],
),
],
),
),
),
],
);
}
Widget _buildStepItem(String step, bool isDark, Color primaryColor) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 8,
height: 8,
margin: const EdgeInsets.only(top: 6, right: 12),
decoration: BoxDecoration(
color: primaryColor,
shape: BoxShape.circle,
),
),
Expanded(
child: Text(
step,
style: TextStyle(
fontSize: 14,
color: isDark ? Colors.grey[300] : Colors.grey[700],
height: 1.5,
),
),
),
],
),
);
}
Widget _buildDataModelCard(String className, Map<String, String> fields, bool isDark) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF1A1A1A) : Colors.grey[100],
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
className,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : Colors.black87,
),
),
const SizedBox(height: 8),
...fields.entries.map((entry) {
return Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${entry.key}: ',
style: TextStyle(
fontSize: 12,
fontFamily: 'monospace',
color: isDark ? Colors.blue[300] : Colors.blue[700],
fontWeight: FontWeight.w500,
),
),
Expanded(
child: Text(
entry.value,
style: TextStyle(
fontSize: 12,
color: isDark ? Colors.grey[400] : Colors.grey[600],
),
),
),
],
),
);
}).toList(),
],
),
);
}
}