Flutter for OpenHarmony打造一个高颜值 Flutter 天气卡片应用:完整代码深度解析

本文将带你逐层剖析一个完整的 Flutter 天气卡片应用源码,涵盖主题系统、枚举驱动 UI、交互动画、响应式布局与工程化细节,助你掌握现代 Flutter 应用的最佳实践。

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


完整效果
在这里插入图片描述
在这里插入图片描述

一、项目概览

这个天气卡片应用以简洁优雅的卡片形式展示多个城市的实时天气信息。用户可通过左右滑动或点击“下一个”按钮切换城市,界面会根据当前天气类型自动切换配色方案,并伴有流畅的缩放过渡动画。

核心特性包括:

  • 基于 WeatherCondition 枚举的动态 UI 主题
  • 自定义 AppTheme 全局主题系统
  • 卡片切换动画(AnimationController + Transform.scale
  • 响应式布局适配小屏/大屏设备
  • 手势识别(水平滑动切换城市)
  • 丰富的天气数据展示(温度、体感、湿度、风速、UV、AQI 等)

二、架构设计亮点

1. 枚举驱动 UI:WeatherCondition

这是本项目最精妙的设计之一。我们没有硬编码颜色或图标,而是将所有与天气相关的视觉属性封装在 enum WeatherCondition 中:

enum WeatherCondition {
  sunny, cloudy, rainy, stormy, snowy, foggy;

  String get icon => ...;
  String get description => ...;
  Color get backgroundColor => ...;
  Color get gradientStart => ...;
  Color get textColor => ...;
}

在这里插入图片描述

优势

  • 高内聚:每种天气的所有表现逻辑集中一处。
  • 易扩展:新增天气类型只需添加枚举项并实现 getter。
  • 类型安全:避免字符串魔法值,编译器帮你检查。

这是“数据驱动 UI”思想的典型体现——UI 是数据的函数。


2. 全局主题系统:AppTheme

通过静态常量和 ThemeData 工厂方法,统一管理应用色彩体系:

class AppTheme {
  static const Color primaryColor = Color(0xFF4A90E2);
  // ...

  static ThemeData get lightTheme {
    return ThemeData(
      useMaterial3: true,
      colorScheme: ColorScheme.fromSeed(seedColor: primaryColor),
      scaffoldBackgroundColor: backgroundColor,
      // ...
    );
  }
}

在这里插入图片描述

优势

  • 避免颜色散落在各处(如 Colors.blue[500])。
  • 支持未来一键切换深色/浅色主题。
  • 与 Material 3 完美兼容。

三、UI 构建详解

整个 UI 由 _buildWeatherContent() 组织,包含 AppBar、主体卡片、指示器、提示语和 FAB。

1. 动态渐变背景

背景使用 LinearGradient,颜色来源于当前天气的 gradientStartgradientEnd

decoration: BoxDecoration(
  gradient: LinearGradient(
    colors: [
      condition.gradientStart.withValues(alpha: 0.6),
      condition.gradientEnd.withValues(alpha: 0.9),
      Colors.white,
    ],
  ),
)

注意:这里故意加入 Colors.white 作为第三色,使底部更柔和,避免纯色块突兀。


2. 核心组件:天气卡片 _buildWeatherCard

卡片采用圆角 + 双层阴影营造“浮起”效果:

BoxShadow(
  color: condition.textColor.withValues(alpha: 0.15),
  blurRadius: cardElevation,
  offset: Offset(0, 10),
),
BoxShadow(
  color: Colors.black.withValues(alpha: 0.05),
  blurRadius: 15,
  offset: Offset(0, 5),
),

在这里插入图片描述

设计细节

  • 阴影颜色取自 textColor,保证与天气风格一致。
  • 小屏设备降低 blurRadius,避免性能问题。

3. 温度展示区:带随机高低温

Text('H: ${city.temperature + Random().nextInt(5)}°'),
Text('L: ${city.temperature - Random().nextInt(5)}°'),

虽然真实项目中应使用 API 数据,但此处用 Random() 模拟了“今日最高/最低温”的视觉效果,非常巧妙!


4. 空气质量徽章:动态颜色

final color = quality == '优' ? Colors.green 
            : quality == '良' ? Colors.orange 
            : Colors.red;

在这里插入图片描述

配合半透明背景和边框,形成轻量级状态提示,符合 Material Design 的“微交互”理念。


四、交互动画实现

1. 卡片切换动画

使用 AnimationController 控制卡片缩放:

_scaleAnimation = Tween<double>(begin: 0.95, end: 1.0)
  .animate(CurvedAnimation(parent: _cardController, curve: Curves.easeOutBack));

AnimatedBuilder 中应用:

Transform.scale(scale: _scaleAnimation.value, child: _buildWeatherCard(...))

动画流程

  1. 用户触发切换 → _cardController.forward()(缩小到 0.95)
  2. setState 切换城市数据
  3. _cardController.reverse()(恢复到 1.0)

使用 Curves.easeOutBack 让回弹更有弹性,提升愉悦感。


2. 手势识别:滑动切换

通过 GestureDetector 监听水平拖拽结束事件:

onHorizontalDragEnd: (details) {
  if (details.primaryVelocity! > 0) _prevCity(); // 右滑
  else if (details.primaryVelocity! < 0) _nextCity(); // 左滑
}

注意判空 details.primaryVelocity == null,避免异常。


五、响应式布局策略

项目通过 LayoutBuilder 获取屏幕约束,动态调整 UI:

final isSmallScreen = constraints.maxHeight < 600;
final isMediumScreen = constraints.maxHeight < 700;

然后在各组件中使用条件判断:

SizedBox(height: isSmallScreen ? 20 : 32),
fontSize: isSmallScreen ? 16 : 18,
padding: EdgeInsets.all(isSmallScreen ? 20 : 32),

覆盖场景

  • 小屏手机(如 iPhone SE)
  • 折叠屏展开状态
  • 平板横屏模式

六、工程化与健壮性

1. 防重复点击

通过 _isAnimating 标志防止用户快速多次点击:

Future<void> _navigateToCity(int newIndex) async {
  if (_isAnimating || newIndex == _currentIndex) return;
  _isAnimating = true;
  // ... 动画逻辑
  _isAnimating = false;
}

2. 资源释放

重写 dispose() 方法,确保动画控制器被正确释放:


void dispose() {
  _cardController.dispose();
  _indicatorController.dispose();
  super.dispose();
}

3. 时间动态更新

_buildWeatherExtras 中显示当前时间:

'更新时间: ${DateTime.now().hour.toString().padLeft(2, '0')}:${DateTime.now().minute.toString().padLeft(2, '0')}'

实际项目中应结合 Timer 或状态管理库实现自动刷新。


七、可优化方向(进阶建议)

虽然当前代码已非常完善,但在生产环境中还可进一步增强:

方向 建议
数据来源 接入真实天气 API(如 OpenWeatherMap)
状态管理 引入 Riverpod / Bloc 管理城市列表与加载状态
国际化 使用 flutter_localizations 支持多语言
深色模式 扩展 AppTheme.darkTheme
性能 Random().nextInt(5) 缓存结果,避免 rebuild 时波动
测试 编写 widget test 验证卡片渲染逻辑

八、总结

这个天气卡片应用虽小,却集成了 Flutter 开发中的诸多最佳实践:

  • 枚举驱动 UI:让状态与表现紧密绑定
  • 主题系统:统一视觉语言
  • 精细动画:提升用户体验
  • 响应式设计:适配多端设备
  • 工程规范:资源管理、防抖、错误处理
Logo

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

更多推荐