1172 lines
38 KiB
Dart
1172 lines
38 KiB
Dart
/// 今日诗词 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(),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|