Flutter 框架跨平台鸿蒙开发 - 虚拟红包雨应用开发教程

Flutter虚拟红包雨应用开发教程

项目简介

虚拟红包雨是一款有趣的休闲游戏应用,玩家需要在限定时间内点击屏幕收集从天而降的红包来获得金币。应用包含四种不同稀有度的红包、精美的动画效果、详细的游戏记录和统计分析功能。
运行效果图





主要功能

  • 红包雨游戏:60秒限时收集红包
  • 四种稀有度:普通、稀有、史诗、传说红包
  • 动画效果:红包掉落和收集动画
  • 游戏记录:详细的历史游戏记录
  • 统计分析:全面的数据统计和成就系统
  • 设置管理:游戏设置和数据管理

技术架构

核心技术栈

  • Flutter框架:跨平台UI开发
  • Dart语言:应用逻辑实现
  • Animation系统:动画效果实现
  • Timer机制:游戏计时和红包生成
  • State管理:应用状态管理

项目结构

复制代码
lib/
├── main.dart                 # 应用入口和主要逻辑
├── models/                   # 数据模型
│   ├── red_packet.dart      # 红包模型
│   ├── game_session.dart    # 游戏会话模型
│   └── user_stats.dart      # 用户统计模型
└── widgets/                  # 自定义组件
    ├── game_area.dart       # 游戏区域组件
    ├── red_packet_widget.dart # 红包组件
    └── stats_card.dart      # 统计卡片组件

数据模型设计

红包模型

dart 复制代码
class RedPacket {
  final String id;
  double x;
  double y;
  final double size;
  final double speed;
  final int amount;
  final String type;
  bool isCollected;
  bool isAnimating;
  final Color color;

  RedPacket({
    required this.id,
    required this.x,
    required this.y,
    required this.size,
    required this.speed,
    required this.amount,
    required this.type,
    this.isCollected = false,
    this.isAnimating = false,
    required this.color,
  });

  RedPacket copyWith({
    double? x,
    double? y,
    bool? isCollected,
    bool? isAnimating,
  }) {
    return RedPacket(
      id: id,
      x: x ?? this.x,
      y: y ?? this.y,
      size: size,
      speed: speed,
      amount: amount,
      type: type,
      isCollected: isCollected ?? this.isCollected,
      isAnimating: isAnimating ?? this.isAnimating,
      color: color,
    );
  }
}

游戏会话模型

dart 复制代码
class GameSession {
  final String id;
  final DateTime startTime;
  DateTime? endTime;
  int totalAmount;
  int redPacketsCollected;
  int duration;
  Map<String, int> typeCount;

  GameSession({
    required this.id,
    required this.startTime,
    this.endTime,
    this.totalAmount = 0,
    this.redPacketsCollected = 0,
    this.duration = 0,
    Map<String, int>? typeCount,
  }) : typeCount = typeCount ?? {};
}

用户统计模型

dart 复制代码
class UserStats {
  int totalSessions;
  int totalAmount;
  int totalRedPackets;
  int bestSession;
  int totalPlayTime;
  Map<String, int> typeStats;

  UserStats({
    this.totalSessions = 0,
    this.totalAmount = 0,
    this.totalRedPackets = 0,
    this.bestSession = 0,
    this.totalPlayTime = 0,
    Map<String, int>? typeStats,
  }) : typeStats = typeStats ?? {};
}

核心功能实现

1. 应用主框架

dart 复制代码
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '虚拟红包雨',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.red),
        useMaterial3: true,
      ),
      home: const RedPacketRainHomePage(),
    );
  }
}

2. 主页面结构

dart 复制代码
class RedPacketRainHomePage extends StatefulWidget {
  const RedPacketRainHomePage({super.key});

  @override
  State<RedPacketRainHomePage> createState() => _RedPacketRainHomePageState();
}

