news 2026/4/16 14:22:23

Flutter for OpenHarmony 打造沉浸式呼吸引导应用:用动画疗愈身心

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flutter for OpenHarmony 打造沉浸式呼吸引导应用:用动画疗愈身心

Flutter for OpenHarmony 打造沉浸式呼吸引导应用:用动画疗愈身心

在快节奏的现代生活中,呼吸——这一最自然却常被忽视的生命节律——正成为连接身心、缓解焦虑的关键工具。科学研究表明,有意识的深呼吸练习能有效降低心率、减轻压力、提升专注力。然而,许多人虽知其益,却苦于缺乏引导而难以坚持。

🌐 加入社区 欢迎加入开源鸿蒙跨平台开发者社区,获取最新资源与技术支持: 👉开源鸿蒙跨平台开发者社区


完整效果


一、设计理念:让呼吸“可见”

该应用的核心思想是“可视化呼吸”

💡目标:用户无需思考“现在该做什么”,只需跟随视觉引导,自然进入呼吸节奏。


二、呼吸训练模型:4-7-8 的变体

虽然代码中未显式写出各阶段时长,但从AnimationController(duration: const Duration(seconds: 24))和四阶段均分可推断,每阶段约6 秒,形成一个6-6-6-6的对称循环:

  1. 吸气(Inhale):6 秒,缓慢深吸;
  2. 屏息(Hold):6 秒,保持气息;
  3. 呼气(Exhale):6 秒,缓慢深呼;
  4. 空息(Hold):6 秒,保持空腔。

🌿 这种对称设计简化了认知负担,适合初学者建立呼吸节奏感。


三、核心技术实现

1. 动画驱动:AnimationController+CurvedAnimation

