Flutter 集成三方库开发鸿蒙6.0+(SDK API20+)跨端天气应用

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

一、项目概述

本项目面向鸿蒙开发者,基于 Flutter 框架,结合鸿蒙生态三方库,从零搭建一款可运行于鸿蒙6.0及以上系统(SDK API20+)的跨端天气应用,完整覆盖环境配置、三方库集成、鸿蒙适配、功能开发全流程,帮助开发者快速掌握 Flutter 结合三方库开发鸿蒙应用的核心实践。


二、前置环境准备

2.1 环境要求

  • 操作系统:Windows 10+/macOS 12+/Linux(推荐 Windows,适配鸿蒙开发工具)

  • Flutter 版本:3.13.0 及以上(需支持鸿蒙 ArkUI 适配)

  • 鸿蒙开发环境:DevEco Studio 4.0 及以上,配置鸿蒙 SDK(API 20+,对应鸿蒙6.0及以上系统)

  • Dart 版本:3.1.0 及以上(与 Flutter 版本匹配)

  • 网络环境:可正常访问 Flutter 包管理仓库(pub.dev)

2.2 环境配置步骤

  1. 安装 Flutter
# 1. 下载 Flutter SDK(鸿蒙适配版,官方已原生支持鸿蒙)
# 官网:https://flutter.dev/docs/get-started/install
# 2. 配置环境变量(Windows 示例)
# 右键「此电脑」→「属性」→「高级系统设置」→「环境变量」
# 在「系统变量」的 Path 中添加 Flutter 安装目录下的 bin 文件夹
# 3. 验证安装
flutter doctor

执行 flutter doctor 后,确保所有检查项通过(需安装 Android Studio/Xcode 用于移动端调试,鸿蒙端需 DevEco Studio)。

  1. 配置鸿蒙开发环境
  • 安装 DevEco Studio:https://developer.harmonyos.com/cn/develop/deveco-studio

  • 打开 DevEco Studio,安装鸿蒙 SDK(API 20 及以上,对应鸿蒙6.0+系统),配置签名文件(用于真机调试)

  • 执行 flutter devices,确保鸿蒙设备/模拟器被识别(设备需为鸿蒙6.0及以上版本)

  1. 创建 Flutter 项目
# 1. 执行创建命令
flutter create flutter_harmony_weather
# 2. 进入项目目录
cd flutter_harmony_weather
# 3. 打开项目(VS Code/Android Studio/DevEco Studio 均可)
code .

三、三方库选型与集成

3.1 核心三方库介绍

库名 用途 版本 适配鸿蒙(6.0+,API20+)
http 网络请求,调用天气API ^1.1.0 ✅ 原生支持
shared_preferences 本地存储,缓存城市/主题配置 ^2.2.2 ✅ 鸿蒙适配
flutter_screenutil 屏幕适配,统一多端尺寸 ^5.9.0 ✅ 鸿蒙适配
flutter_spinkit 加载动画,提升交互体验 ^5.2.0 ✅ 原生支持
path_provider 文件路径管理(可选,用于缓存天气数据) ^2.1.1 ✅ 鸿蒙适配

3.2 集成三方库步骤

  1. 配置pubspec.yaml

打开项目根目录的 pubspec.yaml,在 dependencies 节点添加以下依赖:

name: flutter_harmony_weather
description: A Flutter weather app for HarmonyOS 6.0+.
version: 1.0.0+1

environment:
  sdk: '>=3.1.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  # 网络请求
  http: ^1.1.0
  # 本地存储
  shared_preferences: ^2.2.2
  # 屏幕适配
  flutter_screenutil: ^5.9.0
  # 加载动画
  flutter_spinkit: ^5.2.0
  # 文件路径(可选)
  path_provider: ^2.1.1

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^2.0.0

flutter:
  uses-material-design: true
  1. 安装依赖
# 执行命令安装所有三方库
flutter pub get

执行完成后,依赖会自动下载并集成到项目中,确保所有三方库均适配鸿蒙6.0+(API20+)系统。


四、项目架构设计

采用 MVVM 架构,分层清晰,便于维护和后续拓展,适配鸿蒙6.0+跨端开发需求:

  • Model 层:定义天气数据实体、API 接口

  • ViewModel 层:处理业务逻辑、网络请求、数据缓存

  • View 层:UI 界面,绑定 ViewModel 数据,响应用户交互,适配鸿蒙多设备显示

  • Utils 层:工具类(屏幕适配、时间格式化等)


五、核心功能开发(含完整代码+注释)

5.1 Model 层:定义天气数据实体

创建 lib/models/weather_model.dart,定义天气数据结构:

// lib/models/weather_model.dart
/// 天气数据实体类
class WeatherModel {
  // 城市名称
  final String city;
  // 温度(摄氏度)
  final double temperature;
  // 天气描述(如:晴、多云)
  final String description;
  // 天气图标代码(用于加载图标)
  final String icon;
  // 湿度(百分比)
  final int humidity;
  // 风速(km/h)
  final double windSpeed;

  // 构造函数
  WeatherModel({
    required this.city,
    required this.temperature,
    required this.description,
    required this.icon,
    required this.humidity,
    required this.windSpeed,
  });

  // 从JSON解析数据(网络请求返回的JSON转实体)
  factory WeatherModel.fromJson(Map<String, dynamic> json) {
    return WeatherModel(
      city: json['name'] ?? '未知城市',
      temperature: (json['main']['temp'] - 273.15), // 开尔文转摄氏度
      description: json['weather'][0]['description'] ?? '未知天气',
      icon: json['weather'][0]['icon'] ?? '01d',
      humidity: json['main']['humidity'] ?? 0,
      windSpeed: (json['wind']['speed'] * 3.6), // m/s 转 km/h
    );
  }
}

5.2 ViewModel 层:网络请求与业务逻辑

创建 lib/view_models/weather_view_model.dart,处理网络请求、数据缓存:

// lib/view_models/weather_view_model.dart
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import '../models/weather_model.dart';

/// 天气业务逻辑 ViewModel
class WeatherViewModel extends ChangeNotifier {
  // 天气数据
  WeatherModel? _weather;
  // 加载状态
  bool _isLoading = false;
  // 错误信息
  String? _errorMessage;

  //  getter 方法,供 View 层访问
  WeatherModel? get weather => _weather;
  bool get isLoading => _isLoading;
  String? get errorMessage => _errorMessage;

  // 天气API(使用 OpenWeatherMap 免费API,需自行申请Key:https://openweathermap.org/api)
  static const String _apiKey = 'YOUR_API_KEY'; // 替换为你的API Key
  static const String _baseUrl = 'https://api.openweathermap.org/data/2.5/weather';

  /// 根据城市名获取天气
  Future<void> fetchWeather(String city) async {
    // 1. 设置加载状态,通知UI更新
    _isLoading = true;
    _errorMessage = null;
    notifyListeners();

    try {
      // 2. 发起网络请求
      final response = await http.get(
        Uri.parse('$_baseUrl?q=$city&appid=$_apiKey'),
      );

      // 3. 检查响应状态
      if (response.statusCode == 200) {
        // 4. 解析JSON数据
        final jsonData = json.decode(response.body);
        _weather = WeatherModel.fromJson(jsonData);

        // 5. 缓存城市到本地(下次打开自动加载)
        await _saveCityToPrefs(city);
      } else {
        // 6. 处理请求失败
        _errorMessage = '获取天气失败:${response.statusCode}';
      }
    } catch (e) {
      // 7. 捕获异常
      _errorMessage = '网络异常:$e';
    } finally {
      // 8. 结束加载状态,通知UI更新
      _isLoading = false;
      notifyListeners();
    }
  }

  /// 从本地缓存加载上次查询的城市
  Future<void> loadLastCity() async {
    final prefs = await SharedPreferences.getInstance();
    final lastCity = prefs.getString('last_city') ?? '北京'; // 默认北京
    await fetchWeather(lastCity);
  }

  /// 保存城市到本地缓存
  Future<void> _saveCityToPrefs(String city) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString('last_city', city);
  }
}

注意:_apiKey 需要自行到 OpenWeatherMap 官网申请免费密钥,替换后才能正常请求数据,该配置适配鸿蒙6.0+系统网络请求规范。

5.3 View 层:UI 界面开发

5.3.1 主页面:lib/screens/weather_screen.dart

// lib/screens/weather_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:provider/provider.dart';
import '../view_models/weather_view_model.dart';

/// 天气主页面(适配鸿蒙6.0+多设备)
class WeatherScreen extends StatefulWidget {
  const WeatherScreen({super.key});

  
  State<WeatherScreen> createState() => _WeatherScreenState();
}

class _WeatherScreenState extends State<WeatherScreen> {
  // 城市输入控制器
  final TextEditingController _cityController = TextEditingController();

  
  void initState() {
    super.initState();
    // 页面初始化时,加载上次缓存的城市天气
    WidgetsBinding.instance.addPostFrameCallback((_) {
      Provider.of<WeatherViewModel>(context, listen: false).loadLastCity();
    });
  }

  
  void dispose() {
    // 销毁控制器,防止内存泄漏
    _cityController.dispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    // 初始化屏幕适配(鸿蒙6.0+手机、平板、智慧屏等多端统一)
    ScreenUtil.init(context, designSize: const Size(360, 690));

    return Scaffold(
      // 渐变背景
      backgroundColor: Colors.blue[50],
      appBar: AppBar(
        title: const Text('Flutter 鸿蒙天气'),
        centerTitle: true,
        backgroundColor: Colors.blue[400],
      ),
      body: Padding(
        padding: EdgeInsets.all(16.w),
        child: Column(
          children: [
            // 1. 城市输入框
            _buildCityInput(),
            SizedBox(height: 20.h),
            // 2. 天气展示区域
            Expanded(child: _buildWeatherContent()),
          ],
        ),
      ),
    );
  }

