519 lines
16 KiB
Dart
519 lines
16 KiB
Dart
|
|
import 'package:flutter/material.dart';
|
|||
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
|
|
import '../../../core/utils/responsive_utils.dart';
|
|||
|
|
import '../../../shared/widgets/custom_app_bar.dart';
|
|||
|
|
import '../../../shared/widgets/loading_widget.dart';
|
|||
|
|
|
|||
|
|
class AIRecommendationScreen extends ConsumerStatefulWidget {
|
|||
|
|
const AIRecommendationScreen({super.key});
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
ConsumerState<AIRecommendationScreen> createState() => _AIRecommendationScreenState();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class _AIRecommendationScreenState extends ConsumerState<AIRecommendationScreen> {
|
|||
|
|
bool _isLoading = false;
|
|||
|
|
List<RecommendationItem> _recommendations = [];
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
void initState() {
|
|||
|
|
super.initState();
|
|||
|
|
_loadRecommendations();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Future<void> _loadRecommendations() async {
|
|||
|
|
setState(() {
|
|||
|
|
_isLoading = true;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 模拟AI推荐数据加载
|
|||
|
|
await Future.delayed(const Duration(milliseconds: 1000));
|
|||
|
|
|
|||
|
|
setState(() {
|
|||
|
|
_recommendations = _generateRecommendations();
|
|||
|
|
_isLoading = false;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
List<RecommendationItem> _generateRecommendations() {
|
|||
|
|
return [
|
|||
|
|
RecommendationItem(
|
|||
|
|
type: RecommendationType.vocabulary,
|
|||
|
|
title: '商务英语词汇强化',
|
|||
|
|
description: '基于您的学习历史,建议加强商务场景词汇学习',
|
|||
|
|
priority: 'high',
|
|||
|
|
estimatedTime: '15分钟',
|
|||
|
|
icon: Icons.business_center,
|
|||
|
|
color: Colors.blue,
|
|||
|
|
action: '开始学习',
|
|||
|
|
details: [
|
|||
|
|
'包含50个高频商务词汇',
|
|||
|
|
'涵盖会议、谈判、报告等场景',
|
|||
|
|
'配有真实商务对话示例',
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
RecommendationItem(
|
|||
|
|
type: RecommendationType.review,
|
|||
|
|
title: '复习昨日学习内容',
|
|||
|
|
description: '您有8个单词需要复习,趁热打铁效果更佳',
|
|||
|
|
priority: 'high',
|
|||
|
|
estimatedTime: '10分钟',
|
|||
|
|
icon: Icons.refresh,
|
|||
|
|
color: Colors.orange,
|
|||
|
|
action: '立即复习',
|
|||
|
|
details: [
|
|||
|
|
'8个单词待复习',
|
|||
|
|
'基于遗忘曲线算法推荐',
|
|||
|
|
'巩固记忆效果显著',
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
RecommendationItem(
|
|||
|
|
type: RecommendationType.test,
|
|||
|
|
title: '词汇测试挑战',
|
|||
|
|
description: '测试您的词汇掌握程度,发现薄弱环节',
|
|||
|
|
priority: 'medium',
|
|||
|
|
estimatedTime: '20分钟',
|
|||
|
|
icon: Icons.quiz,
|
|||
|
|
color: Colors.green,
|
|||
|
|
action: '开始测试',
|
|||
|
|
details: [
|
|||
|
|
'30道精选测试题',
|
|||
|
|
'多种题型组合',
|
|||
|
|
'即时反馈和解析',
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
RecommendationItem(
|
|||
|
|
type: RecommendationType.plan,
|
|||
|
|
title: '制定本周学习计划',
|
|||
|
|
description: '为您量身定制的学习计划,提高学习效率',
|
|||
|
|
priority: 'medium',
|
|||
|
|
estimatedTime: '5分钟',
|
|||
|
|
icon: Icons.schedule,
|
|||
|
|
color: Colors.purple,
|
|||
|
|
action: '查看计划',
|
|||
|
|
details: [
|
|||
|
|
'个性化学习路径',
|
|||
|
|
'合理安排学习时间',
|
|||
|
|
'目标导向的学习方案',
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
RecommendationItem(
|
|||
|
|
type: RecommendationType.weakness,
|
|||
|
|
title: '语法薄弱点强化',
|
|||
|
|
description: '针对您在时态方面的薄弱点进行专项训练',
|
|||
|
|
priority: 'low',
|
|||
|
|
estimatedTime: '25分钟',
|
|||
|
|
icon: Icons.trending_up,
|
|||
|
|
color: Colors.red,
|
|||
|
|
action: '开始训练',
|
|||
|
|
details: [
|
|||
|
|
'时态专项练习',
|
|||
|
|
'常见错误纠正',
|
|||
|
|
'实用例句训练',
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Widget build(BuildContext context) {
|
|||
|
|
final isMobile = ResponsiveUtils.isMobile(context);
|
|||
|
|
|
|||
|
|
return Scaffold(
|
|||
|
|
appBar: CustomAppBar(
|
|||
|
|
title: 'AI助手推荐',
|
|||
|
|
),
|
|||
|
|
body: _isLoading
|
|||
|
|
? const LoadingWidget()
|
|||
|
|
: _buildContent(context, isMobile),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Widget _buildContent(BuildContext context, bool isMobile) {
|
|||
|
|
return RefreshIndicator(
|
|||
|
|
onRefresh: _loadRecommendations,
|
|||
|
|
child: SingleChildScrollView(
|
|||
|
|
padding: EdgeInsets.all(isMobile ? 16.0 : 24.0),
|
|||
|
|
child: Column(
|
|||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|||
|
|
children: [
|
|||
|
|
_buildHeader(context, isMobile),
|
|||
|
|
const SizedBox(height: 24),
|
|||
|
|
_buildRecommendationsList(context, isMobile),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Widget _buildHeader(BuildContext context, bool isMobile) {
|
|||
|
|
return Container(
|
|||
|
|
padding: EdgeInsets.all(isMobile ? 20.0 : 24.0),
|
|||
|
|
decoration: BoxDecoration(
|
|||
|
|
gradient: LinearGradient(
|
|||
|
|
colors: [Colors.purple.shade400, Colors.blue.shade500],
|
|||
|
|
begin: Alignment.topLeft,
|
|||
|
|
end: Alignment.bottomRight,
|
|||
|
|
),
|
|||
|
|
borderRadius: BorderRadius.circular(16),
|
|||
|
|
boxShadow: [
|
|||
|
|
BoxShadow(
|
|||
|
|
color: Colors.purple.withOpacity(0.3),
|
|||
|
|
blurRadius: 8,
|
|||
|
|
offset: const Offset(0, 4),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
child: Column(
|
|||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|||
|
|
children: [
|
|||
|
|
Row(
|
|||
|
|
children: [
|
|||
|
|
Container(
|
|||
|
|
padding: const EdgeInsets.all(12),
|
|||
|
|
decoration: BoxDecoration(
|
|||
|
|
color: Colors.white.withOpacity(0.2),
|
|||
|
|
borderRadius: BorderRadius.circular(12),
|
|||
|
|
),
|
|||
|
|
child: Icon(
|
|||
|
|
Icons.psychology,
|
|||
|
|
color: Colors.white,
|
|||
|
|
size: isMobile ? 28 : 32,
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
const SizedBox(width: 16),
|
|||
|
|
Expanded(
|
|||
|
|
child: Column(
|
|||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|||
|
|
children: [
|
|||
|
|
Text(
|
|||
|
|
'AI智能推荐',
|
|||
|
|
style: TextStyle(
|
|||
|
|
color: Colors.white,
|
|||
|
|
fontSize: isMobile ? 20 : 24,
|
|||
|
|
fontWeight: FontWeight.bold,
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
const SizedBox(height: 4),
|
|||
|
|
Text(
|
|||
|
|
'基于您的学习数据智能分析',
|
|||
|
|
style: TextStyle(
|
|||
|
|
color: Colors.white.withOpacity(0.9),
|
|||
|
|
fontSize: isMobile ? 14 : 16,
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
const SizedBox(height: 16),
|
|||
|
|
Container(
|
|||
|
|
padding: const EdgeInsets.all(12),
|
|||
|
|
decoration: BoxDecoration(
|
|||
|
|
color: Colors.white.withOpacity(0.1),
|
|||
|
|
borderRadius: BorderRadius.circular(8),
|
|||
|
|
),
|
|||
|
|
child: Row(
|
|||
|
|
children: [
|
|||
|
|
Icon(
|
|||
|
|
Icons.lightbulb,
|
|||
|
|
color: Colors.yellow.shade300,
|
|||
|
|
size: 20,
|
|||
|
|
),
|
|||
|
|
const SizedBox(width: 8),
|
|||
|
|
Expanded(
|
|||
|
|
child: Text(
|
|||
|
|
'今日为您推荐了 ${_recommendations.length} 项学习内容',
|
|||
|
|
style: TextStyle(
|
|||
|
|
color: Colors.white,
|
|||
|
|
fontSize: isMobile ? 13 : 15,
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Widget _buildRecommendationsList(BuildContext context, bool isMobile) {
|
|||
|
|
return Column(
|
|||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|||
|
|
children: [
|
|||
|
|
Text(
|
|||
|
|
'推荐内容',
|
|||
|
|
style: TextStyle(
|
|||
|
|
fontSize: isMobile ? 18 : 20,
|
|||
|
|
fontWeight: FontWeight.bold,
|
|||
|
|
color: Colors.grey[800],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
const SizedBox(height: 16),
|
|||
|
|
ListView.separated(
|
|||
|
|
shrinkWrap: true,
|
|||
|
|
physics: const NeverScrollableScrollPhysics(),
|
|||
|
|
itemCount: _recommendations.length,
|
|||
|
|
separatorBuilder: (context, index) => const SizedBox(height: 16),
|
|||
|
|
itemBuilder: (context, index) {
|
|||
|
|
return _buildRecommendationCard(
|
|||
|
|
context,
|
|||
|
|
_recommendations[index],
|
|||
|
|
isMobile,
|
|||
|
|
);
|
|||
|
|
},
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Widget _buildRecommendationCard(
|
|||
|
|
BuildContext context,
|
|||
|
|
RecommendationItem item,
|
|||
|
|
bool isMobile,
|
|||
|
|
) {
|
|||
|
|
return Card(
|
|||
|
|
elevation: 3,
|
|||
|
|
shape: RoundedRectangleBorder(
|
|||
|
|
borderRadius: BorderRadius.circular(16),
|
|||
|
|
),
|
|||
|
|
child: InkWell(
|
|||
|
|
onTap: () => _handleRecommendationTap(item),
|
|||
|
|
borderRadius: BorderRadius.circular(16),
|
|||
|
|
child: Padding(
|
|||
|
|
padding: EdgeInsets.all(isMobile ? 16.0 : 20.0),
|
|||
|
|
child: Column(
|
|||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|||
|
|
children: [
|
|||
|
|
Row(
|
|||
|
|
children: [
|
|||
|
|
Container(
|
|||
|
|
padding: const EdgeInsets.all(12),
|
|||
|
|
decoration: BoxDecoration(
|
|||
|
|
color: item.color.withOpacity(0.1),
|
|||
|
|
borderRadius: BorderRadius.circular(12),
|
|||
|
|
),
|
|||
|
|
child: Icon(
|
|||
|
|
item.icon,
|
|||
|
|
color: item.color,
|
|||
|
|
size: isMobile ? 24 : 28,
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
const SizedBox(width: 16),
|
|||
|
|
Expanded(
|
|||
|
|
child: Column(
|
|||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|||
|
|
children: [
|
|||
|
|
Row(
|
|||
|
|
children: [
|
|||
|
|
Expanded(
|
|||
|
|
child: Text(
|
|||
|
|
item.title,
|
|||
|
|
style: TextStyle(
|
|||
|
|
fontSize: isMobile ? 16 : 18,
|
|||
|
|
fontWeight: FontWeight.bold,
|
|||
|
|
color: Colors.grey[800],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
_buildPriorityBadge(item.priority),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
const SizedBox(height: 4),
|
|||
|
|
Row(
|
|||
|
|
children: [
|
|||
|
|
Icon(
|
|||
|
|
Icons.access_time,
|
|||
|
|
size: 16,
|
|||
|
|
color: Colors.grey[600],
|
|||
|
|
),
|
|||
|
|
const SizedBox(width: 4),
|
|||
|
|
Text(
|
|||
|
|
item.estimatedTime,
|
|||
|
|
style: TextStyle(
|
|||
|
|
fontSize: 14,
|
|||
|
|
color: Colors.grey[600],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
const SizedBox(height: 16),
|
|||
|
|
Text(
|
|||
|
|
item.description,
|
|||
|
|
style: TextStyle(
|
|||
|
|
fontSize: isMobile ? 14 : 16,
|
|||
|
|
color: Colors.grey[700],
|
|||
|
|
height: 1.4,
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
const SizedBox(height: 16),
|
|||
|
|
ExpansionTile(
|
|||
|
|
title: const Text(
|
|||
|
|
'查看详情',
|
|||
|
|
style: TextStyle(
|
|||
|
|
fontSize: 14,
|
|||
|
|
fontWeight: FontWeight.w500,
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
tilePadding: EdgeInsets.zero,
|
|||
|
|
childrenPadding: const EdgeInsets.only(top: 8),
|
|||
|
|
children: [
|
|||
|
|
...item.details.map((detail) => Padding(
|
|||
|
|
padding: const EdgeInsets.only(bottom: 4),
|
|||
|
|
child: Row(
|
|||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|||
|
|
children: [
|
|||
|
|
Container(
|
|||
|
|
margin: const EdgeInsets.only(top: 6),
|
|||
|
|
width: 4,
|
|||
|
|
height: 4,
|
|||
|
|
decoration: BoxDecoration(
|
|||
|
|
color: item.color,
|
|||
|
|
shape: BoxShape.circle,
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
const SizedBox(width: 8),
|
|||
|
|
Expanded(
|
|||
|
|
child: Text(
|
|||
|
|
detail,
|
|||
|
|
style: TextStyle(
|
|||
|
|
fontSize: 13,
|
|||
|
|
color: Colors.grey[600],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
)),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
const SizedBox(height: 16),
|
|||
|
|
SizedBox(
|
|||
|
|
width: double.infinity,
|
|||
|
|
child: ElevatedButton(
|
|||
|
|
onPressed: () => _handleRecommendationTap(item),
|
|||
|
|
style: ElevatedButton.styleFrom(
|
|||
|
|
backgroundColor: item.color,
|
|||
|
|
foregroundColor: Colors.white,
|
|||
|
|
padding: EdgeInsets.symmetric(
|
|||
|
|
vertical: isMobile ? 12 : 16,
|
|||
|
|
),
|
|||
|
|
shape: RoundedRectangleBorder(
|
|||
|
|
borderRadius: BorderRadius.circular(12),
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
child: Text(
|
|||
|
|
item.action,
|
|||
|
|
style: TextStyle(
|
|||
|
|
fontSize: isMobile ? 14 : 16,
|
|||
|
|
fontWeight: FontWeight.w600,
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Widget _buildPriorityBadge(String priority) {
|
|||
|
|
Color color;
|
|||
|
|
String text;
|
|||
|
|
|
|||
|
|
switch (priority) {
|
|||
|
|
case 'high':
|
|||
|
|
color = Colors.red;
|
|||
|
|
text = '高优先级';
|
|||
|
|
break;
|
|||
|
|
case 'medium':
|
|||
|
|
color = Colors.orange;
|
|||
|
|
text = '中优先级';
|
|||
|
|
break;
|
|||
|
|
case 'low':
|
|||
|
|
color = Colors.green;
|
|||
|
|
text = '低优先级';
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
color = Colors.grey;
|
|||
|
|
text = '普通';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return Container(
|
|||
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|||
|
|
decoration: BoxDecoration(
|
|||
|
|
color: color.withOpacity(0.1),
|
|||
|
|
borderRadius: BorderRadius.circular(12),
|
|||
|
|
border: Border.all(color: color, width: 1),
|
|||
|
|
),
|
|||
|
|
child: Text(
|
|||
|
|
text,
|
|||
|
|
style: TextStyle(
|
|||
|
|
fontSize: 12,
|
|||
|
|
color: color,
|
|||
|
|
fontWeight: FontWeight.w500,
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void _handleRecommendationTap(RecommendationItem item) {
|
|||
|
|
switch (item.type) {
|
|||
|
|
case RecommendationType.vocabulary:
|
|||
|
|
Navigator.of(context).pushNamed('/vocabulary/daily-words');
|
|||
|
|
break;
|
|||
|
|
case RecommendationType.review:
|
|||
|
|
Navigator.of(context).pushNamed('/vocabulary/review');
|
|||
|
|
break;
|
|||
|
|
case RecommendationType.test:
|
|||
|
|
Navigator.of(context).pushNamed('/vocabulary/test');
|
|||
|
|
break;
|
|||
|
|
case RecommendationType.plan:
|
|||
|
|
Navigator.of(context).pushNamed('/vocabulary/study-plan');
|
|||
|
|
break;
|
|||
|
|
case RecommendationType.weakness:
|
|||
|
|
// 处理薄弱点强化
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
enum RecommendationType {
|
|||
|
|
vocabulary,
|
|||
|
|
review,
|
|||
|
|
test,
|
|||
|
|
plan,
|
|||
|
|
weakness,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class RecommendationItem {
|
|||
|
|
final RecommendationType type;
|
|||
|
|
final String title;
|
|||
|
|
final String description;
|
|||
|
|
final String priority;
|
|||
|
|
final String estimatedTime;
|
|||
|
|
final IconData icon;
|
|||
|
|
final Color color;
|
|||
|
|
final String action;
|
|||
|
|
final List<String> details;
|
|||
|
|
|
|||
|
|
const RecommendationItem({
|
|||
|
|
required this.type,
|
|||
|
|
required this.title,
|
|||
|
|
required this.description,
|
|||
|
|
required this.priority,
|
|||
|
|
required this.estimatedTime,
|
|||
|
|
required this.icon,
|
|||
|
|
required this.color,
|
|||
|
|
required this.action,
|
|||
|
|
required this.details,
|
|||
|
|
});
|
|||
|
|
}
|