Flutter for OpenHarmony 实战:双控制系统实现(按钮+键盘)

Flutter for OpenHarmony 实战:双控制系统实现(按钮+键盘)

一、前言

为了提供良好的用户体验,我们实现了双控制系统:屏幕触控按钮和键盘控制。触控按钮方便移动端操作,键盘控制适合PC端开发调试。本文将详细讲解两种控制方式的实现。

二、屏幕按钮控制

2.1 按钮布局设计

采用经典的D-Pad布局(十字方向键):

dart 复制代码
if (!isGameOver)
  Container(
    padding: const EdgeInsets.all(20),
    child: Column(
      children: [
        // 上按钮(单独一行)
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            _buildControlButton(Icons.arrow_upward, () {
              _changeDirection(Direction.up);
            }),
          ],
        ),

        const SizedBox(height: 10),

        // 左、暂停、右按钮(一行三个)
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            _buildControlButton(Icons.arrow_back, () {
              _changeDirection(Direction.left);
            }),
            const SizedBox(width: 20),
            _buildControlButton(
              isPaused ? Icons.play_arrow : Icons.pause,
              () {
                setState(() {
                  isPaused = !isPaused;
                });
              },
            ),
            const SizedBox(width: 20),
            _buildControlButton(Icons.arrow_forward, () {
              _changeDirection(Direction.right);
            }),
          ],
        ),

        const SizedBox(height: 10),

        // 下按钮(单独一行)
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            _buildControlButton(Icons.arrow_downward, () {
              _changeDirection(Direction.down);
            }),
          ],
        ),
      ],
    ),
  ),

布局特点:

  • 上按钮:居中独占一行
  • 中间行:左、暂停、右三个按钮
  • 下按钮:居中独占一行
  • 形成十字键布局

2.2 圆形按钮样式

dart 复制代码
Widget _buildControlButton(IconData icon, VoidCallback onPressed) {
  return Container(
    width: 60,
    height: 60,
    decoration: BoxDecoration(
      color: Colors.green,
      borderRadius: BorderRadius.circular(12),
    ),
    child: IconButton(
      icon: Icon(icon, color: Colors.white, size: 30),
      onPressed: onPressed,
    ),
  );
}

样式特点:

  • 尺寸:60×60像素
  • 颜色:绿色背景、白色图标
  • 圆角:12像素
  • 图标大小:30像素

2.3 点击事件处理

dart 复制代码
_buildControlButton(Icons.arrow_upward, () {
  _changeDirection(Direction.up);
})

事件流程:

  1. 用户点击按钮
  2. 触发onPressed回调
  3. 调用_changeDirection()
  4. 更新nextDirection
  5. 下次_update()时应用新方向

三、KeyboardListener键盘监听

3.1 KeyboardListener组件使用

dart 复制代码
KeyboardListener(
  focusNode: _focusNode,
  onKeyEvent: _handleKeyEvent,
  child: Scaffold(
    // 游戏内容
  ),
)

组件说明:

  • focusNode:焦点控制器
  • onKeyEvent:键盘事件回调
  • child:子组件(游戏界面)

3.2 KeyEvent事件类型

Flutter中KeyEvent有两种类型:

dart 复制代码
void _handleKeyEvent(KeyEvent event) {
  if (event is KeyDownEvent) {  // 按键按下
    // 处理按键
  }

  if (event is KeyUpEvent) {    // 按键抬起
    // 一般不处理
  }
}

事件类型:

  • KeyDownEvent:按键按下时触发
  • KeyUpEvent:按键抬起时触发
  • 我们只处理KeyDownEvent

3.3 FocusNode焦点管理

dart 复制代码
late final FocusNode _focusNode;

@override
void initState() {
  super.initState();
  _focusNode = FocusNode();
  _focusNode.requestFocus();  // 请求焦点
}

@override
void dispose() {
  _focusNode.dispose();  // 释放焦点
  super.dispose();
}

FocusNode作用:

  • 管理组件焦点状态
  • requestFocus():获取焦点
  • 有焦点才能接收键盘事件

重要提示:

  • 必须调用requestFocus()
  • 否则键盘事件无法触发

四、多键位兼容实现

4.1 WASD键位映射

dart 复制代码
case LogicalKeyboardKey.keyW:
  _changeDirection(Direction.up);
  return;
