【maaath】Flutter for OpenHarmony 媒体工具应用开发实战

Flutter for OpenHarmony 媒体工具应用开发实战

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

作者:maaath

一、引言

在移动应用开发领域,截图和录屏功能已经成为各类应用的标配需求。无论是社交分享、系统工具还是办公应用,媒体捕获能力都是提升用户体验的重要组成部分。本文将详细介绍如何使用 Flutter for OpenHarmony 跨平台框架开发一个功能完善的媒体工具应用,涵盖全屏截图、区域截图、长截图以及屏幕录制等核心功能。

Flutter 作为谷歌推出的跨平台 UI 框架,其"一次编写,多端运行"的理念与 OpenHarmony 的分布式设计理念高度契合。通过 Flutter for OpenHarmony,开发者可以高效地将现有 Flutter 应用迁移到鸿蒙平台,同时保持代码的统一性和可维护性。本文将展示如何利用 Flutter 的声明式 UI 特性,结合 OpenHarmony 的原生能力,构建一个在鸿蒙设备上稳定运行的媒体工具应用。

二、项目概述

2.1 功能需求

本项目实现的媒体工具应用包含以下核心功能模块:

截图功能模块

  • 全屏截图:快速截取当前屏幕的完整内容
  • 区域截图:通过拖动选择器自由框选截图区域
  • 长截图:支持自动滚动截取长页面内容
  • 截图预览与分享:提供截图预览、保存到相册和分享功能

录屏功能模块

  • 屏幕录制:录制屏幕操作过程
  • 录制控制:支持开始、暂停、继续和停止
  • 参数设置:可配置录制时长、画质和帧率
  • 进度显示:实时显示录制时间和进度

历史管理模块

  • 媒体列表:展示所有截图和视频文件
  • 分类筛选:支持按类型筛选截图和视频
  • 预览管理:提供大图预览和操作功能

2.2 技术架构

项目采用 Flutter 推荐的 Clean Architecture 架构,将界面层、业务逻辑层和数据层分离。各页面组件职责明确,通过 Navigator 进行页面路由管理。整体代码结构清晰,便于后续功能扩展和维护。

三、环境准备

3.1 开发环境要求

  • Flutter SDK 3.0 及以上版本
  • 支持 OpenHarmony 的 Flutter 运行环境
  • IDE 推荐使用 DevEco Studio 或 VS Code

3.2 项目创建

bash 复制代码
# 创建 Flutter 项目
flutter create media_tool_app
cd media_tool_app

# 添加必要的依赖
flutter pub add cupertino_icons

项目配置文件 pubspec.yaml 如下:

yaml 复制代码
name: media_tool_app
description: 媒体工具应用 - Flutter for OpenHarmony
publish_to: 'none'
version: 1.0.0+1

environment:
  sdk: '>=3.0.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.6

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^3.0.0

flutter:
  uses-material-design: true

四、核心功能实现

4.1 主页设计与实现

媒体工具主页是应用的入口页面,采用简洁清晰的设计风格。页面顶部为标题栏,包含应用名称和历史记录入口;主体部分分为截图工具区和录屏工具区,每个功能卡片展示图标、标题和描述信息;底部包含功能说明区域,帮助用户快速了解各项功能。