class _RedPacketRainHomePageState extends State<RedPacketRainHomePage>
    with TickerProviderStateMixin {
  int _selectedIndex = 0;
  List<RedPacket> _redPackets = [];
  List<GameSession> _gameSessions = [];
  UserStats _userStats = UserStats();
  
  bool _isGameActive = false;
  int _currentAmount = 0;
  int _currentRedPackets = 0;
  int _gameTimeLeft = 0;
  Timer? _gameTimer;
  Timer? _spawnTimer;
  Timer? _animationTimer;
  
  late AnimationController _collectAnimationController;
  late Animation<double> _collectScaleAnimation;
  
  GameSession? _currentSession;
  final Random _random = Random();

3. 动画系统设置

dart 复制代码
void _setupAnimations() {
  _collectAnimationController = AnimationController(
    duration: const Duration(milliseconds: 600),
    vsync: this,
  );

  _collectScaleAnimation = Tween<double>(
    begin: 1.0,
    end: 0.0,
  ).animate(CurvedAnimation(
    parent: _collectAnimationController,
    curve: Curves.easeInBack,
  ));
}

4. 游戏逻辑实现

开始游戏
dart 复制代码
void _startGame() {
  setState(() {
    _isGameActive = true;
    _currentAmount = 0;
    _currentRedPackets = 0;
    _gameTimeLeft = 60;
    _redPackets.clear();
  });

  _currentSession = GameSession(
    id: DateTime.now().millisecondsSinceEpoch.toString(),
    startTime: DateTime.now(),
  );

  // 游戏计时器
  _gameTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
    setState(() {
      _gameTimeLeft--;
    });

    if (_gameTimeLeft <= 0) {
      _endGame();
    }
  });

  // 红包生成计时器
  _spawnTimer = Timer.periodic(const Duration(milliseconds: 800), (timer) {
    _spawnRedPacket();
  });

  // 动画更新计时器
  _animationTimer = Timer.periodic(const Duration(milliseconds: 16), (timer) {
    _updateRedPackets();
  });
}
红包生成算法
dart 复制代码
void _spawnRedPacket() {
  if (!_isGameActive) return;

  final screenWidth = MediaQuery.of(context).size.width;
  final probabilities = [0.6, 0.25, 0.12, 0.03];
  
  // 根据概率选择红包类型
  double rand = _random.nextDouble();
  String type = '普通';
  Color color = Colors.red;
  int baseAmount = 1;
  double size = 60;

  if (rand < probabilities[3]) {
    type = '传说';
    color = Colors.purple;
    baseAmount = 50;
    size = 80;
  } else if (rand < probabilities[3] + probabilities[2]) {
    type = '史诗';
    color = Colors.orange;
    baseAmount = 20;
    size = 70;
  } else if (rand < probabilities[3] + probabilities[2] + probabilities[1]) {
    type = '稀有';
    color = Colors.blue;
    baseAmount = 8;
    size = 65;
  }

  final redPacket = RedPacket(
    id: DateTime.now().millisecondsSinceEpoch.toString(),
    x: _random.nextDouble() * (screenWidth - size),
    y: -size,
    size: size,
    speed: 2 + _random.nextDouble() * 3,
    amount: baseAmount + _random.nextInt(baseAmount),
    type: type,
    color: color,
  );

  setState(() {
    _redPackets.add(redPacket);
  });
}
红包更新逻辑
dart 复制代码
void _updateRedPackets() {
  if (!_isGameActive) return;

  final screenHeight = MediaQuery.of(context).size.height;
  
  setState(() {
    _redPackets = _redPackets.where((redPacket) {
      if (redPacket.isCollected || redPacket.isAnimating) {
        return false;
      }
      
      redPacket.y += redPacket.speed;
      
      // 移除超出屏幕的红包
      return redPacket.y < screenHeight + redPacket.size;
    }).toList();
  });
}

5. 触摸交互处理