case LogicalKeyboardKey.keyS:
  _changeDirection(Direction.down);
  return;
case LogicalKeyboardKey.keyA:
  _changeDirection(Direction.left);
  return;
case LogicalKeyboardKey.keyD:
  _changeDirection(Direction.right);
  return;

WASD布局:

复制代码
W
A S D
  • W:上
  • S:下
  • A:左
  • D:右

4.2 方向键映射

dart 复制代码
case LogicalKeyboardKey.arrowUp:
  _changeDirection(Direction.up);
  return;
case LogicalKeyboardKey.arrowDown:
  _changeDirection(Direction.down);
  return;
case LogicalKeyboardKey.arrowLeft:
  _changeDirection(Direction.left);
  return;
case LogicalKeyboardKey.arrowRight:
  _changeDirection(Direction.right);
  return;

方向键布局:

复制代码
  ↑
← ↓ →

4.3 LogicalKeyboardKey使用

Flutter的LogicalKeyboardKey枚举提供跨平台的按键映射:

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

switch (event.logicalKey) {
  case LogicalKeyboardKey.keyW:
  case LogicalKeyboardKey.arrowUp:
    _changeDirection(Direction.up);
    return;
  // ...
}

优势:

  • 跨平台统一API
  • 自动处理不同键盘布局
  • 支持物理键盘和软键盘

五、辅助功能实现

5.1 空格暂停/继续

dart 复制代码
case LogicalKeyboardKey.space:
  setState(() {
    isPaused = !isPaused;
  });
  return;

功能说明:

  • 按下空格键
  • 切换暂停状态
  • true→false或false→true
  • 定时器在_update()中检查isPaused

5.2 R键重新开始

dart 复制代码
case LogicalKeyboardKey.keyR:
  if (isGameOver) {
    _initGame();
  }
  return;

功能说明:

  • 游戏结束时才有效
  • 按R键重新开始
  • 调用_initGame()重置状态

5.3 焦点自动获取

dart 复制代码
@override
void initState() {
  super.initState();
  _focusNode = FocusNode();
  _focusNode.requestFocus();  // 自动获取焦点
  _initGame();
}

为什么需要自动获取焦点?

  • 应用启动后,焦点可能不在游戏组件
  • 没有焦点就无法接收键盘事件
  • requestFocus()确保游戏能接收输入

六、完整代码实现

dart 复制代码
class _GameHomePageState extends State<GameHomePage> {
  late final FocusNode _focusNode;

  @override
  void initState() {
    super.initState();
    _focusNode = FocusNode();
    _focusNode.requestFocus();
    _initGame();
  }

  void _changeDirection(Direction newDirection) {
    if ((direction == Direction.up && newDirection != Direction.down) ||
        (direction == Direction.down && newDirection != Direction.up) ||
        (direction == Direction.left && newDirection != Direction.right) ||
        (direction == Direction.right && newDirection != Direction.left)) {
      nextDirection = newDirection;
    }
  }

  void _handleKeyEvent(KeyEvent event) {
    if (event is KeyDownEvent) {
      switch (event.logicalKey) {
        case LogicalKeyboardKey.keyW:
        case LogicalKeyboardKey.arrowUp:
          _changeDirection(Direction.up);
          return;
        case LogicalKeyboardKey.keyS:
        case LogicalKeyboardKey.arrowDown:
          _changeDirection(Direction.down);
          return;
        case LogicalKeyboardKey.keyA:
        case LogicalKeyboardKey.arrowLeft:
          _changeDirection(Direction.left);
          return;
        case LogicalKeyboardKey.keyD:
        case LogicalKeyboardKey.arrowRight:
          _changeDirection(Direction.right);
          return;
        case LogicalKeyboardKey.space:
          setState(() {
            isPaused = !isPaused;
          });
          return;
        case LogicalKeyboardKey.keyR:
          if (isGameOver) {
            _initGame();
          }
          return;
      }
    }
  }

