Flutter 不写 App 了,来做一个“随机漂浮的宇宙星尘”互动屏保**
2026/5/7 4:01:27 网站建设 项目流程

在 Flutter 的世界里,我们总在构建按钮、列表、路由和状态管理 ——
但今天,我们不做一个 App。
我们不做登录页,不调 REST API,也不用 Provider 或 Bloc。

我们要用 Flutter 写一场会呼吸的星空
点开应用,屏幕化作深空,无数微小光点如星尘般随机漂浮,有的闪烁,有的拉出尾迹,当你触碰屏幕时,它们像受惊般四散逃逸。

这不是 UI 教程,而是一次对Flutter 与粒子系统结合可能性的实验
代码不多,美感十足,适合放进你的个人作品集,说一句:“看,这是我用 Flutter 画的宇宙。”


🌌 项目目标

  • 创建一个全屏黑色背景
  • 生成多个“星尘粒子”,具有:
    • 随机位置、大小、亮度
    • 缓慢移动(伪三维视差感)
    • 触摸时产生排斥力场,粒子向外加速飞散
  • 使用纯 Dart +CustomPainter实现,无第三方依赖

它不是实用工具,但它让技术有了诗意。


✅ 初始化项目

flutter create cosmic_dustcdcosmic_dust

无需添加任何插件,全部使用 Flutter 原生能力。


🌠 第一步:定义“星尘”类

每个粒子都是一个会动的小光点。