dart 复制代码
void _handleTap(Offset position) {
  if (!_isGameActive) return;

  for (final redPacket in _redPackets) {
    if (redPacket.isCollected || redPacket.isAnimating) continue;

    final distance = (Offset(redPacket.x + redPacket.size / 2, 
                            redPacket.y + redPacket.size / 2) - position).distance;
    
    if (distance <= redPacket.size / 2) {
      _collectRedPacket(redPacket);
      break;
    }
  }
}

void _collectRedPacket(RedPacket redPacket) {
  if (redPacket.isCollected || redPacket.isAnimating) return;

  setState(() {
    redPacket.isAnimating = true;
    _currentAmount += redPacket.amount;
    _currentRedPackets++;
    
    // 更新当前会话的类型统计
    if (_currentSession != null) {
      _currentSession!.typeCount[redPacket.type] = 
          (_currentSession!.typeCount[redPacket.type] ?? 0) + 1;
    }
  });

  // 播放收集动画
  _collectAnimationController.forward().then((_) {
    _collectAnimationController.reset();
    setState(() {
      redPacket.isCollected = true;
    });
  });
}

UI界面设计

1. 游戏开始页面

dart 复制代码
Widget _buildGameStart() {
  return Center(
    child: SingleChildScrollView(
      padding: const EdgeInsets.all(24),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          // 游戏标题
          Container(
            padding: const EdgeInsets.all(32),
            decoration: BoxDecoration(
              gradient: LinearGradient(
                colors: [Colors.red.shade400, Colors.orange.shade400],
                begin: Alignment.topLeft,
                end: Alignment.bottomRight,
              ),
              borderRadius: BorderRadius.circular(20),
              boxShadow: [
                BoxShadow(
                  color: Colors.red.withValues(alpha: 0.3),
                  blurRadius: 20,
                  spreadRadius: 5,
                ),
              ],
            ),
            child: const Column(
              children: [
                Text('🧧', style: TextStyle(fontSize: 80)),
                SizedBox(height: 16),
                Text(
                  '虚拟红包雨',
                  style: TextStyle(
                    fontSize: 28,
                    fontWeight: FontWeight.bold,
                    color: Colors.white,
                  ),
                ),
                SizedBox(height: 8),
                Text(
                  '抢红包,赢大奖!',
                  style: TextStyle(
                    fontSize: 16,
                    color: Colors.white70,
                  ),
                ),
              ],
            ),
          ),
          
          const SizedBox(height: 40),
          
          // 游戏规则说明
          _buildGameRules(),
          
          const SizedBox(height: 40),
          
          // 开始游戏按钮
          _buildStartButton(),
        ],
      ),
    ),
  );
}

2. 红包渲染组件