_animationController=AnimationController(duration:constDuration(seconds:24),// 一个完整循环24秒vsync:this,);_breathAnimation=Tween<double>(begin:0.3,end:1.0).animate(CurvedAnimation(parent:_animationController,curve:Curves.easeInOut),);

2. 阶段识别:从连续动画到离散状态

_breathAnimation.addListener((){setState((){_currentPhase=(_animationController.value*4).floor();if(_currentPhase>=4)_currentPhase=3;});});

3. 循环控制:自动重置与计数

..addStatusListener((status){if(status==AnimationStatus.completed){setState((){_cycleCount++;_currentPhase=0;});_animationController.reset();if(_isRunning)_animationController.forward();// 自动开始下一循环}});

四、UI/UX 设计亮点

1. 色彩心理学应用

阶段颜色心理暗示
吸气🟢 绿色 (green.shade400)生长、能量、吸入生命力
屏息(吸后)🟡 琥珀色 (amber.shade300)温暖、稳定、蓄势待发
呼气🔴 红色 (red.shade400)释放、排出、代谢废物
屏息(呼后)🔵 蓝色 (blue.shade300)冷静、空灵、内在平静

每种颜色不仅用于中心球,还同步应用于:

2. 多层次视觉反馈

3. 交互设计


五、代码结构与健壮性


六、应用场景与扩展可能

适用场景

可扩展方向


七、结语:技术为身心服务

这段代码远不止是一个动画演示,它体现了“科技向善”的理念——用精巧的技术手段,服务于最基础的人类需求:呼吸

完整代码

import'package:flutter/material.dart';voidmain(){runApp(const BreathTrainerApp());}class BreathTrainerApp extends StatelessWidget{const BreathTrainerApp({super.key});@override Widget build(BuildContext context){returnMaterialApp(debugShowCheckedModeBanner: false, title:'🌬️ 呼吸引导', theme: ThemeData(brightness: Brightness.dark, scaffoldBackgroundColor: const Color(0xFF0F172A), primarySwatch: Colors.blue, textTheme: const TextTheme(displayLarge: TextStyle(fontFamily:'Arial', fontWeight: FontWeight.w300),),), home: const BreathTrainerScreen(),);}}class BreathTrainerScreen extends StatefulWidget{const BreathTrainerScreen({super.key});@override State<BreathTrainerScreen>createState()=>_BreathTrainerScreenState();}class _BreathTrainerScreenState extends State<BreathTrainerScreen>with TickerProviderStateMixin{late AnimationController _animationController;late Animation<double>_breathAnimation;int _currentPhase=0;//0: inhale,1: hold,2: exhale,3: hold bool _isRunning=false;int _cycleCount=0;final List<String>_phases=['吸气','屏息','呼气','屏息'];final List<Color>_phaseColors=[Colors.green.shade400, Colors.amber.shade300, Colors.red.shade400, Colors.blue.shade300,];final List<String>_instructions=['缓慢深吸气...','保持呼吸...','缓慢深呼气...','保持空息...',];final List<IconData>_phaseIcons=[Icons.arrow_upward, Icons.pause_circle_outline, Icons.arrow_downward, Icons.pause_circle_outline,];@override voidinitState(){super.initState();_animationController=AnimationController(duration: const Duration(seconds:24), vsync: this,)..addStatusListener((status){ if(status==AnimationStatus.completed){ setState((){ _cycleCount++;_currentPhase=0;});_animationController.reset();if(_isRunning)_animationController.forward();} });_breathAnimation=Tween<double>(begin:0.3,end:1.0).animate(CurvedAnimation(parent:_animationController,curve:Curves.easeInOut),)..addListener((){ setState((){ _currentPhase=(_animationController.value*4).floor();if(_currentPhase>=4)_currentPhase=3;});});} @override void dispose(){ _animationController.dispose();super.dispose();} void _toggleTraining(){ setState((){ _isRunning=!_isRunning;if(_isRunning){ _animationController.forward();} else { _animationController.stop();} });} void _resetTraining(){ setState((){ _isRunning=false;_cycleCount=0;_currentPhase=0;_animationController.reset();});} @override Widget build(BuildContext context){ final currentColor=_phaseColors[_currentPhase];final safeAreaHeight=MediaQuery.of(context).padding.top;return Scaffold(body:Container(decoration:BoxDecoration(gradient:LinearGradient(begin:Alignment.topCenter,end:Alignment.bottomCenter,colors:[ Colors.black87,Color.lerp(Colors.black87,currentColor.withOpacity(0.15),0.3)!,Color.lerp(Colors.black87,currentColor.withOpacity(0.05),0.6)!,Colors.black87,],),),child:SafeArea(child:Column(children:[//顶部状态栏 Padding(padding:EdgeInsets.only(top:safeAreaHeight+8,left:20,right:20),child:Row(mainAxisAlignment:MainAxisAlignment.spaceBetween,children:[ Column(crossAxisAlignment:CrossAxisAlignment.start,children:[ const Text('🌬️ 呼吸引导',style:TextStyle(fontSize:28,fontWeight:FontWeight.bold),),const SizedBox(height:4),Text('${_cycleCount} 次循环',style:const TextStyle(fontSize:16,color:Colors.grey),),],),Container(padding:const EdgeInsets.symmetric(horizontal:16,vertical:6),decoration:BoxDecoration(color:_isRunning?Colors.green.withOpacity(0.2):Colors.red.withOpacity(0.2),borderRadius:BorderRadius.circular(20),),child:Row(children:[ Icon(_isRunning?Icons.play_arrow:Icons.stop,size:18,color:_isRunning?Colors.green:Colors.red,),const SizedBox(width:4),Text(_isRunning?'进行中':'已暂停',style:TextStyle(fontSize:14,color:_isRunning?Colors.green:Colors.red,fontWeight:FontWeight.w600,),),],),),],),),const SizedBox(height:30),//呼吸可视化区域 Expanded(child:Stack(alignment:Alignment.center,children:[//背景脉动圆 AnimatedBuilder(animation:_breathAnimation,builder:(context,child){ return Container(width:320*_breathAnimation.value,height:320*_breathAnimation.value,decoration:BoxDecoration(shape:BoxShape.circle,gradient:RadialGradient(colors:[ currentColor.withOpacity(0.15),currentColor.withOpacity(0.05),],),),);},),//中心呼吸球 AnimatedBuilder(animation:_breathAnimation,builder:(context,child){ return Container(width:180*_breathAnimation.value,height:180*_breathAnimation.value,decoration:BoxDecoration(shape:BoxShape.circle,gradient:RadialGradient(colors:[ currentColor.withOpacity(0.9),currentColor.withOpacity(0.7),],),boxShadow:[ BoxShadow(color:currentColor.withOpacity(0.4),blurRadius:30,spreadRadius:10,),],),child:Center(child:Icon(_phaseIcons[_currentPhase],size:60*_breathAnimation.value,color:Colors.white,),),);},),//阶段指示器 Positioned(bottom:40,child:Container(padding:const EdgeInsets.symmetric(horizontal:24,vertical:12),decoration:BoxDecoration(color:Colors.black87.withOpacity(0.7),borderRadius:BorderRadius.circular(30),border:Border.all(color:currentColor.withOpacity(0.5)),), child: Row(mainAxisSize: MainAxisSize.min, children:[Icon(_phaseIcons[_currentPhase], color: currentColor, size:24,), const SizedBox(width:12), Text(_phases[_currentPhase], style: TextStyle(fontSize:20, fontWeight: FontWeight.bold, color: currentColor,),),],),),),],),), // 指导文字 Padding(padding: const EdgeInsets.only(bottom:24), child: Text(_instructions[_currentPhase], style: TextStyle(fontSize:22, fontWeight: FontWeight.w300, color: currentColor, height:1.5,), textAlign: TextAlign.center,),), // 控制按钮 Container(padding: const EdgeInsets.symmetric(horizontal:24, vertical:20), decoration: BoxDecoration(color: Colors.black87.withOpacity(0.8), borderRadius: const BorderRadius.vertical(top: Radius.circular(30)),), child: Column(children:[// 进度指示器 Row(children: List.generate(4,(index){final isActive=index==_currentPhase;returnExpanded(child: Padding(padding: const EdgeInsets.symmetric(horizontal:4), child: Container(height:8, decoration: BoxDecoration(color: isActive ? _phaseColors[index]:_phaseColors[index].withOpacity(0.3), borderRadius: BorderRadius.circular(4),),),),);}),), const SizedBox(height:24), // 主控制按钮 Row(children:[Expanded(child: OutlinedButton.icon(onPressed: _resetTraining, icon: const Icon(Icons.refresh, size:20), label: const Text('重置', style: TextStyle(fontSize:16)), style: OutlinedButton.styleFrom(foregroundColor: Colors.grey, side: const BorderSide(color: Colors.grey), padding: const EdgeInsets.symmetric(vertical:16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),),),), const SizedBox(width:16), Expanded(flex:2, child: ElevatedButton.icon(onPressed: _toggleTraining, icon: Icon(_isRunning ? Icons.pause:Icons.play_arrow, size:28,), label: Text(_isRunning ?'暂停训练':'开始训练', style: const TextStyle(fontSize:18, fontWeight: FontWeight.bold),), style: ElevatedButton.styleFrom(backgroundColor: _isRunning ? Colors.red:Colors.green, padding: const EdgeInsets.symmetric(vertical:18), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), elevation:4,),),),],),],),),],),),),);}}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 16:43:44

毕业论文无忧:11个AI辅助写作网站精选

人工智能语言模型的快速发展显著改变了学术研究方式&#xff0c;特别是ChatGPT等工具自2022年推出后&#xff0c;AI在学术写作、数据分析、模式识别及决策辅助等领域广泛应用&#xff0c;帮助研究者提升效率。目前已有多种AI工具可支持毕业论文写作全流程&#xff0c;涵盖不同学…

作者头像 李华
网站建设 2026/4/16 16:10:32

11个AI工具助力毕业论文写作,覆盖多学科需求

人工智能语言模型的快速发展显著改变了学术研究方式&#xff0c;特别是自2022年11月ChatGPT发布后&#xff0c;AI工具在学术界的应用迅速普及&#xff0c;帮助研究人员提升效率、节省时间。这些工具能分析数据、识别模式、生成内容并辅助决策&#xff0c;覆盖写作、编辑和研究支…

作者头像 李华
网站建设 2026/4/16 14:23:28

AI赋能:6款高效工具提升论文质量与写作效率

当前学术写作领域已出现一系列融合人工智能技术的创新工具&#xff0c;这些系统依托前沿的自然语言处理算法&#xff0c;能够高效完成论文框架自动构建、文本润色提升以及相似度检测等关键任务&#xff0c;特别适合研究生学位论文写作和学术材料整理等应用场景。值得注意的是&a…

作者头像 李华
网站建设 2026/4/16 13:03:10

AI创作工具横评:10款免费与付费选项性能解析

&#xfffd;&#xfffd; 10大降AIGC平台核心对比速览 排名 工具名称 降AIGC效率 适用场景 免费/付费 1 askpaper ⭐⭐⭐⭐⭐ 学术论文精准降AI 付费 2 秒篇 ⭐⭐⭐⭐⭐ 快速降AIGC降重 付费 3 Aibiye ⭐⭐⭐⭐ 多学科论文降AI 付费 4 Aicheck ⭐⭐⭐⭐…

作者头像 李华