models/dust_particle.dart
import'dart:math';import'package:flutter/material.dart';classDustParticle{Offsetposition;double size;Colorcolor;double speed;double angle;DustParticle(this.position):size=Random().nextDouble()*2.5+0.8,color=Colors.white.withOpacity(Random().nextDouble()*0.7+0.3),speed=Random().nextDouble()*0.3+0.1,angle=Random().nextDouble()*2*pi;voidmove(){// 微移(模拟缓慢漂浮)position+=Offset(cos(angle)*speed,sin(angle)*speed,);// 小概率轻微转向(更自然)if(Random().nextDouble()<0.02){angle+=(Random().nextDouble()-0.5)*0.3;}}voidrepelFrom(Offsetsource,double force){finaldx=position.dx-source.dx;finaldy=position.dy-source.dy;finaldistance=sqrt(dx*dx+dy*dy);if(distance<150){finalstrength=(150-distance)/150*force;position-=Offset(dx,dy).normalize()*strength;}}}

🖌️ 第二步:自定义绘制星尘动画

我们将用CustomPainter手绘所有粒子,并加入高斯模糊般的发光效果。

painters/cosmic_painter.dart
// painters/cosmic_painter.dartimport'package:flutter/material.dart';import'../models/dust_particle.dart';classCosmicPainterextendsCustomPainter{finalList<DustParticle>particles;CosmicPainter(this.particles);@overridevoidpaint(Canvascanvas,Sizesize){finalpaint=Paint();// 绘制渐变夜空背景constbgGradient=RadialGradient(colors:[Color(0xFF000011),Color(0xFF0A0022)]);paint.shader=bgGradient.createShader(Rect.fromLTWH(0,0,size.width,size.height));canvas.drawRect(Rect.fromLTWH(0,0,size.width,size.height),paint);// 绘制每个粒子(带光晕)for(finalpinparticles){finalcenter=p.position;// 多层透明圆叠加,制造发光感for(int i=0;i<3;i++){paint..color=p.color.withAlpha((p.color.alpha*(0.4-i*0.2)).toInt())..maskFilter=constMaskFilter.blur(BlurStyle.normal,0.8);finalradius=p.size*(1.0+i*1.5);canvas.drawCircle(center,radius,paint);}}}@overrideboolshouldRepaint(CosmicPainteroldDelegate)=>true;}

⚙️ 第三步:主动画控制器

使用AnimationController驱动每一帧更新。

widgets/cosmic_canvas.dart
// widgets/cosmic_canvas.dartimport'package:flutter/material.dart';import'../models/dust_particle.dart';import'../painters/cosmic_painter.dart';classCosmicCanvasextendsStatefulWidget{constCosmicCanvas({super.key});@overrideState<CosmicCanvas>createState()=>_CosmicCanvasState();}class_CosmicCanvasStateextendsState<CosmicCanvas>withSingleTickerProviderStateMixin{lateAnimationController_controller;lateList<DustParticle>_particles;Offset?_touchPoint;@overridevoidinitState(){super.initState();_controller=AnimationController(vsync:this,duration:null)..repeat();// 初始化 60 个星尘粒子_particles=List.generate(60,(_){returnDustParticle(Offset(Random().nextDouble()*MediaQuery.sizeOf(context).width,Random().nextDouble()*MediaQuery.sizeOf(context).height,),);});// 每帧更新粒子位置_controller.addListener((){if(_touchPoint!=null){// 受力场影响for(varpin_particles){p.repelFrom(_touchPoint!,2.5);}}// 自然漂移for(varpin_particles){p.move();// 边界环绕finalsize=MediaQuery.sizeOf(context);p.position=Offset((p.position.dx+size.width)%size.width,(p.position.dy+size.height)%size.height,);}if(mounted)setState((){});});}@overridevoiddispose(){_controller.dispose();super.dispose();}@overrideWidgetbuild(BuildContextcontext){returnGestureDetector(onPanUpdate:(details){finalrenderBox=context.findRenderObject()asRenderBox;_touchPoint=renderBox.globalToLocal(details.globalPosition);},onPanEnd:(_)=>_touchPoint=null,child:CustomPaint(painter:CosmicPainter(_particles),size:Size.infinite,),);}}

🔗 最后:接入主界面

main.dart
import'package:flutter/material.dart';import'widgets/cosmic_canvas.dart';voidmain()=>runApp(constApp());classAppextendsStatelessWidget{constApp({super.key});@overrideWidgetbuild(BuildContextcontext){returnMaterialApp(debugShowCheckedModeBanner:false,home:Scaffold(backgroundColor:Colors.black,body:Container(decoration:constBoxDecoration(gradient:LinearGradient(begin:Alignment.topCenter,end:Alignment.bottomCenter,colors:[Color(0xFF000011),Color(0xFF0A0022)],),),child:constCosmicCanvas(),),),);}}

🎥 运行效果

flutter run

你会看到:

  • 屏幕中散布着几十个微微发光的小点,像宇宙中的尘埃
  • 它们缓慢漂移,有的亮些,有的暗些,形成深度错觉
  • 当你手指划过,星尘像被推开一样向四周散去
  • 抬起手指后,恢复宁静漂浮

如同触摸了整个宇宙的一角。


🧠 技术亮点

特性说明
CustomPainter + maskFilter制造发光/模糊效果
极坐标运动(angle + speed)更自然的非线性移动
排斥力场计算模拟物理反馈
环绕边界处理粒子永不消失
单帧setState()驱动轻量高效

🚀 扩展想法

  • 加入音效响应:音乐节奏触发星群脉冲
  • “双指捏合”缩放视角,模拟太空望远镜
  • 长按保存当前星空为壁纸(repaintBoundary截图)
  • 多设备同步:WebSocket 广播触摸事件,实现“同一片星空”
  • 打包成桌面屏保或数字艺术装置

结语:Flutter 是数字画笔

我们总说 Flutter 快、跨平台、好维护,
却忘了它最迷人的地方是:

你可以用代码,创造原本只存在于梦境中的画面。

这团星尘不会帮你找工作,也不会带来用户增长,
但它提醒我们:
编程不只是逻辑与工程,也可以是视觉、情感与想象力的延伸

下次当你打开编辑器,别急着写业务逻辑。
试试问自己:

“如果我能控制每一个像素,我想看见什么?”

也许答案,就是一片属于你的星空。


“We are all made of starstuff.”
— Carl Sagan
而现在,我们也能用 Flutter 重现它。✨
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询