主页采用 SingleChildScrollView 实现滚动布局,Column 组件进行垂直排列。功能卡片使用 Container 包装,通过 BoxDecoration 实现圆角和阴影效果,InkWell 组件实现点击反馈。整体设计遵循 Material Design 3 规范,色彩搭配协调统一。

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF5F5F5),
      appBar: AppBar(
        title: const Row(
          children: [
            Icon(Icons.photo_camera, size: 28),
            SizedBox(width: 12),
            Text('媒体工具', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
          ],
        ),
        backgroundColor: Colors.white,
        actions: [
          TextButton.icon(
            onPressed: () {
              Navigator.push(context, MaterialPageRoute(builder: (context) => const MediaHistoryPage()));
            },
            icon: const Icon(Icons.history, color: Colors.blue),
            label: const Text('历史', style: TextStyle(color: Colors.blue)),
          ),
        ],
      ),
      body: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            _buildSectionTitle('截图工具'),
            _buildScreenshotSection(context),
            _buildSectionTitle('录屏工具'),
            _buildRecordSection(context),
          ],
        ),
      ),
    );
  }

  Widget _buildFeatureCard(
    BuildContext context, {
    required IconData icon,
    required String title,
    required String desc,
    required Color color,
    required VoidCallback onTap,
  }) {
    return Container(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        boxShadow: [
          BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 8, offset: const Offset(0, 2)),
        ],
      ),
      child: Material(
        color: Colors.transparent,
        child: InkWell(
          borderRadius: BorderRadius.circular(16),
          onTap: onTap,
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Row(
              children: [
                Container(
                  width: 70, height: 70,
                  decoration: BoxDecoration(color: color.withOpacity(0.15), borderRadius: BorderRadius.circular(16)),
                  child: Icon(icon, size: 40, color: color),
                ),
                const SizedBox(width: 16),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
                      const SizedBox(height: 4),
                      Text(desc, style: const TextStyle(fontSize: 13, color: Color(0xFF888888))),
                    ],
                  ),
                ),
                const Icon(Icons.chevron_right, size: 24, color: Color(0xFFCCCCCC)),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

4.2 截图功能实现

截图页面是应用的核心页面之一,支持三种截图模式:全屏截图、区域截图和长截图。页面根据传入的截图类型参数动态渲染不同的 UI 界面。

全屏截图实现

全屏截图界面包含一个居中显示的相机图标提示区域和底部的截图按钮。用户点击按钮后,页面显示加载进度动画,模拟截图处理过程,最后展示截图预览。进度动画使用 LinearProgressIndicator 和 CircularProgressIndicator 组件实现,为用户提供直观的操作反馈。

dart 复制代码
Widget _buildFullscreenView() {
  return Column(
    children: [
      Expanded(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: const [
              Icon(Icons.photo_camera, size: 100, color: Colors.white),
              SizedBox(height: 30),
              Text('点击下方按钮', style: TextStyle(fontSize: 18, color: Colors.white)),
              SizedBox(height: 10),
              Text('截取当前屏幕', style: TextStyle(fontSize: 14, color: Color(0xFF888888))),
            ],
          ),
        ),
      ),
      Padding(
        padding: const EdgeInsets.only(bottom: 80),
        child: GestureDetector(
          onTap: _takeFullscreenScreenshot,
          child: Container(
            width: 80, height: 80,
            decoration: BoxDecoration(
              color: const Color(0xFF4CAF50),
              shape: BoxShape.circle,
              boxShadow: [
                BoxShadow(color: const Color(0xFF4CAF50).withOpacity(0.6), blurRadius: 15, offset: const Offset(0, 5)),
              ],
            ),
            child: const Icon(Icons.circle, size: 50, color: Colors.white),
          ),
        ),
      ),
    ],
  );
}

void _takeFullscreenScreenshot() async {
  setState(() { isCapturing = true; captureProgress = 0; });
  for (int i = 0; i <= 10; i++) {
    await Future.delayed(const Duration(milliseconds: 100));
    setState(() { captureProgress = i * 10.0; });
  }
  setState(() { captured = true; isCapturing = false; });
  _showSnackBar('截图成功');
}

区域截图实现

区域截图功能通过 GestureDetector 组件的 onPanStart、onPanUpdate 和 onPanEnd 回调实现拖动选择。用户在空白区域按下并拖动时,程序记录起点和终点坐标,实时渲染选区框。选区框使用 Positioned 组件定位,通过计算起点和终点的最小坐标确定左上角位置,通过绝对值计算确定宽高。