  @override
  void dispose() {
    gameTimer?.cancel();
    _focusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return KeyboardListener(
      focusNode: _focusNode,
      onKeyEvent: _handleKeyEvent,
      child: Scaffold(
        body: Column(
          children: [
            // 游戏画面
            Expanded(
              child: CustomPaint(
                painter: GamePainter(...),
              ),
            ),
            // 控制按钮
            if (!isGameOver)
              Container(
                padding: const EdgeInsets.all(20),
                child: Column(
                  children: [
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        _buildControlButton(Icons.arrow_upward, () {
                          _changeDirection(Direction.up);
                        }),
                      ],
                    ),
                    const SizedBox(height: 10),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        _buildControlButton(Icons.arrow_back, () {
                          _changeDirection(Direction.left);
                        }),
                        const SizedBox(width: 20),
                        _buildControlButton(
                          isPaused ? Icons.play_arrow : Icons.pause,
                          () {
                            setState(() {
                              isPaused = !isPaused;
                            });
                          },
                        ),
                        const SizedBox(width: 20),
                        _buildControlButton(Icons.arrow_forward, () {
                          _changeDirection(Direction.right);
                        }),
                      ],
                    ),
                    const SizedBox(height: 10),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        _buildControlButton(Icons.arrow_downward, () {
                          _changeDirection(Direction.down);
                        }),
                      ],
                    ),
                    const SizedBox(height: 10),
                    Text(
                      '或使用 WASD / 方向键控制',
                      style: TextStyle(
                        fontSize: 14,
                        color: Colors.grey[400],
                      ),
                    ),
                  ],
                ),
              ),
          ],
        ),
      ),
    );
  }

  Widget _buildControlButton(IconData icon, VoidCallback onPressed) {
    return Container(
      width: 60,
      height: 60,
      decoration: BoxDecoration(
        color: Colors.green,
        borderRadius: BorderRadius.circular(12),
      ),
      child: IconButton(
        icon: Icon(icon, color: Colors.white, size: 30),
        onPressed: onPressed,
      ),
    );
  }
}

七、功能演示

操作演示:

  1. 启动游戏,蛇自动向右移动
  2. 点击上按钮或按W键:蛇向上移动
  3. 点击暂停按钮或按空格键:游戏暂停
  4. 游戏结束后,按R键:重新开始

双系统优势:

  • 触控按钮:移动端友好
  • 键盘控制:PC端开发高效
  • 可同时使用,互不冲突

八、总结

本文讲解了双控制系统实现:

  1. 触控按钮:十字布局,绿色圆角样式
  2. KeyboardListener:监听键盘事件
  3. WASD+方向键:多种键位兼容
  4. 辅助功能:空格暂停、R键重开

关键要点:

  • FocusNode必须requestFocus()
  • KeyEvent分为KeyDown和KeyUp
  • 多键位兼容提升用户体验

下篇预告:《Flutter for OpenHarmony 实战:开发调试中的三个典型Bug》

社区支持

欢迎加入开源 OpenHarmony 跨平台社区,获取更多技术支持和资源:

如果本文对您有帮助,欢迎点赞、收藏和评论。您的支持是我持续创作的动力!

相关推荐
子春一2 小时前
Flutter for OpenHarmony:构建一个高精度 Flutter 计时器:深入解析 Timer、状态同步与 UI 响应式设计
flutter·ui
雨季6662 小时前
构建 OpenHarmony 简易文字行数统计器:用字符串分割实现纯文本结构感知
开发语言·前端·javascript·flutter·ui·dart
雨季6662 小时前
Flutter 三端应用实战:OpenHarmony 简易倒序文本查看器开发指南
开发语言·javascript·flutter·ui
九 龙2 小时前
Flutter框架跨平台鸿蒙开发——水电缴费提醒APP的开发流程
flutter·华为·harmonyos·鸿蒙
2401_892000522 小时前
Flutter for OpenHarmony 猫咪管家App实战 - 添加支出实现
前端·javascript·flutter
2601_949613023 小时前
flutter_for_openharmony家庭药箱管理app实战+药品分类实现
大数据·数据库·flutter
摘星编程3 小时前
React Native鸿蒙版:StackNavigation页面返回拦截
react native·react.js·harmonyos
BlackWolfSky4 小时前
鸿蒙中级课程笔记4—应用程序框架进阶1—Stage模型应用组成结构、UIAbility启动模式、启动应用内UIAbility
笔记·华为·harmonyos
00后程序员张4 小时前
对比 Ipa Guard 与 Swift Shield 在 iOS 应用安全处理中的使用差异
android·开发语言·ios·小程序·uni-app·iphone·swift
Miguo94well4 小时前
Flutter框架跨平台鸿蒙开发——植物养殖APP的开发流程
flutter·华为·harmonyos·鸿蒙