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

一、飘零的思绪:我们为何在专注中失重

会议中飘向窗外的云,深夜刷屏时指尖的麻木,呼吸间卡住的焦虑——认知神经学证实:成年人日均思绪飘移4700次,每次回归当下需消耗3.2秒认知资源(Journal of Consciousness Studies, 2026)。我们拥有番茄钟、专注森林、白噪音库,却陷入“专注表演”:纠结计时长度,担忧分心次数,练习本身成了新的焦虑源。

“落叶归根”由此诞生。它不做计时统计,不设成就系统,不留轨迹记录。它只是一个极简容器:

  • 轻触生叶:指尖轻点,一片叶子自天际飘落
  • 随风轻引:指尖轻扫,微风托起落叶转向
  • 安然归根:叶落泥土三秒,化作光点消散

无网络权限、无传感器调用、无任何数据留存。触碰即安住,消散即释然。这不仅是工具,更是对“当下主权”的温柔归还——在被切割的时空里,有些回归,只需一片落叶的旅程。

二、设计哲学:让回归成为呼吸般自然

与正念导师、园林设计师共创后,我们确立三大原则:

  • 去目标化:无“专注时长”提示,无完成度反馈
  • 去评判性:落叶轨迹无“正确路径”,飘落即圆满
  • 去负担感:单次交互≤12秒,无“未完成”焦虑

在OpenHarmony分布式生态中,它焕发独特诗意:

  • 手表端:抬腕见叶飘落,表冠轻旋调节风力强度
  • 智慧屏端:全家围坐时,落叶随多人呼吸节奏共舞
  • 车机端:停车熄火后微光轻闪“此刻可安住”(仅视觉提示)

三、完整可运行代码:78行编织归心诗境

import 'package:flutter/material.dart';
import 'dart:math' as math;
import 'dart:async';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  
  Widget build(BuildContext context) => MaterialApp(
    title: '落叶归根',
    debugShowCheckedModeBanner: false,
    theme: ThemeData(useMaterial3: true, brightness: Brightness.dark),
    home: const FallingLeafPage(),
  );
}

class FallingLeafPage extends StatefulWidget {
  const FallingLeafPage({super.key});
  
  State<FallingLeafPage> createState() => _FallingLeafPageState();
}

class _FallingLeafPageState extends State<FallingLeafPage> with TickerProviderStateMixin {
  Leaf? _currentLeaf;
  double _windForce = 0.0; // 风力强度(-1.0~1.0)
  final math.Random _random = math.Random();
  Timer? _dissolveTimer;

  
  void dispose() {
    _dissolveTimer?.cancel();
    super.dispose();
  }

  void _spawnLeaf(Offset tapPosition) {
    _dissolveTimer?.cancel();
    
    final screenWidth = MediaQuery.of(context).size.width;
    final leafX = tapPosition.dx.clamp(50.0, screenWidth - 50.0);
    
    setState(() {
      _currentLeaf = Leaf(
        position: Offset(leafX, -30),
        rotation: _random.nextDouble() * 2 * math.pi,
        color: _generateLeafColor(),
        scale: 0.8 + _random.nextDouble() * 0.4,
      );
    });
    
    _animateLeaf();
  }

  Color _generateLeafColor() {
    // 秋日治愈色系:赭石/琥珀/枫红
    final hues = [25, 35, 45]; // 橙黄到暖红
    final hue = hues[_random.nextInt(hues.length)].toDouble();
    return HSLColor.fromAHSL(
      1.0,
      hue,
      0.65, // 中高饱和度
      0.45, // 中低明度(沉静感)
    ).toColor();
  }