  /// 城市输入组件
  Widget _buildCityInput() {
    return TextField(
      controller: _cityController,
      decoration: InputDecoration(
        hintText: '请输入城市名(如:北京、Shanghai)',
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(12.r),
        ),
        suffixIcon: IconButton(
          icon: const Icon(Icons.search),
          onPressed: () {
            // 点击搜索按钮,获取天气
            final city = _cityController.text.trim();
            if (city.isNotEmpty) {
              Provider.of<WeatherViewModel>(context, listen: false)
                  .fetchWeather(city);
            }
          },
        ),
      ),
      // 点击键盘完成按钮,触发搜索
      onSubmitted: (value) {
        if (value.isNotEmpty) {
          Provider.of<WeatherViewModel>(context, listen: false)
              .fetchWeather(value);
        }
      },
    );
  }

  /// 天气内容展示组件
  Widget _buildWeatherContent() {
    return Consumer<WeatherViewModel>(
      builder: (context, viewModel, child) {
        // 1. 加载中状态
        if (viewModel.isLoading) {
          return Center(
            child: SpinKitFadingCircle(
              color: Colors.blue[400],
              size: 50.w,
            ),
          );
        }

        // 2. 错误状态
        if (viewModel.errorMessage != null) {
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Icon(Icons.error_outline, size: 60.w, color: Colors.red),
                SizedBox(height: 16.h),
                Text(
                  viewModel.errorMessage!,
                  style: TextStyle(fontSize: 16.sp, color: Colors.red),
                  textAlign: TextAlign.center,
                ),
                SizedBox(height: 16.h),
                ElevatedButton(
                  onPressed: () {
                    viewModel.loadLastCity();
                  },
                  child: const Text('重试'),
                ),
              ],
            ),
          );
        }

        // 3. 正常数据状态
        final weather = viewModel.weather;
        if (weather == null) {
          return const Center(child: Text('暂无天气数据'));
        }

        return SingleChildScrollView(
          child: Column(
            children: [
              // 城市名
              Text(
                weather.city,
                style: TextStyle(
                  fontSize: 32.sp,
                  fontWeight: FontWeight.bold,
                  color: Colors.blue[800],
                ),
              ),
              SizedBox(height: 20.h),
              // 天气图标(OpenWeatherMap 图标)
              Image.network(
                'https://openweathermap.org/img/wn/${weather.icon}@2x.png',
                width: 100.w,
                height: 100.h,
              ),
              SizedBox(height: 10.h),
              // 温度
              Text(
                '${weather.temperature.toStringAsFixed(1)}°C',
                style: TextStyle(
                  fontSize: 48.sp,
                  fontWeight: FontWeight.bold,
                  color: Colors.blue[700],
                ),
              ),
              SizedBox(height: 10.h),
              // 天气描述
              Text(
                weather.description,
                style: TextStyle(
                  fontSize: 20.sp,
                  color: Colors.blue[600],
                ),
              ),
              SizedBox(height: 30.h),
              // 详细信息卡片
              Card(
                elevation: 4,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(12.r),
                ),
                child: Padding(
                  padding: EdgeInsets.all(16.w),
                  child: Column(
                    children: [
                      // 湿度
                      _buildDetailRow(
                        icon: Icons.water_drop,
                        title: '湿度',
                        value: '${weather.humidity}%',
                      ),
                      SizedBox(height: 16.h),
                      // 风速
                      _buildDetailRow(
                        icon: Icons.air,
                        title: '风速',
                        value: '${weather.windSpeed.toStringAsFixed(1)} km/h',
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        );
      },
    );
  }

  /// 详细信息行组件
  Widget _buildDetailRow({
    required IconData icon,
    required String title,
    required String value,
  }) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Row(
          children: [
            Icon(icon, color: Colors.blue[400], size: 24.w),
            SizedBox(width: 8.w),
            Text(
              title,
              style: TextStyle(fontSize: 16.sp, color: Colors.grey[700]),
            ),
          ],
        ),
        Text(
          value,
          style: TextStyle(
            fontSize: 16.sp,
            fontWeight: FontWeight.bold,
            color: Colors.blue[800],
          ),
        ),
      ],
    );
  }
}

5.3.2 主入口:lib/main.dart

// lib/main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'screens/weather_screen.dart';
import 'view_models/weather_view_model.dart';