dart 复制代码
Widget _buildRedPacket(RedPacket redPacket) {
  if (redPacket.isCollected) return const SizedBox.shrink();

  return Positioned(
    left: redPacket.x,
    top: redPacket.y,
    child: GestureDetector(
      onTap: () => _collectRedPacket(redPacket),
      child: Container(
        width: redPacket.size,
        height: redPacket.size,
        decoration: BoxDecoration(
          gradient: RadialGradient(
            colors: [
              redPacket.color,
              redPacket.color.withValues(alpha: 0.8),
            ],
          ),
          borderRadius: BorderRadius.circular(redPacket.size / 2),
          boxShadow: [
            BoxShadow(
              color: redPacket.color.withValues(alpha: 0.4),
              blurRadius: 8,
              spreadRadius: 2,
            ),
          ],
        ),
        child: Stack(
          children: [
            // 红包主体
            Center(
              child: Container(
                width: redPacket.size * 0.8,
                height: redPacket.size * 0.8,
                decoration: BoxDecoration(
                  color: redPacket.color,
                  borderRadius: BorderRadius.circular(redPacket.size * 0.4),
                  border: Border.all(color: Colors.yellow, width: 2),
                ),
                child: Center(
                  child: Text(
                    '🧧',
                    style: TextStyle(fontSize: redPacket.size * 0.4),
                  ),
                ),
              ),
            ),
            
            // 金额显示
            Positioned(
              bottom: 2,
              left: 0,
              right: 0,
              child: Center(
                child: Container(
                  padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
                  decoration: BoxDecoration(
                    color: Colors.white.withValues(alpha: 0.9),
                    borderRadius: BorderRadius.circular(8),
                  ),
                  child: Text(
                    '${redPacket.amount}',
                    style: TextStyle(
                      fontSize: redPacket.size * 0.15,
                      fontWeight: FontWeight.bold,
                      color: redPacket.color,
                    ),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    ),
  );
}

3. 游戏状态显示

dart 复制代码
Widget _buildGameStatus() {
  return Container(
    padding: const EdgeInsets.all(16),
    decoration: BoxDecoration(
      color: Colors.white.withValues(alpha: 0.9),
      borderRadius: BorderRadius.circular(16),
      boxShadow: [
        BoxShadow(
          color: Colors.black.withValues(alpha: 0.1),
          blurRadius: 10,
          spreadRadius: 2,
        ),
      ],
    ),
    child: Row(
      children: [
        Expanded(
          child: _buildStatusItem(
            '金币',
            '$_currentAmount',
            Icons.monetization_on,
            Colors.amber,
          ),
        ),
        Container(
          width: 1,
          height: 40,
          color: Colors.grey.shade300,
        ),
        Expanded(
          child: _buildStatusItem(
            '红包',
            '$_currentRedPackets',
            Icons.card_giftcard,
            Colors.red,
          ),
        ),
        Container(
          width: 1,
          height: 40,
          color: Colors.grey.shade300,
        ),
        Expanded(
          child: _buildStatusItem(
            '时间',
            '${_gameTimeLeft}s',
            Icons.timer,
            Colors.blue,
          ),
        ),
      ],
    ),
  );
}

游戏记录管理

1. 记录页面实现

dart 复制代码
Widget _buildRecordsPage() {
  return Padding(
    padding: const EdgeInsets.all(16),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          '游戏记录',
          style: Theme.of(context).textTheme.headlineSmall?.copyWith(
            fontWeight: FontWeight.bold,
          ),
        ),
        const SizedBox(height: 16),
        
        if (_gameSessions.isEmpty)
          _buildEmptyRecords()
        else
          Expanded(
            child: ListView.builder(
              itemCount: _gameSessions.length,
              itemBuilder: (context, index) {
                final session = _gameSessions[_gameSessions.length - 1 - index];
                return _buildSessionCard(session, index == 0);
              },
            ),
          ),
      ],
    ),
  );
}

2. 游戏会话卡片

dart 复制代码
Widget _buildSessionCard(GameSession session, bool isLatest) {
  return Card(
    margin: const EdgeInsets.only(bottom: 12),
    elevation: isLatest ? 4 : 2,
    child: Container(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(12),
        border: isLatest ? Border.all(color: Colors.red.shade200, width: 2) : null,
      ),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 时间戳和标签
            Row(
              children: [
                Icon(Icons.access_time, size: 16, color: Colors.grey),
                const SizedBox(width: 4),
                Text(
                  _formatDateTime(session.startTime),
                  style: TextStyle(color: Colors.grey, fontSize: 12),
                ),
                const Spacer(),
                if (isLatest)
                  Container(
                    padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
                    decoration: BoxDecoration(
                      color: Colors.red.shade100,
                      borderRadius: BorderRadius.circular(12),
                    ),
                    child: Text(
                      '最新',
                      style: TextStyle(
                        color: Colors.red,
                        fontSize: 10,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ),
              ],
            ),
            const SizedBox(height: 12),
            
            // 统计数据
            Row(
              children: [
                Expanded(
                  child: _buildSessionStat(
                    '金币', '${session.totalAmount}',
                    Icons.monetization_on, Colors.amber,
                  ),
                ),
                Expanded(
                  child: _buildSessionStat(
                    '红包', '${session.redPacketsCollected}',
                    Icons.card_giftcard, Colors.red,
                  ),
                ),
                Expanded(
                  child: _buildSessionStat(
                    '时长', '${session.duration}s',
                    Icons.timer, Colors.blue,
                  ),
                ),
              ],
            ),
            
            // 红包类型统计
            if (session.typeCount.isNotEmpty) ...[
              const SizedBox(height: 12),
              const Divider(),
              const SizedBox(height: 8),
              Text(
                '红包类型统计',
                style: TextStyle(
                  fontSize: 12,
                  color: Colors.grey,
                  fontWeight: FontWeight.bold,
                ),
              ),
              const SizedBox(height: 8),
              _buildTypeCountChips(session.typeCount),
            ],
          ],
        ),
      ),
    ),
  );
}

统计分析功能

1. 统计页面结构

dart 复制代码
Widget _buildStatsPage() {
  return Padding(
    padding: const EdgeInsets.all(16),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          '统计分析',
          style: Theme.of(context).textTheme.headlineSmall?.copyWith(
            fontWeight: FontWeight.bold,
          ),
        ),
        const SizedBox(height: 16),
        
        if (_userStats.totalSessions == 0)
          _buildEmptyStats()
        else
          Expanded(
            child: SingleChildScrollView(
              child: Column(
                children: [
                  _buildOverallStatsCard(),
                  const SizedBox(height: 16),
                  _buildAverageStatsCard(),
                  const SizedBox(height: 16),
                  _buildAchievementsCard(),
                ],
              ),
            ),
          ),
      ],
    ),
  );
}