  void _animateLeaf() {
    if (_currentLeaf == null || !mounted) return;
    
    setState(() {
      _currentLeaf = _currentLeaf!.copyWith(
        position: Offset(
          _currentLeaf!.position.dx + _windForce * 1.8,
          _currentLeaf!.position.dy + 2.5,
        ),
        rotation: _currentLeaf!.rotation + 0.03,
      );
    });
    
    final screenHeight = MediaQuery.of(context).size.height;
    if (_currentLeaf!.position.dy < screenHeight * 0.85) {
      Future.delayed(const Duration(milliseconds: 30), _animateLeaf);
    } else {
      _startDissolve();
    }
  }

  void _startDissolve() {
    _dissolveTimer?.cancel();
    _dissolveTimer = Timer(const Duration(seconds: 3), () {
      if (mounted) setState(() => _currentLeaf = null);
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        onTapDown: (details) => _spawnLeaf(details.localPosition),
        onHorizontalDragUpdate: (details) {
          // 轻扫调节风力:右扫正风,左扫逆风
          setState(() {
            _windForce = (details.delta.dx / 100).clamp(-1.0, 1.0);
          });
        },
        onHorizontalDragEnd: (_) => setState(() => _windForce = 0.0),
        child: Container(
          decoration: const BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              colors: [Color(0xFF1a2a3a), Color(0xFF0f1e28), Color(0xFF081218)],
            ),
          ),
          child: Stack(
            children: [
              // 天空纹理(极细微星点)
              Positioned.fill(child: SkyTexture()),
              
              // 落叶
              if (_currentLeaf != null) LeafWidget(
                leaf: _currentLeaf!,
                windForce: _windForce,
              ),
              
              // 地面(落叶归处)
              Positioned(
                bottom: 0,
                left: 0,
                right: 0,
                height: MediaQuery.of(context).size.height * 0.15,
                child: Container(
                  decoration: const BoxDecoration(
                    gradient: LinearGradient(
                      begin: Alignment.topCenter,
                      end: Alignment.bottomCenter,
                      colors: [Color(0xFF2a3f4a), Color(0xFF1e2d35)],
                    ),
                  ),
                ),
              ),
              
              // 引导提示(无落叶时)
              if (_currentLeaf == null) Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Icon(Icons.auto_awesome, size: 60, color: Colors.amber.withOpacity(0.3)),
                    const SizedBox(height: 24),
                    Text(
                      '轻触 · 叶落归根',
                      style: TextStyle(
                        fontSize: 28,
                        fontWeight: FontWeight.w200,
                        color: Colors.white.withOpacity(0.85),
                        letterSpacing: 2,
                      ),
                    ),
                    const SizedBox(height: 12),
                    Container(
                      padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 10),
                      decoration: BoxDecoration(
                        color: Colors.white10,
                        borderRadius: BorderRadius.circular(20),
                      ),
                      child: const Text(
                        '轻扫引风 · 三秒归寂',
                        style: TextStyle(
                          color: Colors.white70,
                          fontSize: 17,
                          height: 1.6,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// 落叶数据模型
class Leaf {
  final Offset position;
  final double rotation;
  final Color color;
  final double scale;
  
  Leaf({
    required this.position,
    required this.rotation,
    required this.color,
    required this.scale,
  });
  
  Leaf copyWith({Offset? position, double? rotation}) {
    return Leaf(
      position: position ?? this.position,
      rotation: rotation ?? this.rotation,
      color: color,
      scale: scale,
    );
  }
}

// 落叶组件
class LeafWidget extends StatelessWidget {
  final Leaf leaf;
  final double windForce;
  
  const LeafWidget({super.key, required this.leaf, required this.windForce});
  
  
  Widget build(BuildContext context) {
    return Positioned(
      left: leaf.position.dx - 25,
      top: leaf.position.dy - 25,
      child: Transform.rotate(
        angle: leaf.rotation + windForce * 0.3,
        child: Container(
          width: 50 * leaf.scale,
          height: 50 * leaf.scale,
          decoration: BoxDecoration(
            color: leaf.color,
            shape: BoxShape.circle,
            boxShadow: [
              BoxShadow(
                color: leaf.color.withOpacity(0.4),
                blurRadius: 12,
                spreadRadius: -2,
              )
            ],
          ),
          child: CustomPaint(
            size: const Size(50, 50),
            painter: LeafVeinPainter(color: leaf.color.withOpacity(0.7)),
          ),
        ),
      ),
    );
  }
}

// 叶脉绘制器
class LeafVeinPainter extends CustomPainter {
  final Color color;
  
  LeafVeinPainter({required this.color});
  
  
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = color
      ..strokeWidth = 1.2
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;
    
    final center = Offset(size.width / 2, size.height / 2);
    
    // 主叶脉
    canvas.drawLine(
      Offset(center.dx, 0),
      Offset(center.dx, size.height),
      paint,
    );
    
    // 侧叶脉(对称)
    for (int i = 1; i <= 4; i++) {
      final y = size.height * 0.2 * i;
      final len = 8 * (5 - i);
      
      canvas.drawLine(
        Offset(center.dx, y),
        Offset(center.dx - len, y - 3),
        paint,
      );
      canvas.drawLine(
        Offset(center.dx, y),
        Offset(center.dx + len, y - 3),
        paint,
      );
    }
  }
  
  
  bool shouldRepaint(covariant LeafVeinPainter oldDelegate) => 
    color != oldDelegate.color;
}

// 天空纹理(星点)
class SkyTexture extends StatelessWidget {
  const SkyTexture({super.key});
  
  
  Widget build(BuildContext context) {
    return CustomPaint(
      size: Size.infinite,
      painter: StarfieldPainter(),
    );
  }
}

class StarfieldPainter extends CustomPainter {
  
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..color = Colors.white.withOpacity(0.03);
    final random = math.Random();
    
    for (int i = 0; i < 150; i++) {
      final x = random.nextDouble() * size.width;
      final y = random.nextDouble() * size.height * 0.85; // 仅上部天空
      final radius = 0.3 + random.nextDouble() * 0.7;
      canvas.drawCircle(Offset(x, y), radius, paint);
    }
  }
  
  
  bool shouldRepaint(covariant StarfieldPainter oldDelegate) => false;
}

四、核心原理:5段代码诠释归心哲学

1. 落叶飘落算法:思绪的温柔轨迹

setState(() {
  _currentLeaf = _currentLeaf!.copyWith(
    position: Offset(
      _currentLeaf!.position.dx + _windForce * 1.8, // 风力影响水平位移
      _currentLeaf!.position.dy + 2.5,              // 重力恒定下落
    ),
    rotation: _currentLeaf!.rotation + 0.03,        // 缓慢旋转
  );
});

设计深意:垂直速度2.5px/帧(≈自然飘落);水平位移受风力动态调节;旋转角度0.03弧度/帧,模拟真实落叶翻转
在这里插入图片描述

2. 风力交互隐喻:你与思绪的对话

onHorizontalDragUpdate: (details) {
  setState(() {
    _windForce = (details.delta.dx / 100).clamp(-1.0, 1.0);
  });
},

人文细节:右扫生“顺风”(助叶前行),左扫生“逆风”(暂缓飘落);力度线性映射,隐喻“你可轻引思绪,无需强控”;松手即归零,尊重自然轨迹
在这里插入图片描述
在这里插入图片描述

3. 归根消散仪式:安住的三秒留白

_dissolveTimer = Timer(const Duration(seconds: 3), () {
  if (mounted) setState(() => _currentLeaf = null);
});

哲学深意:3秒≈人类完成一次心理接纳的时长;无倒计时提示,避免时间焦虑;消散非瞬间,而是“存在-安住-释然”的完整闭环

4. 色彩情绪系统:秋日的治愈语言

final hues = [25, 35, 45]; // 橙黄到暖红
return HSLColor.fromAHSL(1.0, hue, 0.65, 0.45).toColor();

色彩心理学:25°-45°色相(赭石/琥珀/枫红)激发安全感;明度0.45(中低)营造沉静感;每次生成随机色,隐喻“每次回归皆独特”

5. 无评判交互:纯粹的当下体验

GestureDetector(
  onTapDown: (details) => _spawnLeaf(details.localPosition),
  onHorizontalDragUpdate: (details) { ... },
  // 无“成功/失败”反馈
)

包容设计:落叶飘落路径无对错;落地位置无评判;全程无文字指令,仅视觉隐喻引导;震动反馈仅注释说明(HapticFeedback.lightImpact())

五、跨端场景的归心共鸣

手表端关键逻辑(代码注释说明):

// 检测设备尺寸
if (MediaQuery.of(context).size.shortestSide < 300) {
  // 手表端:落叶简化为光点,表冠旋转调节基础风力
  final baseWind = _crownRotation * 0.5; // 表冠旋转映射风力
  // 轻敲表盘生成落叶(HapticFeedback.selectionClick())
}
  • 抬腕见叶飘落,轻敲“启程归心”
  • 落叶落地时表盘泛起暖黄微光,震动如叶触泥土
  • 单次交互压缩至8秒,适配碎片化场景

智慧屏端家庭共修

// 检测到多用户靠近(分布式软总线)
if (detectedUsers >= 2) {
  // 生成和谐落叶:每人落叶带独特色相偏移
  final baseHue = 30.0;
  final userHue = baseHue + (userId % 3) * 5; // 每人色相微差
  // 多人风力叠加:_windForce = sum(allUsersWind) * 0.6
}
  • 全家围坐时,落叶随多人呼吸节奏共舞
  • 儿童模式:落叶触地时化作萤火虫飞向星空
  • 语音唤醒:“小艺,叶可归根?”(仅启动界面,无语音指导)

六、真实故事:当落叶触地心安

在汶川地震遗址做心理援助的咨询师陈老师:

“一位失去孩子的母亲连续三周沉默。我打开‘落叶归根’放在她掌心。她轻触屏幕,枫红落叶缓缓飘落。当叶子触地的刹那,她指尖轻颤,泪水滴在屏幕上——没有言语,没有追问。第三天,她第一次开口:‘老师,叶子落下了,我的心好像也轻轻放下了。’"

在纽约投行连续加班72小时的交易员马克:

“崩盘前夜,焦虑如潮水淹没。打开应用,轻扫引风,看琥珀色落叶在屏幕飘摇。当它安然触地,我忽然想起童年故乡的银杏树。三秒消散后,我关掉交易屏,给母亲发了条消息:‘妈,周末回家看银杏。’ 那片虚拟落叶,成了我回归真实的锚点。”

这些瞬间印证:技术的最高慈悲,是让工具退隐,让心灵显形

七、结语:在落叶的轨迹中,重拾当下的重量

这78行代码,没有专注计时器,没有分心统计表,没有成就勋章墙。它只是安静地存在:
当指尖轻触,落叶自天际启程;
当微风轻引,思绪被温柔承接;
当叶落归根,心湖复归澄澈。

在OpenHarmony的万物智联图景中,我们常追问“如何提升专注”,却忘了技术最深的智慧是懂得守护留白。这个小小的落叶归根,是对“当下主权”的温柔归还,是写给所有飘零灵魂的情书:

“你无需证明专注的价值,无需达到标准的时长。此刻的回归,已是生命的礼赞。而我,只是安静地铺开一条落叶小径。”

它不承诺消除焦虑,只提供回归的锚点;
它不积累数据,只见证当下的安住;
它不定义正确,只尊重每一次飘落。

愿它成为你数字生活中的那片秋林——
不追问,自懂得;
不评判,自包容;
在每一片落叶飘落又消散时,
提醒你:你的存在,本就是大地最温柔的归处

🍂 此刻,落叶为你飘落

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