void main() {
  runApp(
    // 注入 ViewModel,全局共享状态(适配鸿蒙6.0+系统运行规范)
    ChangeNotifierProvider(
      create: (context) => WeatherViewModel(),
      child: const MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter 鸿蒙天气',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      // 鸿蒙6.0+多端自适应主题
      darkTheme: ThemeData.dark(),
      themeMode: ThemeMode.system,
      home: const WeatherScreen(),
      // 关闭调试标签
      debugShowCheckedModeBanner: false,
    );
  }
}

六、鸿蒙适配与打包(适配6.0+,SDK API20+)

6.1 鸿蒙专属配置

  1. 配置鸿蒙项目文件

Flutter 会自动生成鸿蒙项目目录 ohos/,无需手动修改核心配置,只需确保以下两点(适配鸿蒙6.0+系统):

  • ohos/entry/src/main/module.json5 中权限配置正确(网络权限)

  • SDK 版本配置为 API20+,对应鸿蒙6.0及以上系统

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ],
    "compileSdkVersion": 20,
    "compatibleSdkVersion": 20
  }
}
  1. 鸿蒙多设备适配

已通过 flutter_screenutil 实现屏幕适配,自动适配鸿蒙6.0+系统的手机、平板、智慧屏等多类设备,确保UI显示一致。

6.2 运行与调试

  1. 连接鸿蒙设备/启动模拟器
  • 真机:开启开发者模式、USB调试,连接电脑(设备需为鸿蒙6.0及以上版本)

  • 模拟器:DevEco Studio 中启动鸿蒙6.0+模拟器(SDK API20+)

  1. 运行项目
# 直接运行到鸿蒙设备(6.0+)
flutter run -d harmony

执行后,Flutter 会自动编译并安装到鸿蒙6.0+设备,完成调试。

6.3 打包鸿蒙应用

# 1. 构建鸿蒙安装包(.hap),适配6.0+系统
flutter build hap --release
# 2. 打包完成后,安装包路径:
# build/harmony/ohos/build/outputs/hap/release/entry-release.hap

可通过 DevEco Studio 对 hap 包进行签名,发布到鸿蒙应用市场(需符合鸿蒙6.0+应用发布规范)。


七、项目优化与拓展

7.1 优化方向

  • 性能优化:添加网络请求缓存、图片懒加载、列表预渲染,适配鸿蒙6.0+系统性能要求

  • 功能拓展:添加7天天气预报、城市管理、主题切换、通知提醒

  • 鸿蒙特性适配:集成鸿蒙原子化服务、卡片、分布式能力(适配鸿蒙6.0+新特性)

  • 三方库拓展:集成 flutter_local_notifications 实现天气提醒,geolocator 实现定位获取天气(确保三方库适配鸿蒙6.0+)

7.2 常见问题排查

问题 解决方案
网络请求失败 检查 API Key 是否正确、网络是否通畅、鸿蒙网络权限是否开启,确认设备为鸿蒙6.0+版本
鸿蒙设备无法识别 执行 flutter devices 检查,重新连接设备/重启 ADB,确保设备系统版本≥6.0
三方库集成失败 执行 flutter pub cache repair 修复缓存,重新 flutter pub get,确认三方库适配鸿蒙6.0+(API20+)
屏幕适配异常 确保 ScreenUtil.init 在页面构建时正确初始化,适配鸿蒙6.0+多设备尺寸
打包失败 检查 SDK 版本是否配置为 API20+,确认签名文件配置正确,符合鸿蒙6.0+打包规范

八、项目总结

本项目基于 Flutter 框架,集成 http、shared_preferences 等三方库,完成了一款可运行于鸿蒙6.0及以上系统(SDK API20+)的跨端天气应用开发,覆盖了从环境搭建、三方库集成、业务开发到鸿蒙打包的全流程。

通过本项目,可掌握以下核心内容:

  • Flutter 结合三方库开发鸿蒙6.0+应用的完整流程

  • 常用 Flutter 三方库的集成与鸿蒙适配方法

  • MVVM 架构在 Flutter 鸿蒙跨端项目中的实践

  • 鸿蒙6.0+(SDK API20+)系统的适配、调试与打包技巧


九、附录:项目结构

flutter_harmony_weather/
├── lib/
│   ├── models/
│   │   └── weather_model.dart       # 数据实体
│   ├── screens/
│   │   └── weather_screen.dart      # 主页面
│   ├── view_models/
│   │   └── weather_view_model.dart  # 业务逻辑
│   └── main.dart                    # 入口文件
├── ohos/                            # 鸿蒙项目目录(自动生成,适配API20+)
├── pubspec.yaml                     # 依赖配置
└── README.md                        # 项目说明

运行效果

在这里插入图片描述

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