dart 复制代码
Widget _buildRegionView() {
  return Stack(
    children: [
      Container(color: Colors.black.withOpacity(0.3)),
      GestureDetector(
        onPanStart: (details) {
          setState(() {
            regionStartX = details.localPosition.dx;
            regionStartY = details.localPosition.dy;
            isSelecting = true;
          });
        },
        onPanUpdate: (details) {
          setState(() {
            regionEndX = details.localPosition.dx;
            regionEndY = details.localPosition.dy;
          });
        },
        onPanEnd: (details) {
          setState(() {
            regionEndX = details.localPosition.dx;
            regionEndY = details.localPosition.dy;
          });
        },
        child: Container(color: Colors.transparent),
      ),
      if (isSelecting && (regionEndX != 0 || regionEndY != 0))
        Positioned(
          left: regionStartX < regionEndX ? regionStartX : regionEndX,
          top: regionStartY < regionEndY ? regionStartY : regionEndY,
          child: Container(
            width: (regionEndX - regionStartX).abs(),
            height: (regionEndY - regionStartY).abs(),
            decoration: BoxDecoration(
              border: Border.all(color: const Color(0xFF4CAF50), width: 2),
              color: const Color(0xFF4CAF50).withOpacity(0.2),
            ),
          ),
        ),
    ],
  );
}

长截图实现

长截图界面提供开始按钮,点击后进入滚动截取模式。页面顶部显示滚动进度条,模拟页面自动滚动的过程。进度条使用 LinearProgressIndicator 组件,通过 setState 定时更新进度值。截图完成后进入预览界面。

4.3 录屏功能实现

屏幕录制页面包含三个状态:设置状态、录制状态和预览状态。通过状态变量 isRecording 和 hasRecording 控制界面切换。

录制控制逻辑

录屏功能使用 Future.doWhile 实现定时器效果,在录制状态且未暂停时每秒更新时间计数器和进度值。当达到最大录制时长或用户手动停止时,进入预览状态。

dart 复制代码
class ScreenRecordPage extends StatefulWidget {
  const ScreenRecordPage({super.key});
  @override
  State<ScreenRecordPage> createState() => _ScreenRecordPageState();
}

class _ScreenRecordPageState extends State<ScreenRecordPage> {
  bool isRecording = false;
  bool isPaused = false;
  int recordingTime = 0;
  double recordingProgress = 0;
  int maxDuration = 60;
  bool hasRecording = false;

  void _startRecording() {
    setState(() {
      isRecording = true;
      isPaused = false;
      recordingTime = 0;
      recordingProgress = 0;
    });
    Future.doWhile(() async {
      if (!isRecording) return false;
      if (!isPaused) {
        await Future.delayed(const Duration(seconds: 1));
        if (isRecording && !isPaused) {
          setState(() {
            recordingTime++;
            recordingProgress = recordingTime / maxDuration;
          });
          if (recordingTime >= maxDuration) {
            _stopRecording();
            return false;
          }
        }
      } else {
        await Future.delayed(const Duration(milliseconds: 100));
      }
      return isRecording;
    });
  }

  void _togglePause() {
    setState(() { isPaused = !isPaused; });
  }

  void _stopRecording() {
    setState(() { isRecording = false; hasRecording = true; });
  }
}

4.4 历史记录管理

历史记录页面使用 Tab 布局实现分类筛选功能。页面顶部的 TabBar 组件包含"全部"、"截图"和"视频"三个选项卡,通过 selectedTab 状态变量控制当前选中项。媒体列表使用 GridView.builder 实现网格布局,每个网格项展示缩略图和文件信息。

dart 复制代码
class MediaHistoryPage extends StatefulWidget {
  const MediaHistoryPage({super.key});
  @override
  State<MediaHistoryPage> createState() => _MediaHistoryPageState();
}

class _MediaHistoryPageState extends State<MediaHistoryPage> {
  String selectedTab = 'all';
  final List<Map<String, String>> allItems = [
    {'id': '1', 'type': 'screenshot', 'name': 'Screenshot_20260502_001.jpg', 'date': '今天 12:30', 'size': '2.5 MB'},
    {'id': '2', 'type': 'video', 'name': 'ScreenRecord_20260501.mp4', 'date': '昨天 23:15', 'size': '45.2 MB'},
  ];

