【无标题】
本文介绍了基于Flutter的鸿蒙智能天气助手开发实践。项目采用MVVM架构,集成多个三方库实现核心功能:通过http库获取天气API数据,使用geolocator实现定位服务,shared_preferences进行本地缓存,flutter_spinkit提供加载动画。文章详细展示了环境配置(Flutter SDK 3.16+、HarmonyOS 6.0+)、项目结构设计、鸿蒙平台权限配置以及数
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
Flutter鸿蒙开发实践案例:集成三方库实现智能天气助手
一、项目概述
1.1 项目背景
随着鸿蒙(HarmonyOS)生态的快速发展,Flutter作为优秀的跨平台开发框架,为开发者提供了在鸿蒙平台上构建应用的便捷途径。本案例将详细介绍如何使用Flutter结合多个三方库,开发一个功能完整的智能天气助手应用。
1.2 项目目标
- 掌握Flutter在鸿蒙平台上的开发流程
- 学习集成和使用多个Flutter三方库
- 实现天气查询、定位、缓存等核心功能
- 理解跨平台开发的最佳实践
1.3 技术栈
- Flutter: 3.16+ (支持鸿蒙)
- 鸿蒙: HarmonyOS 6.0+
- 鸿蒙SDK: API 20+
- http: ^1.2.0 - 网络请求库
- geolocator: ^10.1.0 - 地理定位库
- shared_preferences: ^2.2.2 - 本地数据持久化
- flutter_spinkit: ^5.2.0 - 加载动画库
- intl: ^0.18.1 - 国际化支持
二、项目准备
2.1 环境配置
2.1.1 安装Flutter SDK
# 下载Flutter SDK (确保版本支持鸿蒙)
# https://docs.flutter.dev/get-started/install
# 配置环境变量
# Windows: 将 flutter/bin 添加到 PATH
# 验证安装
flutter doctor
2.1.2 配置鸿蒙开发环境
# 1. 安装DevEco Studio 5.0+
# https://developer.harmonyos.com/cn/develop/deveco-studio
# 2. 安装鸿蒙SDK (API 20+)
# 在DevEco Studio中安装HarmonyOS SDK,确保API Level >= 20
# 3. 配置Flutter鸿蒙支持
flutter config --enable-harmony-os
# 4. 验证鸿蒙SDK版本
flutter doctor -v
2.2 创建项目
# 创建Flutter项目
flutter create weather_app_harmony
# 进入项目目录
cd weather_app_harmony
# 查看项目结构
tree /F
2.3 配置pubspec.yaml
name: weather_app_harmony
description: Flutter鸿蒙智能天气助手应用
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
flutter:
sdk: flutter
# 网络请求库 - 用于调用天气API
http: ^1.2.0
# 地理定位库 - 获取用户当前位置
geolocator: ^10.1.0
# 本地缓存库 - 存储用户偏好和天气数据
shared_preferences: ^2.2.2
# 加载动画库 - 美化加载状态
flutter_spinkit: ^5.2.0
# 国际化库 - 支持多语言
intl: ^0.18.1
# UI组件库 - 提供美观的卡片和列表
cupertino_icons: ^1.0.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
flutter:
uses-material-design: true
# 鸿蒙平台配置
harmony:
# 指定鸿蒙SDK最低API版本
compileSdkVersion: 20
# 指定目标鸿蒙系统版本
targetSdkVersion: 20
三、项目架构设计
3.1 目录结构
lib/
├── main.dart # 应用入口
├── models/ # 数据模型
│ └── weather_model.dart # 天气数据模型
├── services/ # 业务服务
│ ├── weather_service.dart # 天气API服务
│ └── location_service.dart # 定位服务
├── repositories/ # 数据仓库层
│ └── weather_repository.dart
├── widgets/ # 自定义组件
│ ├── weather_card.dart # 天气卡片组件
│ └── loading_widget.dart # 加载动画组件
└── screens/ # 页面
└── home_screen.dart # 主页面
3.2 架构模式
采用MVVM架构模式:
- Model: 数据模型层,定义数据结构
- View: 视图层,负责UI展示
- ViewModel: 视图模型层,处理业务逻辑
四、核心功能实现
4.0 鸿蒙平台配置
在鸿蒙平台上运行需要配置权限和模块信息。在 harmony/entry/src/main/module.json5 中添加以下配置:
{
"module": {
"name": "entry",
"type": "entry",
"description": "天气应用主模块",
"mainElement": "EntryAbility",
"deviceTypes": [
"default",
"tablet"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "天气应用入口页面",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "需要网络权限获取天气数据",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "需要获取位置信息提供本地天气",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}
重要说明:
ohos.permission.INTERNET: 网络权限,用于调用天气APIohos.permission.APPROXIMATELY_LOCATION: 模糊定位权限,API 20+推荐使用- 设备类型支持手机和平板
- 最低兼容API版本为20
4.1 数据模型层
创建 lib/models/weather_model.dart:
/// 天气数据模型
class WeatherModel {
final String cityName;
final double temperature;
final String description;
final int humidity;
final double windSpeed;
final String iconUrl;
WeatherModel({
required this.cityName,
required this.temperature,
required this.description,
required this.humidity,
required this.windSpeed,
required this.iconUrl,
});
factory WeatherModel.fromJson(Map<String, dynamic> json) {
return WeatherModel(
cityName: json['name'],
temperature: json['main']['temp'].toDouble(),
description: json['weather'][0]['description'],
humidity: json['main']['humidity'],
windSpeed: json['wind']['speed'].toDouble(),
iconUrl: 'https://openweathermap.org/img/wn/${json['weather'][0]['icon']}@2x.png',
);
}
String get formattedTemperature => '${temperature.round()}°C';
}
4.2 服务层
4.2.1 天气API服务 (使用http库)
import 'package:http/http.dart' as http;
import 'dart:convert';
import '../models/weather_model.dart';
class WeatherService {
static const String _apiKey = 'YOUR_API_KEY';
static const String _baseUrl = 'https://api.openweathermap.org/data/2.5';
Future<WeatherModel> getWeatherByCity(String cityName) async {
final url = Uri.parse('$_baseUrl/weather?q=$cityName&appid=$_apiKey&units=metric');
final response = await http.get(url);
if (response.statusCode == 200) {
return WeatherModel.fromJson(json.decode(response.body));
}
throw Exception('获取天气失败');
}
}
4.2.2 定位服务 (使用geolocator库)
import 'package:geolocator/geolocator.dart';
class LocationService {
Future<Position> getCurrentLocation() async {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
}
// 鸿蒙API 20+支持模糊定位,优先使用模糊定位保护用户隐私
return await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.medium, // 使用中等精度,兼容鸿蒙模糊定位
timeLimit: const Duration(seconds: 10), // 设置超时时间
);
}
}
4.3 数据仓库层 (使用shared_preferences库)
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
import '../models/weather_model.dart';
import '../services/weather_service.dart';
import '../services/location_service.dart';
class WeatherRepository {
final WeatherService _weatherService = WeatherService();
final LocationService _locationService = LocationService();
Future<WeatherModel> getWeather({String? cityName}) async {
// 尝试从缓存读取
final prefs = await SharedPreferences.getInstance();
final cached = prefs.getString('cached_weather');
if (cached != null) {
return WeatherModel.fromJson(json.decode(cached));
}
// 获取新数据
WeatherModel weather;
if (cityName != null) {
weather = await _weatherService.getWeatherByCity(cityName);
} else {
final position = await _locationService.getCurrentLocation();
weather = await _weatherService.getWeatherByCity('Beijing'); // 简化处理
}
// 缓存数据
await prefs.setString('cached_weather', json.encode({
'name': weather.cityName,
'main': {'temp': weather.temperature, 'humidity': weather.humidity},
'weather': [{'description': weather.description, 'icon': '01d'}],
'wind': {'speed': weather.windSpeed}
}));
return weather;
}
}
4.4 UI组件
4.4.1 加载动画 (使用flutter_spinkit库)
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
class LoadingWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Center(
child: SpinKitFadingCircle(
color: Theme.of(context).primaryColor,
size: 50.0,
),
);
}
}
4.4.2 天气卡片
import 'package:flutter/material.dart';
import '../models/weather_model.dart';
class WeatherCard extends StatelessWidget {
final WeatherModel weather;
const WeatherCard({required this.weather});
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(weather.cityName, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
Text(weather.formattedTemperature, style: const TextStyle(fontSize: 48, color: Colors.blue)),
Text(weather.description, style: const TextStyle(fontSize: 18, color: Colors.grey)),
],
),
),
);
}
}
4.5 主页面
import 'package:flutter/material.dart';
import '../repositories/weather_repository.dart';
import '../widgets/weather_card.dart';
import '../widgets/loading_widget.dart';
class HomeScreen extends StatefulWidget {
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final WeatherRepository _repository = WeatherRepository();
bool _isLoading = false;
String? _errorMessage;
void initState() {
super.initState();
_loadWeather();
}
Future<void> _loadWeather() async {
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
await _repository.getWeather();
setState(() => _isLoading = false);
} catch (e) {
setState(() {
_isLoading = false;
_errorMessage = e.toString();
});
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('智能天气助手')),
body: _isLoading
? const LoadingWidget()
: _errorMessage != null
? Center(child: Text('加载失败: $_errorMessage'))
: const Center(child: Text('天气数据加载完成')),
floatingActionButton: FloatingActionButton(
onPressed: _loadWeather,
child: const Icon(Icons.refresh),
),
);
}
}
4.6 应用入口
import 'package:flutter/material.dart';
import 'screens/home_screen.dart';
void main() {
runApp(const WeatherApp());
}
class WeatherApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: '智能天气助手',
theme: ThemeData(useMaterial3: true),
home: const HomeScreen(),
);
}
}
五、运行和测试
5.1 安装依赖
# 在项目根目录执行
flutter pub get
5.2 运行应用
5.2.1 在鸿蒙模拟器上运行
# 启动鸿蒙模拟器 (确保系统版本 >= 6.0)
# 在DevEco Studio中创建并启动模拟器
# 然后运行应用
flutter run -d harmony
# 查看可用的鸿蒙设备
flutter devices
5.2.2 在鸿蒙真机上运行
# 连接鸿蒙设备 (确保系统版本 >= 6.0)
# 通过USB连接设备,并在设备上开启USB调试
# 验证设备连接
flutter devices
# 运行应用
flutter run -d <device_id>
# 构建HAP包
flutter build harmony --release
5.3 测试功能
-
定位功能测试
- 应用启动后自动获取当前位置天气
- 点击浮动按钮刷新位置
-
城市搜索测试
- 点击搜索按钮
- 输入城市名称(如"北京"、“上海”)
- 查看对应城市的天气信息
-
缓存功能测试
- 关闭应用后重新打开
- 查看是否显示缓存的天气数据
- 30分钟后缓存自动失效
-
错误处理测试
- 断开网络连接
- 查看错误提示和缓存数据展示
-
UI交互测试
- 下拉刷新
- 点击刷新按钮
- 查看加载动画效果
六、项目总结
6.1 技术亮点
-
三方库集成
- http: 简洁的网络请求实现
- geolocator: 跨平台定位功能
- shared_preferences: 轻量级本地存储
- flutter_spinkit: 优雅的加载动画
-
架构设计
- MVVM架构模式,代码结构清晰
- 分层设计,职责明确
- 便于维护和扩展
-
用户体验
- Material 3设计规范
- 流畅的动画效果
- 完善的错误处理
-
性能优化
- 数据缓存机制
- 异步加载
- 状态管理优化
6.2 鸿蒙平台适配要点
-
系统版本要求
- 最低支持HarmonyOS 6.0+
- 使用API 20+ SDK
- 兼容鸿蒙Next系统特性
-
权限配置
- 在鸿蒙平台的module.json5中配置定位权限
- 使用模糊定位权限(APPROXIMATELY_LOCATION)保护用户隐私
- 处理权限请求流程
-
网络配置
- 配置鸿蒙网络权限
- 处理HTTPS证书
- 支持鸿蒙网络安全策略
-
定位适配
- 使用中等精度定位,兼容鸿蒙模糊定位
- 设置合理的超时时间
- 处理定位服务不可用的情况
-
UI适配
- 鸿蒙系统的UI规范
- 字体和图标适配
- 支持鸿蒙设备多形态
6.3 后续优化方向
-
功能扩展
- 添加未来几天的天气预报
- 支持多城市管理
- 添加天气预警功能
-
性能优化
- 使用Provider/Riverpod进行状态管理
- 图片缓存优化
- 网络请求优化
-
国际化
- 支持多语言切换
- 使用intl库进行日期格式化
-
UI美化
- 添加更多动画效果
- 支持深色模式
- 自定义主题
6.4 学习收获
通过这个实践案例,我们学习了:
- Flutter在鸿蒙平台上的完整开发流程
- 如何集成和使用多个三方库
- MVVM架构模式的实际应用
- 数据持久化和缓存策略
- 异步编程和错误处理
- 跨平台开发的最佳实践
七、常见问题
7.1 鸿蒙平台相关
Q: Flutter应用无法在鸿蒙设备上运行?
A: 确保Flutter版本支持鸿蒙,并正确配置了鸿蒙开发环境。检查系统版本是否为HarmonyOS 6.0+。
Q: 定位权限无法获取?
A: 检查module.json5中的权限配置,确保使用APPROXIMATELY_LOCATION权限,并确认用户已授权。
Q: API版本不兼容怎么办?
A: 确保DevEco Studio安装了API 20+ SDK,在pubspec.yaml中配置正确的compileSdkVersion和targetSdkVersion。
Q: 模糊定位不工作?
A: API 20+支持模糊定位,确保使用LocationAccuracy.medium或更低精度,并在module.json5中配置正确的权限。
7.2 三方库相关
Q: http库请求失败?
A: 检查网络连接,确认API密钥正确,查看鸿蒙网络权限配置。
Q: shared_preferences数据丢失?
A: 鸿蒙系统清理缓存时会清除shared_preferences数据,这是正常行为。
7.3 开发调试
Q: 如何查看鸿蒙日志?
A: 使用DevEco Studio的日志工具或命令行工具查看。
Q: 热重载在鸿蒙上不工作?
A: 鸿蒙平台对热重载的支持有限,建议使用完全重启。
结语
本案例通过一个完整的天气应用,展示了Flutter在鸿蒙平台上的开发流程和三方库的使用方法。希望这个案例能够帮助新手鸿蒙开发者快速上手Flutter跨端开发,理解跨平台开发的核心理念和实践技巧。
Flutter的生态非常丰富,三方库众多,合理选择和使用这些库可以大大提高开发效率。在实际项目中,建议根据具体需求选择合适的库,并遵循最佳实践进行开发。
祝大家在Flutter鸿蒙开发的道路上越走越远!
更多推荐



所有评论(0)