2. 成就系统

dart 复制代码
List<Map<String, dynamic>> _getAchievements() {
  return [
    {
      'title': '初出茅庐',
      'description': '完成第一局游戏',
      'icon': Icons.play_arrow,
      'unlocked': _userStats.totalSessions >= 1,
    },
    {
      'title': '小有成就',
      'description': '累计获得100金币',
      'icon': Icons.monetization_on,
      'unlocked': _userStats.totalAmount >= 100,
    },
    {
      'title': '红包达人',
      'description': '累计收集50个红包',
      'icon': Icons.card_giftcard,
      'unlocked': _userStats.totalRedPackets >= 50,
    },
    {
      'title': '游戏专家',
      'description': '完成10局游戏',
      'icon': Icons.games,
      'unlocked': _userStats.totalSessions >= 10,
    },
    {
      'title': '财富自由',
      'description': '单局获得100金币',
      'icon': Icons.emoji_events,
      'unlocked': _userStats.bestSession >= 100,
    },
    {
      'title': '时间管理大师',
      'description': '累计游戏时长超过10分钟',
      'icon': Icons.schedule,
      'unlocked': _userStats.totalPlayTime >= 600,
    },
  ];
}

3. 成就展示组件

dart 复制代码
Widget _buildAchievementItem(Map<String, dynamic> achievement) {
  final bool isUnlocked = achievement['unlocked'];
  
  return Container(
    margin: const EdgeInsets.symmetric(vertical: 4),
    padding: const EdgeInsets.all(12),
    decoration: BoxDecoration(
      color: isUnlocked ? Colors.amber.shade50 : Colors.grey.shade100,
      borderRadius: BorderRadius.circular(8),
      border: Border.all(
        color: isUnlocked ? Colors.amber.shade200 : Colors.grey.shade300,
      ),
    ),
    child: Row(
      children: [
        Icon(
          achievement['icon'],
          color: isUnlocked ? Colors.amber : Colors.grey,
          size: 24,
        ),
        const SizedBox(width: 12),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                achievement['title'],
                style: TextStyle(
                  fontWeight: FontWeight.bold,
                  color: isUnlocked ? Colors.amber : Colors.grey,
                ),
              ),
              Text(
                achievement['description'],
                style: TextStyle(
                  fontSize: 12,
                  color: isUnlocked ? Colors.amber : Colors.grey,
                ),
              ),
            ],
          ),
        ),
        if (isUnlocked)
          const Icon(Icons.check_circle, color: Colors.green, size: 20),
      ],
    ),
  );
}

