flutter_for_openharmony手语学习app实战+手语识别实现

手语识别是应用的核心功能之一,通过摄像头捕捉用户的手势动作并进行识别。本文介绍如何实现一个手语识别练习页面,包括摄像头预览、识别控制和词汇选择。

StatefulWidget与状态管理

识别页面需要管理多个状态:

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

  @override
  State<RecognitionScreen> createState() => _RecognitionScreenState();
}

class _RecognitionScreenState extends State<RecognitionScreen> {
  String _currentWord = '你好';
  bool _isRecording = false;
  int _score = 0;
  int _attempts = 0;

  final List<String> _words = ['你好', '谢谢', '对不起', '再见', '我爱你', '帮助'];

使用StatefulWidget管理识别状态。_currentWord是当前练习的词汇,_isRecording表示是否正在识别,_score记录得分,_attempts记录尝试次数。_words列表包含可选择的练习词汇。这些状态变量共同构成了识别功能的核心数据

AppBar与得分显示

构建顶部导航栏:

dart 复制代码
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('手语识别练习'),
        actions: [
          Center(
            child: Padding(
              padding: EdgeInsets.only(right: 16.w),
              child: Text('得分: $_score', style: TextStyle(fontSize: 16.sp)),
            ),
          ),
        ],
      ),

AppBaractions区域显示当前得分,让用户随时了解练习成果。得分用CenterPadding包裹,确保垂直居中且与右边缘保持适当距离。这种实时反馈增强了练习的趣味性。

页面布局

上下分割的布局设计:

dart 复制代码
      body: Column(
        children: [
          Expanded(flex: 2, child: _buildCameraPreview()),
          Expanded(flex: 3, child: _buildPracticeArea()),
        ],
      ),
    );
  }

Column纵向排列摄像头预览和练习区域,Expandedflex参数控制比例。摄像头占2份,练习区域占3份,形成2:3的黄金比例。这样既能看清摄像头画面,又有足够空间显示练习内容。

摄像头预览区域

构建摄像头的占位容器:

dart 复制代码
  Widget _buildCameraPreview() {
    return Container(
      margin: EdgeInsets.all(16.w),
      decoration: BoxDecoration(
        color: Colors.grey[900],
        borderRadius: BorderRadius.circular(16.r),
      ),
      child: Stack(
        alignment: Alignment.center,
        children: [
          Icon(Icons.videocam, size: 80.sp, color: Colors.white24),

用深灰色背景和圆角模拟摄像头预览区域,中间放置半透明的摄像机图标作为占位符。实际项目中这里应该嵌入真实的摄像头预览 ,使用camera插件获取视频流。Stack用于层叠显示多个元素。

识别状态指示器

右上角显示识别状态:

dart 复制代码
          Positioned(
            top: 16.h,
            right: 16.w,
            child: Container(
              padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
              decoration: BoxDecoration(
                color: _isRecording ? Colors.red : Colors.grey,
                borderRadius: BorderRadius.circular(12.r),
              ),
              child: Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(Icons.circle, color: Colors.white, size: 8.sp),
                  SizedBox(width: 4.w),
                  Text(
                    _isRecording ? '识别中' : '待开始',
                    style: TextStyle(color: Colors.white, fontSize: 12.sp),
                  ),
                ],
              ),
            ),
          ),

Positioned将状态指示器固定在右上角。识别中显示红色背景和"识别中"文字,待机时显示灰色背景和"待开始"文字。小圆点图标模拟录制指示灯,红色表示正在工作。这种视觉反馈让用户清楚当前状态。

提示文字

未开始时的引导提示:

dart 复制代码
          if (!_isRecording)
            Container(
              padding: EdgeInsets.all(16.w),
              decoration: BoxDecoration(
                color: Colors.black54,
                borderRadius: BorderRadius.circular(8.r),
              ),
              child: Text(
                '点击下方按钮开始识别',
                style: TextStyle(color: Colors.white, fontSize: 14.sp),
              ),
            ),
        ],
      ),
    );
  }

if条件渲染,只在未识别时显示提示文字。半透明黑色背景让文字清晰可读,白色文字与深色背景形成对比。这种引导提示降低了用户的学习成本,知道下一步该做什么。

练习区域

构建下方的练习控制区:

dart 复制代码
  Widget _buildPracticeArea() {
    return SingleChildScrollView(
      padding: EdgeInsets.all(16.w),
      child: Column(
        children: [
          Card(
            child: Padding(
              padding: EdgeInsets.all(20.w),
              child: Column(
                children: [
                  Text('请用手语表达', style: TextStyle(fontSize: 14.sp, color: Colors.grey)),
                  SizedBox(height: 8.h),

SingleChildScrollView包裹,内容过多时可以滚动。Column纵向排列各个功能区域。顶部卡片显示当前练习的词汇和控制按钮,用灰色小字提示"请用手语表达",说明任务目标。

当前词汇显示

大字号显示练习词汇:

dart 复制代码
                  Text(
                    _currentWord,
                    style: TextStyle(fontSize: 36.sp, fontWeight: FontWeight.bold),
                  ),
                  SizedBox(height: 16.h),

词汇用36.sp的大字号和粗体显示,作为页面的视觉焦点。用户一眼就能看到要练习的内容。字号足够大,即使在一定距离外也能清晰看到,方便用户边看边做手势。

识别控制按钮

开始/停止识别的按钮:

dart 复制代码
                  ElevatedButton.icon(
                    onPressed: () => setState(() => _isRecording = !_isRecording),
                    icon: Icon(_isRecording ? Icons.stop : Icons.play_arrow, color: Colors.white),
                    label: Text(_isRecording ? '停止识别' : '开始识别', style: const TextStyle(color: Colors.white)),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: _isRecording ? Colors.red : const Color(0xFF00897B),
                      padding: EdgeInsets.symmetric(horizontal: 32.w, vertical: 12.h),
                    ),
                  ),
                ],
              ),
            ),
          ),
          SizedBox(height: 16.h),

按钮根据识别状态动态变化:识别中显示红色背景和停止图标,待机时显示主题色背景和播放图标。点击按钮切换_isRecording状态,调用setState触发界面更新。这种状态切换的交互方式简单直观。

词汇选择器

构建词汇选择区域:

dart 复制代码
          _buildWordSelector(),
          SizedBox(height: 16.h),
          _buildTips(),
        ],
      ),
    );
  }

  Widget _buildWordSelector() {
    return Card(
      child: Padding(
        padding: EdgeInsets.all(16.w),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('选择练习词汇', style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.bold)),
            SizedBox(height: 12.h),

词汇选择器用Card包裹,标题用粗体显示。crossAxisAlignment.start让内容左对齐,符合阅读习惯。这个区域让用户可以自由选择想要练习的词汇,提高了练习的灵活性。

Wrap布局

使用Wrap自动换行排列词汇:

dart 复制代码
            Wrap(
              spacing: 8.w,
              runSpacing: 8.h,
              children: _words.map((word) {
                final isSelected = word == _currentWord;
                return ChoiceChip(
                  label: Text(word),
                  selected: isSelected,
                  onSelected: (selected) {
                    if (selected) setState(() => _currentWord = word);
                  },
                  selectedColor: const Color(0xFF00897B).withOpacity(0.2),
                );
              }).toList(),
            ),
          ],
        ),
      ),
    );
  }

Wrap类似Row但会自动换行,spacing控制水平间距,runSpacing控制垂直间距。ChoiceChip是单选芯片组件,选中时显示主题色半透明背景。点击芯片更新_currentWord并刷新界面。这种芯片选择的交互方式现代且易用。

提示信息

底部的使用提示:

dart 复制代码
  Widget _buildTips() {
    return Card(
      color: Colors.blue[50],
      child: Padding(
        padding: EdgeInsets.all(16.w),
        child: Row(
          children: [
            Icon(Icons.info_outline, color: Colors.blue),
            SizedBox(width: 12.w),
            Expanded(
              child: Text(
                '确保光线充足,手势在摄像头范围内,动作清晰完整',
                style: TextStyle(fontSize: 13.sp, color: Colors.blue[800]),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

提示卡片用浅蓝色背景,左侧放置信息图标,右侧显示提示文字。Expanded让文字占据剩余空间,自动换行。蓝色配色表示这是信息提示而非警告或错误。提示内容告诉用户如何获得更好的识别效果。

Stack的层叠布局

摄像头预览区的层叠设计:

dart 复制代码
Stack(
  alignment: Alignment.center,
  children: [
    Icon(...),              // 底层:占位图标
    Positioned(...),        // 顶层:状态指示器
    if (!_isRecording) ..., // 条件层:提示文字
  ],
)

Stack让多个组件层叠显示,alignment设为center让未定位的子组件居中。Positioned精确控制状态指示器的位置,if条件渲染提示文字。这种层叠布局创造了丰富的视觉层次。

状态切换的逻辑

简洁的状态切换实现:

dart 复制代码
onPressed: () => setState(() => _isRecording = !_isRecording),

使用!运算符取反,一行代码实现状态切换。setState包裹状态更新,触发界面重建。这种简洁的写法if-else判断更优雅,代码可读性高。

ChoiceChip的优势

使用ChoiceChip实现单选:

dart 复制代码
ChoiceChip(
  label: Text(word),
  selected: isSelected,
  onSelected: (selected) {
    if (selected) setState(() => _currentWord = word);
  },
  selectedColor: const Color(0xFF00897B).withOpacity(0.2),
)

ChoiceChip是Flutter提供的选择芯片组件,自带选中状态管理和动画效果。selected属性控制是否选中,onSelected回调处理选择事件。相比自己实现单选逻辑,使用现成组件开发效率更高

Wrap的自动换行

处理不同屏幕宽度:

dart 复制代码
Wrap(
  spacing: 8.w,
  runSpacing: 8.h,
  children: _words.map((word) => ChoiceChip(...)).toList(),
)

Wrap会根据可用宽度自动换行,不会像Row那样溢出屏幕。spacing控制同一行元素的间距,runSpacing控制不同行的间距。这种自适应布局在不同屏幕上都能正常显示。

颜色的语义化

不同颜色传达不同信息:

dart 复制代码
Colors.red,              // 识别中/停止
Color(0xFF00897B),       // 主题色/开始
Colors.grey,             // 待机状态
Colors.blue[50],         // 信息提示背景

红色表示正在工作或停止操作,主题色表示可以开始,灰色表示待机,浅蓝色表示信息提示。这种颜色语义化符合用户认知习惯,无需文字说明就能理解含义。

Expanded的flex比例

控制区域大小比例:

dart 复制代码
Expanded(flex: 2, child: _buildCameraPreview()),
Expanded(flex: 3, child: _buildPracticeArea()),

flex参数控制占用空间的比例,2:3的比例让摄像头预览占40%,练习区域占60%。这个比例经过视觉调试,既能看清摄像头画面,又有足够空间显示控制元素。

条件渲染的应用

根据状态显示不同内容:

dart 复制代码
if (!_isRecording)
  Container(
    padding: EdgeInsets.all(16.w),
    decoration: BoxDecoration(...),
    child: Text('点击下方按钮开始识别', ...),
  ),

Dart的if语句可以直接用在集合中,条件为真时才添加元素。这比三元运算符!_isRecording ? Container(...) : SizedBox()更简洁。条件渲染让代码更声明式,逻辑清晰易懂。

动态图标和文字

根据状态变化UI:

dart 复制代码
icon: Icon(_isRecording ? Icons.stop : Icons.play_arrow, color: Colors.white),
label: Text(_isRecording ? '停止识别' : '开始识别', ...),
backgroundColor: _isRecording ? Colors.red : const Color(0xFF00897B),

图标、文字、背景色都根据_isRecording状态动态变化。这种一致性变化让用户清楚按钮的当前功能,点击后会发生什么。UI与状态保持同步是良好用户体验的关键。

SingleChildScrollView的作用

处理内容溢出:

dart 复制代码
SingleChildScrollView(
  padding: EdgeInsets.all(16.w),
  child: Column(
    children: [...],
  ),
)

SingleChildScrollView让内容可以滚动,避免在小屏设备上溢出。padding设置整体内边距,Column纵向排列各个区域。这种防御性设计确保在各种屏幕尺寸上都能正常显示。

响应式布局

使用flutter_screenutil适配屏幕:

dart 复制代码
fontSize: 36.sp,
padding: EdgeInsets.all(16.w),
spacing: 8.w,
runSpacing: 8.h,

.sp用于字号,.w.h用于尺寸和间距。这些单位会根据屏幕尺寸自动缩放,确保在不同设备上比例一致。一套代码适配所有屏幕,无需针对不同设备单独调整。

小结

手语识别页面通过摄像头预览和练习控制实现识别功能,状态指示器实时反馈识别状态。词汇选择器让用户自由选择练习内容,提示信息指导用户获得更好的识别效果。整体设计注重状态管理和视觉反馈,打造流畅的识别练习体验。


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

相关推荐
雨季6661 小时前
Flutter 三端应用实战:OpenHarmony 简易数字累加器开发指南
开发语言·flutter·ui·ecmascript
浅念-2 小时前
C语言——内存函数
c语言·经验分享·笔记·学习·算法
●VON2 小时前
Flutter for OpenHarmony:基于 SharedPreferences 的本地化笔记应用架构与实现
笔记·学习·flutter·ui·架构·openharmony·von
程序员清洒2 小时前
Flutter for OpenHarmony:Dialog 与 BottomSheet — 弹出式交互
开发语言·flutter·华为·交互·鸿蒙
ujainu2 小时前
Flutter + OpenHarmony开发端组件实战
flutter
求真求知的糖葫芦2 小时前
耦合传输线分析学习笔记(九)对称耦合微带线S参数矩阵推导与应用(下)
笔记·学习·矩阵·射频工程
herinspace2 小时前
管家婆分销软件中如何进行现金流量分配
运维·服务器·数据库·学习·电脑
2601_949575862 小时前
Flutter for OpenHarmony艺考真题题库+个人信息管理实现
java·前端·flutter
LYS_06182 小时前
寒假学习(8)(c语言8+模数电8)
c语言·学习·pcb