Flutter for OpenHarmony三国杀攻略App实战 - 武将配合推荐实现
在三国杀游戏中,武将之间的配合往往比单个武将的强度更重要。一个优秀的武将配合推荐系统能够帮助玩家发现强力组合,提升游戏体验和胜率。本文将详细介绍如何在Flutter中实现一个智能的武将配合推荐系统,包括配合算法设计、推荐引擎和可视化展示等功能。我们将使用机器学习算法分析武将技能协同效应,通过协同过滤和内容推荐相结合的方式,为用户提供个性化的武将配合建议。同时结合GetX状态管理和精美的UI设计,打
·
前言
在三国杀游戏中,武将之间的配合往往比单个武将的强度更重要。一个优秀的武将配合推荐系统能够帮助玩家发现强力组合,提升游戏体验和胜率。本文将详细介绍如何在Flutter中实现一个智能的武将配合推荐系统,包括配合算法设计、推荐引擎和可视化展示等功能。
我们将使用机器学习算法分析武将技能协同效应,通过协同过滤和内容推荐相结合的方式,为用户提供个性化的武将配合建议。同时结合GetX状态管理和精美的UI设计,打造一个既智能又易用的推荐系统,并确保在OpenHarmony系统上的完美适配。
功能需求
武将配合推荐功能需要满足以下核心需求:
- 智能推荐:基于武将技能和历史数据推荐最佳配合
- 配合分析:分析武将间的协同效应和配合强度
- 个性化推荐:根据用户偏好和游戏风格定制推荐
- 配合评分:对武将组合进行量化评分和排序
- 实战验证:提供配合组合的实战数据和胜率统计
技术实现
1. 配合推荐数据模型
首先定义配合推荐相关的数据结构:
// lib/models/hero_combo_model.dart
class HeroComboModel {
final String id;
final List<String> heroIds;
final List<HeroComboMember> members;
final double synergy;
final double winRate;
final int popularity;
final List<ComboTag> tags;
final String description;
final List<ComboStrategy> strategies;
final Map<String, double> strengthAnalysis;
final DateTime lastUpdated;
HeroComboModel({
required this.id,
required this.heroIds,
required this.members,
required this.synergy,
required this.winRate,
required this.popularity,
required this.tags,
required this.description,
required this.strategies,
required this.strengthAnalysis,
required this.lastUpdated,
});
factory HeroComboModel.fromJson(Map<String, dynamic> json) {
return HeroComboModel(
id: json['id'] ?? '',
heroIds: List<String>.from(json['heroIds'] ?? []),
members: (json['members'] as List?)
?.map((e) => HeroComboMember.fromJson(e))
.toList() ?? [],
synergy: (json['synergy'] ?? 0.0).toDouble(),
winRate: (json['winRate'] ?? 0.0).toDouble(),
popularity: json['popularity'] ?? 0,
tags: (json['tags'] as List?)
?.map((e) => ComboTag.fromJson(e))
.toList() ?? [],
description: json['description'] ?? '',
strategies: (json['strategies'] as List?)
?.map((e) => ComboStrategy.fromJson(e))
.toList() ?? [],
strengthAnalysis: Map<String, double>.from(json['strengthAnalysis'] ?? {}),
lastUpdated: DateTime.parse(json['lastUpdated'] ?? DateTime.now().toIso8601String()),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'heroIds': heroIds,
'members': members.map((e) => e.toJson()).toList(),
'synergy': synergy,
'winRate': winRate,
'popularity': popularity,
'tags': tags.map((e) => e.toJson()).toList(),
'description': description,
'strategies': strategies.map((e) => e.toJson()).toList(),
'strengthAnalysis': strengthAnalysis,
'lastUpdated': lastUpdated.toIso8601String(),
};
}
}
class HeroComboMember {
final String heroId;
final String heroName;
final String heroAvatar;
final String faction;
final String role;
final double contribution;
final List<String> keySkills;
HeroComboMember({
required this.heroId,
required this.heroName,
required this.heroAvatar,
required this.faction,
required this.role,
required this.contribution,
required this.keySkills,
});
factory HeroComboMember.fromJson(Map<String, dynamic> json) {
return HeroComboMember(
heroId: json['heroId'] ?? '',
heroName: json['heroName'] ?? '',
heroAvatar: json['heroAvatar'] ?? '',
faction: json['faction'] ?? '',
role: json['role'] ?? '',
contribution: (json['contribution'] ?? 0.0).toDouble(),
keySkills: List<String>.from(json['keySkills'] ?? []),
);
}
Map<String, dynamic> toJson() {
return {
'heroId': heroId,
'heroName': heroName,
'heroAvatar': heroAvatar,
'faction': faction,
'role': role,
'contribution': contribution,
'keySkills': keySkills,
};
}
}
class ComboTag {
final String name;
final String color;
final String description;
ComboTag({
required this.name,
required this.color,
required this.description,
});
factory ComboTag.fromJson(Map<String, dynamic> json) {
return ComboTag(
name: json['name'] ?? '',
color: json['color'] ?? '',
description: json['description'] ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'color': color,
'description': description,
};
}
}
class ComboStrategy {
final String title;
final String description;
final List<String> steps;
final String difficulty;
ComboStrategy({
required this.title,
required this.description,
required this.steps,
required this.difficulty,
});
factory ComboStrategy.fromJson(Map<String, dynamic> json) {
return ComboStrategy(
title: json['title'] ?? '',
description: json['description'] ?? '',
steps: List<String>.from(json['steps'] ?? []),
difficulty: json['difficulty'] ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'title': title,
'description': description,
'steps': steps,
'difficulty': difficulty,
};
}
}
class RecommendationRequest {
final List<String> selectedHeroes;
final String gameMode;
final String difficulty;
final List<String> preferredTags;
final String userLevel;
RecommendationRequest({
required this.selectedHeroes,
required this.gameMode,
required this.difficulty,
required this.preferredTags,
required this.userLevel,
});
Map<String, dynamic> toJson() {
return {
'selectedHeroes': selectedHeroes,
'gameMode': gameMode,
'difficulty': difficulty,
'preferredTags': preferredTags,
'userLevel': userLevel,
};
}
}
2. 配合推荐控制器
// lib/controllers/hero_combo_controller.dart
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import '../models/hero_combo_model.dart';
import '../models/hero_model.dart';
import '../services/combo_service.dart';
import '../services/recommendation_service.dart';
class HeroComboController extends GetxController {
final ComboService _comboService = Get.find<ComboService>();
final RecommendationService _recommendationService = Get.find<RecommendationService>();
// 响应式变量
final RxList<HeroComboModel> allCombos = <HeroComboModel>[].obs;
final RxList<HeroComboModel> recommendedCombos = <HeroComboModel>[].obs;
final RxList<HeroComboModel> filteredCombos = <HeroComboModel>[].obs;
final RxList<HeroModel> selectedHeroes = <HeroModel>[].obs;
final RxList<HeroModel> availableHeroes = <HeroModel>[].obs;
final Rx<HeroComboModel?> selectedCombo = Rx<HeroComboModel?>(null);
final RxBool isLoading = false.obs;
final RxBool isRecommending = false.obs;
// 筛选条件
final RxString selectedGameMode = 'identity'.obs;
final RxString selectedDifficulty = 'all'.obs;
final RxString sortBy = 'synergy'.obs;
final RxList<String> selectedTags = <String>[].obs;
final RxDouble minSynergy = 0.0.obs;
final RxDouble minWinRate = 0.0.obs;
// 推荐参数
final RxString userLevel = 'intermediate'.obs;
final RxInt maxRecommendations = 10.obs;
// 可选项
final List<String> gameModes = ['identity', 'duel', 'team', 'custom'];
final List<String> difficulties = ['all', 'easy', 'medium', 'hard'];
final List<String> sortOptions = ['synergy', 'winRate', 'popularity'];
final List<String> userLevels = ['beginner', 'intermediate', 'advanced', 'expert'];
void onInit() {
super.onInit();
loadComboData();
loadAvailableHeroes();
}
/// 加载配合数据
Future<void> loadComboData() async {
try {
isLoading.value = true;
final combos = await _comboService.getAllCombos();
allCombos.assignAll(combos);
_applyFilters();
} catch (e) {
Get.snackbar('错误', '加载配合数据失败: $e');
} finally {
isLoading.value = false;
}
}
/// 加载可选武将
Future<void> loadAvailableHeroes() async {
try {
final heroes = await _comboService.getAvailableHeroes();
availableHeroes.assignAll(heroes);
} catch (e) {
Get.snackbar('错误', '加载武将数据失败: $e');
}
}
/// 获取智能推荐
Future<void> getRecommendations() async {
try {
isRecommending.value = true;
final request = RecommendationRequest(
selectedHeroes: selectedHeroes.map((h) => h.id).toList(),
gameMode: selectedGameMode.value,
difficulty: selectedDifficulty.value,
preferredTags: selectedTags,
userLevel: userLevel.value,
);
final recommendations = await _recommendationService.getRecommendations(request);
recommendedCombos.assignAll(recommendations);
Get.snackbar('成功', '已为您推荐 ${recommendations.length} 个配合组合');
} catch (e) {
Get.snackbar('错误', '获取推荐失败: $e');
} finally {
isRecommending.value = false;
}
}
/// 添加武将到选择列表
void addHero(HeroModel hero) {
if (!selectedHeroes.any((h) => h.id == hero.id)) {
selectedHeroes.add(hero);
_updateRecommendations();
}
}
/// 移除选中的武将
void removeHero(String heroId) {
selectedHeroes.removeWhere((h) => h.id == heroId);
_updateRecommendations();
}
/// 清空选中的武将
void clearSelectedHeroes() {
selectedHeroes.clear();
recommendedCombos.clear();
}
/// 更新推荐
void _updateRecommendations() {
if (selectedHeroes.isNotEmpty) {
getRecommendations();
} else {
recommendedCombos.clear();
}
}
/// 应用筛选条件
void _applyFilters() {
List<HeroComboModel> filtered = List.from(allCombos);
// 按游戏模式筛选
if (selectedGameMode.value != 'all') {
filtered = filtered.where((combo) {
return combo.tags.any((tag) => tag.name == selectedGameMode.value);
}).toList();
}
// 按难度筛选
if (selectedDifficulty.value != 'all') {
filtered = filtered.where((combo) {
return combo.strategies.any((strategy) => strategy.difficulty == selectedDifficulty.value);
}).toList();
}
// 按标签筛选
if (selectedTags.isNotEmpty) {
filtered = filtered.where((combo) {
return selectedTags.every((tag) => combo.tags.any((t) => t.name == tag));
}).toList();
}
// 按协同度筛选
filtered = filtered.where((combo) => combo.synergy >= minSynergy.value).toList();
// 按胜率筛选
filtered = filtered.where((combo) => combo.winRate >= minWinRate.value).toList();
// 排序
_sortCombos(filtered);
filteredCombos.assignAll(filtered);
}
/// 排序配合组合
void _sortCombos(List<HeroComboModel> combos) {
switch (sortBy.value) {
case 'synergy':
combos.sort((a, b) => b.synergy.compareTo(a.synergy));
break;
case 'winRate':
combos.sort((a, b) => b.winRate.compareTo(a.winRate));
break;
case 'popularity':
combos.sort((a, b) => b.popularity.compareTo(a.popularity));
break;
}
}
/// 设置游戏模式
void setGameMode(String mode) {
selectedGameMode.value = mode;
_applyFilters();
}
/// 设置难度
void setDifficulty(String difficulty) {
selectedDifficulty.value = difficulty;
_applyFilters();
}
/// 设置排序方式
void setSortBy(String sort) {
sortBy.value = sort;
_applyFilters();
}
/// 切换标签选择
void toggleTag(String tag) {
if (selectedTags.contains(tag)) {
selectedTags.remove(tag);
} else {
selectedTags.add(tag);
}
_applyFilters();
}
/// 设置协同度筛选
void setSynergyFilter(double synergy) {
minSynergy.value = synergy;
_applyFilters();
}
/// 设置胜率筛选
void setWinRateFilter(double winRate) {
minWinRate.value = winRate;
_applyFilters();
}
/// 选择配合组合
void selectCombo(HeroComboModel combo) {
selectedCombo.value = combo;
}
/// 分析配合强度
Map<String, dynamic> analyzeComboStrength(HeroComboModel combo) {
final analysis = <String, dynamic>{};
// 计算各维度得分
analysis['offense'] = combo.strengthAnalysis['offense'] ?? 0.0;
analysis['defense'] = combo.strengthAnalysis['defense'] ?? 0.0;
analysis['control'] = combo.strengthAnalysis['control'] ?? 0.0;
analysis['support'] = combo.strengthAnalysis['support'] ?? 0.0;
analysis['synergy'] = combo.synergy;
// 计算总体评级
final totalScore = (analysis['offense'] + analysis['defense'] +
analysis['control'] + analysis['support']) / 4.0;
if (totalScore >= 90) {
analysis['rating'] = 'S';
analysis['ratingColor'] = Colors.red;
} else if (totalScore >= 80) {
analysis['rating'] = 'A';
analysis['ratingColor'] = Colors.orange;
} else if (totalScore >= 70) {
analysis['rating'] = 'B';
analysis['ratingColor'] = Colors.yellow;
} else if (totalScore >= 60) {
analysis['rating'] = 'C';
analysis['ratingColor'] = Colors.green;
} else {
analysis['rating'] = 'D';
analysis['ratingColor'] = Colors.grey;
}
return analysis;
}
/// 获取配合建议
String getComboAdvice(HeroComboModel combo) {
final analysis = analyzeComboStrength(combo);
final rating = analysis['rating'];
switch (rating) {
case 'S':
return '顶级配合,适合竞技环境使用';
case 'A':
return '优秀配合,推荐在排位赛中使用';
case 'B':
return '良好配合,适合休闲游戏';
case 'C':
return '一般配合,需要熟练操作';
case 'D':
return '较弱配合,不建议使用';
default:
return '配合效果未知';
}
}
/// 收藏配合组合
Future<void> favoriteCombo(String comboId) async {
try {
await _comboService.favoriteCombo(comboId);
Get.snackbar('成功', '已收藏该配合组合');
} catch (e) {
Get.snackbar('错误', '收藏失败: $e');
}
}
/// 评价配合组合
Future<void> rateCombo(String comboId, double rating) async {
try {
await _comboService.rateCombo(comboId, rating);
Get.snackbar('成功', '评价已提交');
await loadComboData(); // 刷新数据
} catch (e) {
Get.snackbar('错误', '评价失败: $e');
}
}
/// 获取相似配合
List<HeroComboModel> getSimilarCombos(HeroComboModel combo) {
return allCombos.where((c) {
if (c.id == combo.id) return false;
// 计算相似度
final commonHeroes = c.heroIds.where((id) => combo.heroIds.contains(id)).length;
final commonTags = c.tags.where((tag) => combo.tags.any((t) => t.name == tag.name)).length;
return commonHeroes >= 1 || commonTags >= 2;
}).take(3).toList();
}
}
3. 配合推荐界面实现
// lib/screens/heroes/hero_combo_screen.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../controllers/hero_combo_controller.dart';
import '../../widgets/combo_card.dart';
import '../../widgets/hero_selector.dart';
import '../../widgets/combo_analysis_chart.dart';
class HeroComboScreen extends StatelessWidget {
const HeroComboScreen({Key? key}) : super(key: key);
Widget build(BuildContext context) {
final controller = Get.put(HeroComboController());
return Scaffold(
appBar: AppBar(
title: const Text('武将配合推荐'),
backgroundColor: Colors.red[700],
foregroundColor: Colors.white,
elevation: 0,
actions: [
IconButton(
icon: const Icon(Icons.filter_list),
onPressed: () => _showFilterDialog(controller),
),
],
),
body: Column(
children: [
_buildHeroSelector(controller),
_buildFilterTabs(controller),
Expanded(
child: _buildContent(controller),
),
],
),
floatingActionButton: Obx(() => controller.selectedHeroes.isNotEmpty
? FloatingActionButton.extended(
onPressed: controller.getRecommendations,
backgroundColor: Colors.red[700],
icon: controller.isRecommending.value
? SizedBox(
width: 16.w,
height: 16.w,
child: const CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: const Icon(Icons.auto_awesome),
label: Text(controller.isRecommending.value ? '推荐中...' : '智能推荐'),
)
: const SizedBox()),
);
}
/// 构建武将选择器
Widget _buildHeroSelector(HeroComboController controller) {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.red[700],
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(20.r),
bottomRight: Radius.circular(20.r),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'选择武将 (${controller.selectedHeroes.length}/5)',
style: TextStyle(
color: Colors.white,
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 12.h),
_buildSelectedHeroes(controller),
SizedBox(height: 12.h),
_buildHeroSelectorButton(controller),
],
),
);
}
/// 构建已选武将
Widget _buildSelectedHeroes(HeroComboController controller) {
return Obx(() => SizedBox(
height: 60.h,
child: controller.selectedHeroes.isEmpty
? Center(
child: Text(
'点击下方按钮选择武将',
style: TextStyle(
color: Colors.white.withOpacity(0.7),
fontSize: 14.sp,
),
),
)
: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: controller.selectedHeroes.length,
itemBuilder: (context, index) {
final hero = controller.selectedHeroes[index];
return Padding(
padding: EdgeInsets.only(right: 8.w),
child: Stack(
children: [
CircleAvatar(
radius: 25.r,
backgroundImage: NetworkImage(hero.avatar),
),
Positioned(
top: 0,
right: 0,
child: GestureDetector(
onTap: () => controller.removeHero(hero.id),
child: Container(
width: 16.w,
height: 16.w,
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
color: Colors.white,
size: 12.w,
),
),
),
),
],
),
);
},
),
));
}
/// 构建武将选择按钮
Widget _buildHeroSelectorButton(HeroComboController controller) {
return Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: () => _showHeroSelector(controller),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.red[700],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
),
icon: const Icon(Icons.add),
label: const Text('选择武将'),
),
),
if (controller.selectedHeroes.isNotEmpty) ...[
SizedBox(width: 8.w),
ElevatedButton(
onPressed: controller.clearSelectedHeroes,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white.withOpacity(0.2),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
),
child: const Text('清空'),
),
],
],
);
}
/// 构建筛选标签
Widget _buildFilterTabs(HeroComboController controller) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
child: Row(
children: [
Text(
'游戏模式:',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w500,
),
),
SizedBox(width: 8.w),
Expanded(
child: Obx(() => SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: controller.gameModes.map((mode) {
final isSelected = controller.selectedGameMode.value == mode;
return Padding(
padding: EdgeInsets.only(right: 8.w),
child: FilterChip(
label: Text(_getGameModeName(mode)),
selected: isSelected,
onSelected: (_) => controller.setGameMode(mode),
selectedColor: Colors.red[700],
checkmarkColor: Colors.white,
),
);
}).toList(),
),
)),
),
],
),
);
}
/// 构建内容区域
Widget _buildContent(HeroComboController controller) {
return Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
if (controller.recommendedCombos.isNotEmpty) {
return _buildRecommendedCombos(controller);
}
if (controller.filteredCombos.isNotEmpty) {
return _buildAllCombos(controller);
}
return _buildEmptyState();
});
}
/// 构建推荐配合
Widget _buildRecommendedCombos(HeroComboController controller) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16.w),
child: Row(
children: [
Icon(
Icons.auto_awesome,
color: Colors.red[700],
size: 20.w,
),
SizedBox(width: 8.w),
Text(
'为您推荐',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
TextButton(
onPressed: () => controller.recommendedCombos.clear(),
child: const Text('查看全部'),
),
],
),
),
Expanded(
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 16.w),
itemCount: controller.recommendedCombos.length,
itemBuilder: (context, index) {
final combo = controller.recommendedCombos[index];
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: ComboCard(
combo: combo,
onTap: () => _showComboDetail(combo, controller),
showRecommendationBadge: true,
),
);
},
),
),
],
);
}
/// 构建所有配合
Widget _buildAllCombos(HeroComboController controller) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16.w),
child: Row(
children: [
Text(
'热门配合 (${controller.filteredCombos.length})',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
DropdownButton<String>(
value: controller.sortBy.value,
items: const [
DropdownMenuItem(value: 'synergy', child: Text('协同度')),
DropdownMenuItem(value: 'winRate', child: Text('胜率')),
DropdownMenuItem(value: 'popularity', child: Text('人气')),
],
onChanged: (value) {
if (value != null) {
controller.setSortBy(value);
}
},
underline: const SizedBox(),
),
],
),
),
Expanded(
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 16.w),
itemCount: controller.filteredCombos.length,
itemBuilder: (context, index) {
final combo = controller.filteredCombos[index];
return Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: ComboCard(
combo: combo,
onTap: () => _showComboDetail(combo, controller),
),
);
},
),
),
],
);
}
/// 构建空状态
Widget _buildEmptyState() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.group,
size: 64.w,
color: Colors.grey[400],
),
SizedBox(height: 16.h),
Text(
'暂无配合数据',
style: TextStyle(
fontSize: 16.sp,
color: Colors.grey[600],
),
),
SizedBox(height: 8.h),
Text(
'选择武将获取智能推荐',
style: TextStyle(
fontSize: 14.sp,
color: Colors.grey[500],
),
),
],
),
);
}
/// 显示武将选择器
void _showHeroSelector(HeroComboController controller) {
showModalBottomSheet(
context: Get.context!,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => HeroSelector(
availableHeroes: controller.availableHeroes,
selectedHeroes: controller.selectedHeroes,
onHeroSelected: controller.addHero,
maxSelection: 5,
),
);
}
/// 显示配合详情
void _showComboDetail(HeroComboModel combo, HeroComboController controller) {
showModalBottomSheet(
context: Get.context!,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => _buildComboDetailSheet(combo, controller),
);
}
/// 构建配合详情面板
Widget _buildComboDetailSheet(HeroComboModel combo, HeroComboController controller) {
return Container(
height: 0.9.sh,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.r),
topRight: Radius.circular(20.r),
),
),
child: Column(
children: [
_buildComboDetailHeader(combo, controller),
Expanded(
child: SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildComboMembers(combo),
SizedBox(height: 20.h),
_buildComboAnalysis(combo, controller),
SizedBox(height: 20.h),
_buildComboStrategies(combo),
SizedBox(height: 20.h),
_buildSimilarCombos(combo, controller),
],
),
),
),
],
),
);
}
/// 构建配合详情头部
Widget _buildComboDetailHeader(HeroComboModel combo, HeroComboController controller) {
final analysis = controller.analyzeComboStrength(combo);
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.red[700],
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.r),
topRight: Radius.circular(20.r),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
'${combo.members.map((m) => m.heroName).join(' + ')}',
style: TextStyle(
color: Colors.white,
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
decoration: BoxDecoration(
color: analysis['ratingColor'],
borderRadius: BorderRadius.circular(8.r),
),
child: Text(
analysis['rating'],
style: TextStyle(
color: Colors.white,
fontSize: 14.sp,
fontWeight: FontWeight.bold,
),
),
),
],
),
SizedBox(height: 8.h),
Text(
combo.description,
style: TextStyle(
color: Colors.white.withOpacity(0.9),
fontSize: 14.sp,
),
),
SizedBox(height: 12.h),
Row(
children: [
_buildStatItem('协同度', '${combo.synergy.toInt()}%'),
SizedBox(width: 16.w),
_buildStatItem('胜率', '${combo.winRate.toInt()}%'),
SizedBox(width: 16.w),
_buildStatItem('人气', '${combo.popularity}'),
],
),
],
),
);
}
/// 构建统计项
Widget _buildStatItem(String label, String value) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
color: Colors.white.withOpacity(0.7),
fontSize: 12.sp,
),
),
Text(
value,
style: TextStyle(
color: Colors.white,
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
],
);
}
/// 构建配合成员
Widget _buildComboMembers(HeroComboModel combo) {
return Card(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'配合成员',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 12.h),
...combo.members.map((member) => Padding(
padding: EdgeInsets.only(bottom: 12.h),
child: Row(
children: [
CircleAvatar(
radius: 20.r,
backgroundImage: NetworkImage(member.heroAvatar),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
member.heroName,
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w500,
),
),
Text(
'${member.role} • 贡献度: ${member.contribution.toInt()}%',
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey[600],
),
),
],
),
),
Wrap(
spacing: 4.w,
children: member.keySkills.take(2).map((skill) => Chip(
label: Text(
skill,
style: TextStyle(fontSize: 10.sp),
),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
)).toList(),
),
],
),
)),
],
),
),
);
}
/// 构建配合分析
Widget _buildComboAnalysis(HeroComboModel combo, HeroComboController controller) {
return Card(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'强度分析',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 12.h),
SizedBox(
height: 200.h,
child: ComboAnalysisChart(
combo: combo,
analysis: controller.analyzeComboStrength(combo),
),
),
SizedBox(height: 12.h),
Text(
controller.getComboAdvice(combo),
style: TextStyle(
fontSize: 14.sp,
color: Colors.grey[700],
),
),
],
),
),
);
}
/// 构建配合策略
Widget _buildComboStrategies(HeroComboModel combo) {
return Card(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'使用策略',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 12.h),
...combo.strategies.map((strategy) => ExpansionTile(
title: Text(
strategy.title,
style: TextStyle(fontSize: 14.sp),
),
subtitle: Text(
'难度: ${strategy.difficulty}',
style: TextStyle(fontSize: 12.sp),
),
children: [
Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
strategy.description,
style: TextStyle(fontSize: 14.sp),
),
SizedBox(height: 8.h),
...strategy.steps.asMap().entries.map((entry) => Padding(
padding: EdgeInsets.only(bottom: 4.h),
child: Text(
'${entry.key + 1}. ${entry.value}',
style: TextStyle(fontSize: 12.sp),
),
)),
],
),
),
],
)),
],
),
),
);
}
/// 构建相似配合
Widget _buildSimilarCombos(HeroComboModel combo, HeroComboController controller) {
final similarCombos = controller.getSimilarCombos(combo);
if (similarCombos.isEmpty) return const SizedBox();
return Card(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'相似配合',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 12.h),
SizedBox(
height: 120.h,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: similarCombos.length,
itemBuilder: (context, index) {
final similarCombo = similarCombos[index];
return Padding(
padding: EdgeInsets.only(right: 12.w),
child: GestureDetector(
onTap: () => _showComboDetail(similarCombo, controller),
child: Container(
width: 200.w,
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey[300]!),
borderRadius: BorderRadius.circular(8.r),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
similarCombo.members.map((m) => m.heroName).join(' + '),
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w500,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 4.h),
Text(
'协同度: ${similarCombo.synergy.toInt()}%',
style: TextStyle(
fontSize: 10.sp,
color: Colors.grey[600],
),
),
],
),
),
),
);
},
),
),
],
),
),
);
}
/// 显示筛选对话框
void _showFilterDialog(HeroComboController controller) {
Get.dialog(
AlertDialog(
title: const Text('筛选条件'),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 协同度筛选
Text('最低协同度: ${controller.minSynergy.value.toInt()}%'),
Obx(() => Slider(
value: controller.minSynergy.value,
min: 0,
max: 100,
divisions: 10,
onChanged: controller.setSynergyFilter,
)),
// 胜率筛选
Text('最低胜率: ${controller.minWinRate.value.toInt()}%'),
Obx(() => Slider(
value: controller.minWinRate.value,
min: 0,
max: 100,
divisions: 10,
onChanged: controller.setWinRateFilter,
)),
],
),
),
actions: [
TextButton(
onPressed: () => Get.back(),
child: const Text('确定'),
),
],
),
);
}
/// 获取游戏模式名称
String _getGameModeName(String mode) {
switch (mode) {
case 'identity':
return '身份局';
case 'duel':
return '对决';
case 'team':
return '团队';
case 'custom':
return '自定义';
default:
return mode;
}
}
}
性能优化
1. 推荐算法优化
// 使用缓存和预计算优化推荐性能
class RecommendationCache {
static final Map<String, List<HeroComboModel>> _cache = {};
static const Duration cacheExpiry = Duration(hours: 1);
static String _generateKey(RecommendationRequest request) {
return '${request.selectedHeroes.join(',')}_${request.gameMode}_${request.difficulty}';
}
static List<HeroComboModel>? getRecommendations(RecommendationRequest request) {
final key = _generateKey(request);
return _cache[key];
}
static void setRecommendations(RecommendationRequest request, List<HeroComboModel> recommendations) {
final key = _generateKey(request);
_cache[key] = recommendations;
}
}
2. UI渲染优化
// 使用懒加载优化长列表性能
class LazyComboList extends StatelessWidget {
final List<HeroComboModel> combos;
final Function(HeroComboModel) onTap;
Widget build(BuildContext context) {
return ListView.builder(
itemCount: combos.length,
itemExtent: 120.h, // 固定高度提升性能
cacheExtent: 1000.h, // 预缓存范围
itemBuilder: (context, index) {
return ComboCard(
combo: combos[index],
onTap: () => onTap(combos[index]),
);
},
);
}
}
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)