设置和管理功能

1. 设置页面

dart 复制代码
Widget _buildSettingsPage() {
  return Padding(
    padding: const EdgeInsets.all(16),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          '设置',
          style: Theme.of(context).textTheme.headlineSmall?.copyWith(
            fontWeight: FontWeight.bold,
          ),
        ),
        const SizedBox(height: 16),
        
        Card(
          child: Column(
            children: [
              ListTile(
                leading: const Icon(Icons.refresh, color: Colors.orange),
                title: const Text('重置游戏数据'),
                subtitle: const Text('清除所有游戏记录和统计数据'),
                trailing: const Icon(Icons.chevron_right),
                onTap: _showResetDialog,
              ),
              const Divider(height: 1),
              ListTile(
                leading: const Icon(Icons.info, color: Colors.blue),
                title: const Text('关于应用'),
                subtitle: const Text('版本信息和开发者信息'),
                trailing: const Icon(Icons.chevron_right),
                onTap: _showAboutDialog,
              ),
              const Divider(height: 1),
              ListTile(
                leading: const Icon(Icons.help, color: Colors.green),
                title: const Text('游戏帮助'),
                subtitle: const Text('查看游戏规则和操作说明'),
                trailing: const Icon(Icons.chevron_right),
                onTap: _showHelpDialog,
              ),
            ],
          ),
        ),
        
        const SizedBox(height: 24),
        
        // 统计概览卡片
        _buildQuickStatsCard(),
      ],
    ),
  );
}

2. 数据重置功能

dart 复制代码
void _showResetDialog() {
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: const Row(
        children: [
          Icon(Icons.warning, color: Colors.orange),
          SizedBox(width: 8),
          Text('重置确认'),
        ],
      ),
      content: const Text('确定要重置所有游戏数据吗?此操作不可恢复。'),
      actions: [
        TextButton(
          onPressed: () => Navigator.of(context).pop(),
          child: const Text('取消'),
        ),
        ElevatedButton(
          onPressed: () {
            setState(() {
              _gameSessions.clear();
              _userStats = UserStats();
            });
            Navigator.of(context).pop();
            ScaffoldMessenger.of(context).showSnackBar(
              const SnackBar(content: Text('游戏数据已重置')),
            );
          },
          style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
          child: const Text('确认重置', style: TextStyle(color: Colors.white)),
        ),
      ],
    ),
  );
}

游戏平衡性设计

红包稀有度系统

稀有度 概率 金币范围 尺寸 颜色
普通 60% 1-2 60px 红色
稀有 25% 8-16 65px 蓝色
史诗 12% 20-40 70px 橙色
传说 3% 50-100 80px 紫色

游戏参数配置

dart 复制代码
// 游戏时长
const int GAME_DURATION = 60; // 秒

// 红包生成间隔
const int SPAWN_INTERVAL = 800; // 毫秒

// 动画更新频率
const int ANIMATION_FPS = 60; // 每秒帧数

// 红包掉落速度范围
const double MIN_SPEED = 2.0;
const double MAX_SPEED = 5.0;

// 概率配置
const List<double> RARITY_PROBABILITIES = [0.6, 0.25, 0.12, 0.03];

性能优化策略

1. 对象池管理

dart 复制代码
class RedPacketPool {
  static final List<RedPacket> _pool = [];
  static const int MAX_POOL_SIZE = 50;

  static RedPacket getRedPacket({
    required String id,
    required double x,
    required double y,
    required double size,
    required double speed,
    required int amount,
    required String type,
    required Color color,
  }) {
    if (_pool.isNotEmpty) {
      final redPacket = _pool.removeLast();
      return redPacket.copyWith(
        x: x,
        y: y,
        isCollected: false,
        isAnimating: false,
      );
    }
    
    return RedPacket(
      id: id,
      x: x,
      y: y,
      size: size,
      speed: speed,
      amount: amount,
      type: type,
      color: color,
    );
  }

