1. 项目概述与背景
Flutter作为Google推出的跨平台UI框架,凭借其高效的渲染性能和"一次编写,多端运行"的特性,已经成为移动开发领域的重要选择。而鸿蒙OS(HarmonyOS)作为华为自主研发的分布式操作系统,正在构建自己的生态体系。将Flutter与鸿蒙结合,可以实现代码在Android、iOS和鸿蒙三大平台上的复用,大幅提升开发效率。
在这个项目中,我们重点解决Flutter应用在鸿蒙平台上的网络请求实现。网络请求作为移动应用的基础功能,其稳定性和易用性直接影响开发体验。Dio作为Flutter生态中最受欢迎的HTTP客户端库之一,提供了强大的网络请求能力和灵活的扩展接口,非常适合作为跨平台开发的网络请求解决方案。
2. 环境准备与项目初始化
2.1 开发环境配置
在开始Flutter鸿蒙跨端开发前,需要确保开发环境正确配置:
-
Flutter SDK安装:
- 下载最新稳定版Flutter SDK(当前推荐3.19.0+)
- 解压后添加flutter/bin目录到系统PATH环境变量
- 运行
flutter doctor检查基础环境
-
鸿蒙开发环境:
- 安装DevEco Studio 3.1或更高版本
- 配置鸿蒙SDK(API Version 9+)
- 安装OHOS工具链(通过DevEco Studio的SDK Manager)
-
IDE选择:
- Android Studio(推荐安装Flutter和Dart插件)
- 或VS Code(安装Flutter扩展)
提示:鸿蒙平台开发需要额外配置OHOS工具链,这是与常规Flutter开发的主要区别点。确保在DevEco Studio中正确安装OHOS SDK。
2.2 创建Flutter鸿蒙项目
在终端执行以下命令创建支持鸿蒙平台的Flutter项目:
bash复制flutter create --platform ohos xiu_mall
关键参数说明:
--platform ohos:指定项目支持鸿蒙平台- 项目名称(xiu_mall)使用小写字母和下划线组合
创建完成后,项目结构如下:
code复制xiu_mall/
├── android/ # Android平台代码
├── ios/ # iOS平台代码
├── ohos/ # 鸿蒙平台特有代码
├── lib/ # 跨平台Dart代码
└── pubspec.yaml # 项目依赖配置文件
2.3 项目目录结构优化
为提高代码可维护性,我们采用功能模块化的目录结构:
bash复制lib/
├── api/ # 网络请求相关
│ ├── cat_api.dart # 猫咪API服务
│ └── http_util.dart # Dio封装
├── components/ # 公共UI组件
│ └── cat_card.dart # 猫咪卡片组件
├── constants/ # 常量配置
│ └── api_constants.dart
├── models/ # 数据模型
│ └── cat_model.dart
├── pages/ # 页面组件
│ ├── cats/ # 猫咪页面
│ ├── login/ # 登录页面
│ └── main/ # 主页面
├── routes/ # 路由配置
│ └── index.dart
├── stores/ # 状态管理
│ └── cat_store.dart
└── main.dart # 应用入口
这种结构清晰分离了不同职责的代码,便于团队协作和后续功能扩展。
3. Dio网络请求架构实现
3.1 Dio库集成与基础配置
3.1.1 添加依赖
在pubspec.yaml中添加Dio和Provider(状态管理)依赖:
yaml复制dependencies:
flutter:
sdk: flutter
dio: ^5.5.0 # 网络请求库
provider: ^6.1.2 # 状态管理
cached_network_image: ^3.3.0 # 图片缓存
执行flutter pub get安装依赖。
3.1.2 基础Dio封装
创建lib/api/http_util.dart实现Dio的基础封装:
dart复制import 'package:dio/dio.dart';
class HttpUtil {
static final HttpUtil _instance = HttpUtil._internal();
late Dio _dio;
factory HttpUtil() => _instance;
HttpUtil._internal() {
_dio = Dio(BaseOptions(
connectTimeout: const Duration(seconds: 15),
receiveTimeout: const Duration(seconds: 15),
headers: {
'Content-Type': 'application/json',
},
));
// 添加拦截器
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
// 可在此添加统一请求头
return handler.next(options);
},
onResponse: (response, handler) {
// 统一处理响应数据
return handler.next(response);
},
onError: (DioException e, handler) {
// 统一错误处理
return handler.next(e);
},
));
}
Future<dynamic> get(String url, {Map<String, dynamic>? params}) async {
try {
final response = await _dio.get(url, queryParameters: params);
return response.data;
} on DioException catch (e) {
throw _handleError(e);
}
}
String _handleError(DioException e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
return '连接超时,请检查网络';
case DioExceptionType.badResponse:
return '服务器错误: ${e.response?.statusCode}';
default:
return '网络请求失败: ${e.message}';
}
}
}
关键设计点:
- 使用单例模式确保全局只有一个Dio实例
- 配置合理的超时时间(15秒)
- 添加拦截器实现统一请求/响应处理
- 封装基础GET方法,其他HTTP方法类似实现
3.2 网络请求架构设计
3.2.1 分层架构设计
我们采用典型的三层架构:
- 数据层:负责原始网络请求(HttpUtil)
- 服务层:业务API封装(如CatApi)
- 展示层:UI调用和状态管理
dart复制// 数据层调用示例
final data = await HttpUtil().get('https://api.example.com/data');
// 服务层封装示例
class CatApi {
static Future<List<Cat>> getCats() async {
final data = await HttpUtil().get('$baseUrl/cats');
return data.map((json) => Cat.fromJson(json)).toList();
}
}
// UI层调用示例
final cats = await CatApi.getCats();
setState(() => _cats = cats);
3.2.2 模型定义
创建lib/models/cat_model.dart定义数据模型:
dart复制class Cat {
final String id;
final String url;
final int width;
final int height;
Cat({
required this.id,
required this.url,
required this.width,
required this.height,
});
factory Cat.fromJson(Map<String, dynamic> json) {
return Cat(
id: json['id'] ?? '',
url: json['url'] ?? '',
width: json['width'] ?? 0,
height: json['height'] ?? 0,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'url': url,
'width': width,
'height': height,
};
}
模型类实现了:
- 从JSON构造对象的
fromJson方法 - 转换为JSON的
toJson方法 - 非空处理(
??操作符)
3.2.3 API服务实现
创建lib/api/cat_api.dart实现猫咪图片API:
dart复制import '../models/cat_model.dart';
import '../constants/api_constants.dart';
import 'http_util.dart';
class CatApi {
static final HttpUtil _http = HttpUtil();
static Future<List<Cat>> getCatImages({
int limit = 1,
int? page,
String? order,
}) async {
final params = {
'limit': limit,
if (page != null) 'page': page,
if (order != null) 'order': order,
};
final data = await _http.get(
'${ApiConstants.catBaseUrl}${ApiConstants.catImagesEndpoint}',
params: params,
);
if (data is List) {
return data.map((json) => Cat.fromJson(json)).toList();
}
throw Exception('Invalid response format');
}
}
特点:
- 使用命名参数提高可读性
- 条件参数通过
if语句动态添加 - 严格的类型检查和错误处理
3.2.4 常量管理
创建lib/constants/api_constants.dart集中管理API配置:
dart复制class ApiConstants {
// 猫咪API
static const String catBaseUrl = 'https://api.thecatapi.com/v1';
static const String catImagesEndpoint = '/images/search';
// 天气API
static const String weatherBaseUrl = 'https://restapi.amap.com/v3/weather';
static const String weatherEndpoint = '/weatherInfo';
// 超时配置
static const int connectTimeout = 15;
static const int receiveTimeout = 15;
}
这种集中管理方式:
- 避免硬编码
- 便于统一修改
- 提高代码可读性
4. 状态管理与UI实现
4.1 状态管理方案选择
对于中小型Flutter应用,Provider是轻量且高效的状态管理方案。我们使用ChangeNotifier实现猫咪图片的状态管理:
dart复制// lib/stores/cat_store.dart
import 'package:flutter/material.dart';
import '../models/cat_model.dart';
import '../api/cat_api.dart';
class CatStore extends ChangeNotifier {
List<Cat> _cats = [];
bool _isLoading = false;
String? _error;
List<Cat> get cats => _cats;
bool get isLoading => _isLoading;
String? get error => _error;
Future<void> fetchCats() async {
_isLoading = true;
_error = null;
notifyListeners();
try {
_cats = await CatApi.getCatImages(limit: 10);
} catch (e) {
_error = e.toString();
} finally {
_isLoading = false;
notifyListeners();
}
}
}
关键功能:
- 管理加载状态和错误信息
- 封装数据获取逻辑
- 通过
notifyListeners()通知UI更新
4.2 UI组件实现
4.2.1 猫咪卡片组件
创建可复用的CatCard组件(lib/components/cat_card.dart):
dart复制import 'package:flutter/material.dart';
import '../models/cat_model.dart';
class CatCard extends StatelessWidget {
final Cat cat;
const CatCard({super.key, required this.cat});
@override
Widget build(BuildContext context) {
return Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
AspectRatio(
aspectRatio: cat.width / cat.height,
child: Image.network(
cat.url,
fit: BoxFit.cover,
loadingBuilder: (_, child, progress) {
if (progress == null) return child;
return Center(
child: CircularProgressIndicator(
value: progress.expectedTotalBytes != null
? progress.cumulativeBytesLoaded / progress.expectedTotalBytes!
: null,
),
);
},
errorBuilder: (_, __, ___) => const Icon(Icons.error),
),
),
Padding(
padding: const EdgeInsets.all(12),
child: Text(
'${cat.width}×${cat.height}',
style: Theme.of(context).textTheme.bodySmall,
),
),
],
),
);
}
}
特点:
- 自适应图片宽高比
- 完善的加载和错误状态处理
- 符合Material Design规范
4.2.2 猫咪页面实现
创建猫咪列表页面(lib/pages/cats_page.dart):
dart复制import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../stores/cat_store.dart';
import '../components/cat_card.dart';
class CatsPage extends StatelessWidget {
const CatsPage({super.key});
@override
Widget build(BuildContext context) {
final catStore = Provider.of<CatStore>(context);
return Scaffold(
appBar: AppBar(
title: const Text('猫咪图库'),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: catStore.fetchCats,
),
],
),
body: _buildBody(catStore),
);
}
Widget _buildBody(CatStore catStore) {
if (catStore.isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (catStore.error != null) {
return Center(child: Text('加载失败: ${catStore.error}'));
}
if (catStore.cats.isEmpty) {
return const Center(child: Text('暂无猫咪图片'));
}
return GridView.builder(
padding: const EdgeInsets.all(8),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 0.8,
),
itemCount: catStore.cats.length,
itemBuilder: (_, index) => CatCard(cat: catStore.cats[index]),
);
}
}
功能要点:
- 响应式布局(GridView)
- 加载状态处理
- 错误显示
- 下拉刷新支持
4.3 路由与应用入口
4.3.1 路由配置
创建lib/routes/router.dart管理应用路由:
dart复制import 'package:flutter/material.dart';
import '../pages/cats_page.dart';
import '../pages/main_page.dart';
class AppRouter {
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (_) => const MainPage());
case '/cats':
return MaterialPageRoute(builder: (_) => const CatsPage());
default:
return MaterialPageRoute(
builder: (_) => Scaffold(
body: Center(
child: Text('未找到路由: ${settings.name}'),
),
),
);
}
}
}
4.3.2 应用入口
修改lib/main.dart初始化应用:
dart复制import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'routes/router.dart';
import 'stores/cat_store.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => CatStore()),
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '猫咪图库',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
onGenerateRoute: AppRouter.generateRoute,
initialRoute: '/',
);
}
}
5. 跨平台适配与调试
5.1 鸿蒙平台特殊配置
5.1.1 网络权限配置
在ohos/config.json中添加网络权限:
json复制{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
5.1.2 鸿蒙平台初始化
在ohos/entry/src/main/cpp/flutter_main.cpp中确保正确初始化Flutter引擎:
cpp复制#include "flutter_main.h"
#include "flutter/logging.h"
void FlutterMain::Run() {
// 初始化Flutter引擎
flutter::FlutterEngine engine;
engine.Run("flutter_assets");
}
5.2 多平台调试技巧
5.2.1 平台特定代码
使用dart.library.io和dart.library.js区分平台:
dart复制import 'dart:io' show Platform;
if (Platform.isAndroid) {
// Android特有逻辑
} else if (Platform.isIOS) {
// iOS特有逻辑
} else if (Platform.isHarmonyOS) {
// 鸿蒙特有逻辑
}
5.2.2 调试工具
- Dio拦截器日志:
dart复制_dio.interceptors.add(LogInterceptor(
request: true,
requestHeader: true,
requestBody: true,
responseHeader: true,
responseBody: true,
error: true,
));
- Charles抓包:
- 配置手机代理
- 安装Charles证书
- 过滤
api.thecatapi.com域名
- Flutter DevTools:
- 性能分析
- 网络请求监控
- Widget树检查
5.3 常见问题解决
5.3.1 证书校验问题
在HttpUtil中配置跳过证书验证(仅开发环境):
dart复制_dio = Dio(BaseOptions(
// ...其他配置
))
..httpClientAdapter = IOHttpClientAdapter(
createHttpClient: () {
final client = HttpClient();
client.badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
return client;
},
);
5.3.2 鸿蒙平台网络请求失败
检查:
- 是否添加了网络权限
- 鸿蒙模拟器网络连接是否正常
- 是否需要在鸿蒙配置中允许非HTTPS请求
5.3.3 图片加载问题
使用cached_network_image优化图片加载:
yaml复制dependencies:
cached_network_image: ^3.3.0
使用示例:
dart复制CachedNetworkImage(
imageUrl: cat.url,
placeholder: (_, __) => const CircularProgressIndicator(),
errorWidget: (_, __, ___) => const Icon(Icons.error),
)
6. 项目构建与部署
6.1 构建鸿蒙应用
- 在项目根目录执行:
bash复制flutter build ohos
- 生成的HAP包位于:
code复制build/ohos/outputs/ohosApp/
- 通过DevEco Studio或
hdc工具安装到鸿蒙设备
6.2 构建Android/iOS应用
标准Flutter构建命令:
bash复制# Android
flutter build apk --split-per-abi
# iOS
flutter build ios
6.3 持续集成建议
- GitHub Actions示例:
yaml复制name: Flutter Build
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
- run: flutter pub get
- run: flutter test
- run: flutter build ohos
- uses: actions/upload-artifact@v3
with:
name: ohos-app
path: build/ohos/outputs/ohosApp/
- 环境变量管理:
- 将API密钥等敏感信息存储在环境变量中
- 使用
--dart-define传递构建参数
7. 架构优化建议
7.1 进一步解耦
- 依赖注入:
使用get_it实现服务定位:
dart复制final getIt = GetIt.instance;
void setup() {
getIt.registerSingleton<HttpUtil>(HttpUtil());
getIt.registerFactory<CatApi>(() => CatApi(getIt<HttpUtil>()));
}
// 使用
final catApi = getIt<CatApi>();
- Repository模式:
dart复制abstract class CatRepository {
Future<List<Cat>> getCats();
}
class CatRepositoryImpl implements CatRepository {
final CatApi _api;
CatRepositoryImpl(this._api);
@override
Future<List<Cat>> getCats() => _api.getCatImages();
}
7.2 性能优化
- 请求缓存:
dart复制import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
void addCacheInterceptor() {
_dio.interceptors.add(DioCacheInterceptor(
options: CacheOptions(
store: MemCacheStore(),
policy: CachePolicy.request,
hitCacheOnErrorExcept: [401, 403],
maxStale: const Duration(days: 7),
),
));
}
- 图片优化:
- 使用WebP格式
- 实现懒加载
- 配置合适的缓存策略
7.3 测试策略
- 单元测试:
dart复制void main() {
late HttpUtil http;
late CatApi api;
setUp(() {
http = MockHttpUtil();
api = CatApi(http);
});
test('getCatImages returns List<Cat>', () async {
when(http.get(any)).thenAnswer((_) async => [
{'id': '1', 'url': 'test.com/1.jpg', 'width': 100, 'height': 100}
]);
final cats = await api.getCatImages();
expect(cats, isA<List<Cat>>());
expect(cats.first.id, '1');
});
}
- Widget测试:
dart复制testWidgets('CatPage displays list', (tester) async {
final store = MockCatStore();
when(store.cats).thenReturn([
Cat(id: '1', url: 'test.com/1.jpg', width: 100, height: 100)
]);
await tester.pumpWidget(
Provider<CatStore>.value(
value: store,
child: const MaterialApp(home: CatsPage()),
)
);
expect(find.byType(CatCard), findsOneWidget);
});
8. 经验总结与避坑指南
8.1 关键经验
- Dio最佳实践:
- 始终使用单例模式管理Dio实例
- 合理设置超时时间(建议15-30秒)
- 添加必要的拦截器(日志、错误处理、缓存等)
- 对不同的API域名创建独立的Dio实例
- 鸿蒙适配要点:
- 确保OHOS工具链正确安装
- 检查网络权限配置
- 鸿蒙平台的Dio适配可能需要特殊处理
- 测试不同鸿蒙API版本的行为差异
- 状态管理选择:
- 小型项目:Provider
- 中型项目:Riverpod
- 大型项目:Bloc或Redux
8.2 常见问题与解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 鸿蒙平台网络请求失败 | 缺少网络权限 | 检查config.json权限配置 |
| 证书验证错误 | 自签名证书或测试环境 | 开发环境可禁用证书验证 |
| 图片加载缓慢 | 未使用缓存 | 引入cached_network_image |
| 跨平台API差异 | 平台特定行为 | 使用Platform检测并分支处理 |
| Dio重复请求 | 未取消前一个请求 | 使用CancelToken中止请求 |
8.3 性能优化技巧
- 网络请求优化:
- 合并多个小请求
- 实现请求优先级
- 使用HTTP/2
- 启用Gzip压缩
- 图片加载优化:
dart复制Image.network(
cat.url,
frameBuilder: (_, child, frame, __) {
if (frame == null) return const Placeholder();
return child;
},
cacheWidth: 400, // 限制解码分辨率
)
- 状态更新优化:
dart复制// 避免不必要的重建
@override
bool operator ==(Object other) =>
identical(this, other) || other is Cat && runtimeType == other.runtimeType && id == other.id;
@override
int get hashCode => id.hashCode;
9. 项目扩展方向
9.1 功能扩展
- 更多API示例:
- 用户认证(JWT处理)
- 文件上传/下载
- WebSocket通信
- GraphQL集成
- 高级功能:
- 请求重试机制
- 离线缓存
- 请求优先级队列
- 请求取消与超时处理
9.2 架构演进
- Clean Architecture:
code复制lib/
├── data/ # 数据层
├── domain/ # 领域层
└── presentation/ # 展示层
- BLoC模式:
dart复制class CatBloc extends Bloc<CatEvent, CatState> {
final CatRepository _repo;
CatBloc(this._repo) : super(CatInitial()) {
on<FetchCats>(_onFetchCats);
}
void _onFetchCats(FetchCats event, Emitter<CatState> emit) async {
emit(CatLoading());
try {
final cats = await _repo.getCats();
emit(CatLoaded(cats));
} catch (e) {
emit(CatError(e.toString()));
}
}
}
9.3 跨平台增强
- Web支持:
bash复制flutter create --platforms web .
- 桌面端支持:
bash复制flutter create --platforms linux,macos,windows .
- 响应式UI:
dart复制LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 600) {
return DesktopLayout();
} else {
return MobileLayout();
}
},
)
10. 资源推荐
10.1 学习资源
- 官方文档:
- 推荐书籍:
- 《Flutter实战·第二版》
- 《Dart编程语言权威指南》
- 《跨平台移动开发实践》
10.2 工具推荐
- 开发工具:
- Android Studio(Flutter插件)
- DevEco Studio(鸿蒙开发)
- VS Code(轻量级选择)
- 调试工具:
- Flutter DevTools
- Charles(网络调试)
- HDC(鸿蒙调试命令行)
- 测试工具:
- Mockito(单元测试)
- Integration_test(集成测试)
- Codemagic(CI/CD)
10.3 社区资源
- 中文社区:
- Flutter中文网
- 鸿蒙开发者社区
- CSDN Flutter专区
- 国际社区:
- Flutter官方论坛
- Stack Overflow
- GitHub开源项目
11. 个人实践心得
在实际开发Flutter鸿蒙跨平台应用过程中,有几个关键点值得特别关注:
-
网络请求的稳定性:在鸿蒙平台上,我发现需要特别注意网络权限的配置,否则即使代码完全正确,请求也会失败。建议在开发初期就验证基础网络功能。
-
Dio的拦截器威力:通过合理使用拦截器,我实现了统一的错误处理、日志记录和请求重试机制,这大大提高了代码的健壮性。特别是对于需要Token认证的API,拦截器可以自动处理Token刷新逻辑。
-
状态管理的选择:对于刚接触Flutter的开发者,我建议从Provider开始,它足够简单但功能强大。随着项目复杂度增加,再考虑更高级的状态管理方案。
-
跨平台差异处理:虽然Flutter强调"一次编写,多端运行",但平台差异仍然存在。特别是在鸿蒙平台上,某些行为可能与Android/iOS不同,需要做好测试和适配。
-
性能优化:网络请求和图片加载是性能瓶颈的常见来源。通过引入缓存、合理设置图片分辨率、使用懒加载等技术,可以显著提升用户体验。
这个项目让我深刻体会到Flutter跨平台开发的效率优势,特别是在需要同时支持Android、iOS和鸿蒙的场景下。Dio作为网络请求解决方案,其灵活性和扩展性完全满足企业级应用的需求。