在这里插入图片描述

前言

在三国杀游戏中,武将之间的配合往往比单个武将的强度更重要。一个优秀的武将配合推荐系统能够帮助玩家发现强力组合,提升游戏体验和胜率。本文将详细介绍如何在Flutter中实现一个智能的武将配合推荐系统,包括配合算法设计、推荐引擎和可视化展示等功能。

我们将使用机器学习算法分析武将技能协同效应,通过协同过滤和内容推荐相结合的方式,为用户提供个性化的武将配合建议。同时结合GetX状态管理和精美的UI设计,打造一个既智能又易用的推荐系统,并确保在OpenHarmony系统上的完美适配。

功能需求

武将配合推荐功能需要满足以下核心需求:

  1. 智能推荐:基于武将技能和历史数据推荐最佳配合
  2. 配合分析:分析武将间的协同效应和配合强度
  3. 个性化推荐:根据用户偏好和游戏风格定制推荐
  4. 配合评分:对武将组合进行量化评分和排序
  5. 实战验证:提供配合组合的实战数据和胜率统计

技术实现

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

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