  static void returnRedPacket(RedPacket redPacket) {
    if (_pool.length < MAX_POOL_SIZE) {
      _pool.add(redPacket);
    }
  }
}

2. 渲染优化

dart 复制代码
// 使用RepaintBoundary减少重绘
Widget _buildOptimizedRedPacket(RedPacket redPacket) {
  return RepaintBoundary(
    child: Positioned(
      left: redPacket.x,
      top: redPacket.y,
      child: _buildRedPacketWidget(redPacket),
    ),
  );
}

// 批量更新减少setState调用
void _batchUpdateRedPackets() {
  bool needsUpdate = false;
  
  for (final redPacket in _redPackets) {
    if (!redPacket.isCollected && !redPacket.isAnimating) {
      redPacket.y += redPacket.speed;
      needsUpdate = true;
    }
  }
  
  if (needsUpdate) {
    setState(() {
      _redPackets.removeWhere((rp) => 
        rp.y > MediaQuery.of(context).size.height + rp.size);
    });
  }
}

测试和调试

1. 单元测试

dart 复制代码
import 'package:flutter_test/flutter_test.dart';
import 'package:red_packet_rain/models/red_packet.dart';
import 'package:red_packet_rain/models/game_session.dart';

void main() {
  group('RedPacket Tests', () {
    test('RedPacket creation', () {
      final redPacket = RedPacket(
        id: '1',
        x: 100,
        y: 200,
        size: 60,
        speed: 3,
        amount: 10,
        type: '普通',
        color: Colors.red,
      );
      
      expect(redPacket.id, '1');
      expect(redPacket.x, 100);
      expect(redPacket.y, 200);
      expect(redPacket.isCollected, false);
    });

    test('RedPacket copyWith', () {
      final original = RedPacket(
        id: '1',
        x: 100,
        y: 200,
        size: 60,
        speed: 3,
        amount: 10,
        type: '普通',
        color: Colors.red,
      );
      
      final copied = original.copyWith(x: 150, isCollected: true);
      
      expect(copied.x, 150);
      expect(copied.isCollected, true);
      expect(copied.y, 200); // 未改变的属性保持原值
    });
  });

  group('GameSession Tests', () {
    test('GameSession statistics', () {
      final session = GameSession(
        id: '1',
        startTime: DateTime.now(),
      );
      
      session.totalAmount = 100;
      session.redPacketsCollected = 10;
      session.typeCount['普通'] = 8;
      session.typeCount['稀有'] = 2;
      
      expect(session.totalAmount, 100);
      expect(session.redPacketsCollected, 10);
      expect(session.typeCount['普通'], 8);
      expect(session.typeCount['稀有'], 2);
    });
  });
}

2. 集成测试

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:red_packet_rain/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  group('Red Packet Rain Integration Tests', () {
    testWidgets('Complete game flow', (WidgetTester tester) async {
      app.main();
      await tester.pumpAndSettle();

      // 验证初始状态
      expect(find.text('虚拟红包雨'), findsOneWidget);
      expect(find.text('开始游戏'), findsOneWidget);

      // 开始游戏
      await tester.tap(find.text('开始游戏'));
      await tester.pumpAndSettle();

      // 验证游戏状态
      expect(find.text('60s'), findsOneWidget);
      expect(find.byIcon(Icons.monetization_on), findsWidgets);

      // 等待红包出现并点击
      await tester.pump(const Duration(seconds: 1));
      
      // 查找红包并点击
      final redPacketFinder = find.byType(GestureDetector);
      if (redPacketFinder.evaluate().isNotEmpty) {
        await tester.tap(redPacketFinder.first);
        await tester.pump();
      }

      // 验证分数更新
      // 这里可以添加更多验证逻辑
    });

    testWidgets('Navigation between tabs', (WidgetTester tester) async {
      app.main();
      await tester.pumpAndSettle();

      // 测试导航到记录页面
      await tester.tap(find.text('记录'));
      await tester.pumpAndSettle();
      expect(find.text('游戏记录'), findsOneWidget);

      // 测试导航到统计页面
      await tester.tap(find.text('统计'));
      await tester.pumpAndSettle();
      expect(find.text('统计分析'), findsOneWidget);

      // 测试导航到设置页面
      await tester.tap(find.text('设置'));
      await tester.pumpAndSettle();
      expect(find.text('设置'), findsOneWidget);
    });
  });
}