  List<Map<String, String>> get filteredItems {
    if (selectedTab == 'all') return allItems;
    return allItems.where((item) => item['type'] == selectedTab).toList();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(children: [
        _buildTabBar(),
        Expanded(child: filteredItems.isNotEmpty ? _buildMediaGrid() : _buildEmptyView()),
      ]),
    );
  }

  Widget _buildTabBar() {
    return Row(children: [
      _buildTabItem('全部', 'all', allItems.length),
      _buildTabItem('截图', 'screenshot', screenshotCount),
      _buildTabItem('视频', 'video', videoCount),
    ]);
  }

  Widget _buildMediaGrid() {
    return GridView.builder(
      padding: const EdgeInsets.all(16),
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2, crossAxisSpacing: 12, mainAxisSpacing: 12,
      ),
      itemCount: filteredItems.length,
      itemBuilder: (context, index) => _buildMediaItem(filteredItems[index]),
    );
  }
}

五、运行效果展示

应用开发完成后,在 OpenHarmony 设备上运行测试,以下为各功能模块的运行效果截图:

主页面运行截图

主页面清晰展示了截图工具和录屏工具两大功能区域,每个功能卡片配有图标和说明文字,用户可以快速了解各项功能并点击进入相应页面。

全屏截图运行截图

点击全屏截图后,进入截图界面,用户点击圆形按钮即可完成截图操作,界面底部显示加载进度。

区域截图运行截图

区域截图界面中,用户可以通过拖动手势自由选择截图区域,选区框实时显示选区大小,松开手指后显示完成和取消按钮。

录屏功能运行截图

录屏界面展示了录制时长、画质和帧率等设置选项,用户点击录制按钮开始录制,录制过程中实时显示时间和进度条。

六、代码托管

本项目完整代码已托管至 AtomGit 平台,仓库地址如下:

代码仓库链接: https://atomgit.com/maaath/media_tool_app

开发者可通过上述链接获取完整项目代码,包括所有页面组件、路由配置和依赖管理文件。建议使用支持 OpenHarmony 的 Flutter 环境进行构建和运行。

七、总结与展望

本文详细介绍了使用 Flutter for OpenHarmony 开发媒体工具应用的完整过程,涵盖了从项目创建、界面设计到功能实现的各个环节。通过实际案例展示了 Flutter 声明式 UI 编程范式在鸿蒙平台上的应用,以及如何利用 Flutter 的跨平台特性实现代码的统一管理。

当前实现的功能已经能够满足基本的截图和录屏需求,但在实际应用中仍有改进空间。未来的优化方向包括:集成原生截图和录屏能力以提升性能和稳定性;增加图片编辑功能如下载、标注等;优化长截图的滚动拼接算法;添加视频剪辑和压缩功能等。

Flutter for OpenHarmony 生态正在快速发展,越来越多的 Flutter 库已经完成鸿蒙化适配,这为开发者提供了更丰富的选择。希望本文能够为正在进行鸿蒙应用开发的开发者提供参考和借鉴,共同推动开源鸿蒙跨平台生态的繁荣发展。


相关推荐
研究点啥好呢2 小时前
网易Java后端开发工程师面试题精选:10道高频考题+答案解析
华为·数据挖掘·数据分析
nashane2 小时前
HarmonyOS 6学习:应用推广引擎评论管理与长截图自动拼接实战
学习·华为·harmonyos·harmonyos 5
key_3_feng2 小时前
鸿蒙基于润和DAYU200(RK3568)开发板的系统移植与实战开发
华为·harmonyos
Swift社区2 小时前
Store + System:鸿蒙游戏黄金分层
游戏·华为·harmonyos
_waylau2 小时前
历时三年《鸿蒙系统(HarmonyOS)移动开发实战》简介
华为·harmonyos·鸿蒙·鸿蒙系统
想你依然心痛2 小时前
HarmonyOS 6(API 23)实战:打造“空间相册“——基于 Face AR 表情驱动 + 沉浸光感悬浮导航的 PC 端沉浸式照片浏览系统
华为·ar·harmonyos·悬浮导航·沉浸光感
maaath2 小时前
【maaath】 Flutter for OpenHarmony 快捷工具箱应用实战开发
flutter·华为·harmonyos
maaath2 小时前
【maaath】Flutter for OpenHarmony 实战:茶叶茶艺应用开发详解
flutter·华为·harmonyos
maaath2 小时前
【maaath】Flutter for OpenHarmony 的手办展示应用开发实践
flutter·华为·harmonyos