部署和发布

1. Android构建配置

gradle 复制代码
// android/app/build.gradle
android {
    compileSdkVersion 34
    
    defaultConfig {
        applicationId "com.example.red_packet_rain"
        minSdkVersion 21
        targetSdkVersion 34
        versionCode 1
        versionName "1.0.0"
    }
    
    buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

2. 性能监控

dart 复制代码
import 'package:flutter/foundation.dart';

class PerformanceMonitor {
  static int _frameCount = 0;
  static DateTime _lastFrameTime = DateTime.now();
  
  static void trackFrame() {
    _frameCount++;
    final now = DateTime.now();
    
    if (now.difference(_lastFrameTime).inSeconds >= 1) {
      if (kDebugMode) {
        print('FPS: $_frameCount');
      }
      _frameCount = 0;
      _lastFrameTime = now;
    }
  }
  
  static void trackMemoryUsage() {
    if (kDebugMode) {
      // 内存使用监控逻辑
    }
  }
}

总结

虚拟红包雨应用通过Flutter框架实现了一个完整的休闲游戏,包含了游戏逻辑、动画效果、数据统计、用户界面等多个方面的功能。

技术亮点

  1. 流畅的动画系统:使用Flutter的Animation框架实现红包掉落和收集动画
  2. 高效的碰撞检测:基于距离计算的触摸检测算法
  3. 科学的概率系统:四级稀有度的红包生成机制
  4. 完整的数据管理:游戏记录、统计分析、成就系统
  5. 优雅的UI设计:Material Design风格的用户界面

学习收获

  • 掌握了Flutter动画系统的使用
  • 学会了游戏循环和状态管理
  • 理解了触摸事件处理机制
  • 实践了数据模型设计和管理
  • 体验了完整的应用开发流程

这个项目展示了如何使用Flutter开发一个功能完整、体验流畅的移动游戏应用,为进一步学习移动应用开发奠定了良好的基础。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

相关推荐
大雷神2 小时前
HarmonyOS智慧农业管理应用开发教程--高高种地--第12篇:成本核算系统
harmonyos
BlackWolfSky3 小时前
鸿蒙中级课程笔记2—状态管理V2—@Computed装饰器:计算属性
笔记·华为·harmonyos
BlackWolfSky5 小时前
鸿蒙中级课程笔记2—状态管理V2—@Local
笔记·华为·harmonyos
2501_944521595 小时前
Flutter for OpenHarmony 微动漫App实战:主题配置实现
android·开发语言·前端·javascript·flutter·ecmascript
时光慢煮5 小时前
Flutter × OpenHarmony 跨端开发实战:动态显示菜单详解
flutter·华为·开源·openharmony
2501_944521595 小时前
Flutter for OpenHarmony 微动漫App实战:动漫卡片组件实现
android·开发语言·javascript·flutter·ecmascript
晚霞的不甘6 小时前
Flutter 布局核心:构建交互式文档应用
开发语言·javascript·flutter·elasticsearch·正则表达式
Easonmax6 小时前
零基础入门 React Native 鸿蒙跨平台开发:3——固定表头表格实现
react native·react.js·harmonyos
不爱吃糖的程序媛7 小时前
【仓颉开发实战】基于 OpenHarmony 实现拨打电话功能
harmonyos