【开源鸿蒙跨平台开发先锋训练营】Day3 Flutter集成Dio网络请求,本地美食数据清单列表功能开发
编写models/food_model.dart,完成美食数据模型定义:适配美食接口的JSON格式,定义“名称/图片/评分”等核心字段,做空安全+鸿蒙数据解析适配。先添加json_serializable依赖(VS Code终端执行),采用json_serializable实现 JSON 数据与 Dart 对象的自动转换,解决数据解析效率问题。// 关联自动生成的解析代码文件final int?i
【开源鸿蒙跨平台开发先锋训练营】Day3 Flutter集成Dio网络请求,本地美食数据清单列表功能开发
目录
【开源鸿蒙跨平台开发先锋训练营】Day3 Flutter集成Dio网络请求,本地美食数据清单列表功能开发
摘 要
为适配 OpenHarmony 生态的跨平台应用开发需求,本文目标设计实现一款本地美食清单应用。该应用基于 Flutter 框架构建,集成 http 网络请求库、图片缓存、下拉刷新及星级评分等核心功能模块,实现了美食数据的获取、解析、展示全流程。开发过程中完成了 OpenHarmony SDK 与 Flutter 环境的兼容配置,解决了第三方依赖的空安全适配问题,最终实现应用在DevEco Studio上的稳定运行。
1 引言
1.1 项目背景
Flutter是Google开发的跨平台UI框架,用于通过一套代码库高效构建跨平台应用。它采用Dart 编程语言驱动,支持将应用编译为原生机器代码或 JavaScript,从而实现高性能渲染和多端一致性。Flutter 的核心优势在于一次开发、多端部署,支持移动(iOS/Android)、Web、桌面(Windows/macOS/Linux)及嵌入式设备。OpenHarmony版Flutter 是Flutter针对OpenHarmony系统的适配版本,可以让你用Flutter开发HarmonyOS/OpenHarmony应用。
而本地美食清单作为高频生活类应用场景,需实现网络数据请求、图片加载优化、列表交互等核心功能,适合作为 Flutter 与 OpenHarmony 集成开发的实践载体。
1.2 项目目标
本项目旨在开发一款基于 Flutter+OpenHarmony 的跨平台本地美食清单应用,具体目标包括:
1. 完成 Flutter 与 OpenHarmony 开发环境的搭建与兼容配置;
2. 实现美食数据的网络请求、JSON 解析与本地模型映射;
3. 开发具备图片缓存、下拉刷新、星级评分功能的交互界面;
4. 确保应用在DevEco Studio(模拟器)上稳定运行。
1.3 技术选型依据
1. 框架选择:Flutter 3.27.4(鸿蒙适配版),支持跨平台 UI 一致性渲染,适配 OpenHarmony 系统特性;
2. 网络请求:选用 http 库(轻量高效,适配 Dart 空安全特性,满足基础数据请求需求);
3. 功能增强:集成 cached_network_image 优化图片加载性能,pull_to_refresh 实现交互刷新,flutter_rating_bar 完成评分展示(解决原 rating_bar 库空安全不兼容问题);
4. 开发工具:VS Code 1.108.1(Flutter 代码编写)与 DevEco Studio 6.0.0(鸿蒙权限配置与部署)协同工作。
2 项目功能与开发准备
2.1 功能核心定位
1. 明确页面目标:
基于开源鸿蒙跨平台工程(Flutter+OpenHarmony) 开发“本地美食数据清单”页面。
2. 核心目标:
完成工程网络请求能力的标准化集成、实现美食类数据清单的UI构建+数据绑定+交互逻辑开发、最终在DevEco Studio 6.0.0模拟器完成“网络请求→数据解析→列表渲染→交互验证”全流程运行验证,达成跨平台工程的“网络+列表”核心能力落地。
2.2 开发环境与技术栈配置
2.2.1 基础开发环境
| 开发工具/环境 | 版本规格 | 用途说明 |
| 操作系统 | Windows 10 64 位 | 开发主机运行环境 |
| VS Code | 1.108.1(user setup) | Flutter 业务代码编写与依赖管理 |
| DevEco Studio | 6.0.0 Release | OpenHarmony 应用配置与部署 |
| OpenHarmony SDK | API Version 20(6.0.0.47) | 鸿蒙应用开发核心依赖 |
| Flutter | 3.27.4(鸿蒙适配版) | 跨平台 UI 框架 |
2.2.2 核心依赖配置
通过VS Code软件完成pubspec.yaml 新增美食场景专属依赖。
1. 首先在终端输入以下命令,将http package添加到依赖中:
flutter pub add http
http package:Dart 内置的 http 包。
这是 Dart 语言标准库的一部分,提供了一个简单的 HTTP 客户端。
在 Flutter 中使用时,需要在 pubspec.yaml 文件中添加依赖:http: ^1.6.0(博主使用版本)。
该包提供了基本的 GET、POST 等请求方法,但功能相对基础。


2. 添加“美食图片缓存”依赖(解决鸿蒙设备图片重复加载卡顿)
Vs code终端中运行以下命令:
flutter pub add cached_network_image


3. 添加“下拉刷新”依赖(适配鸿蒙触控交互的下拉刷新功能)
flutter pub add pull_to_refresh


4. 添加“美食评分组件”依赖(实现鸿蒙 UI 适配的星级评分展示)
支持空安全的评分组件:
flutter pub add flutter_rating_bar


以上四条命令执行完成后,这些依赖会自动添加到你的pubspec.yaml的dependencies部分,同时 Flutter 会自动下载这些依赖包,无需额外执行flutter pub get(flutter pub add命令会自动触发依赖安装)。
最终你的pubspec.yaml的dependencies区域会变成这样:

依赖生效:执行 flutter pub get 命令确保无鸿蒙适配报错。
flutter pub get


3 项目总体设计
3.1 架构设计
应用采用模块化分层架构,按功能职责划分为五层,确保代码的可维护性与扩展性:
1. 表现层(UI 层):包含美食清单页面、列表项组件,负责用户界面渲染;
2. 接口层(API 层):封装美食数据请求接口,统一数据获取入口;
3. 网络层:实现 http 请求封装、响应处理、异常捕获,适配 OpenHarmony 网络特性;
4. 数据层:定义美食数据模型,完成 JSON 与 Dart 对象的序列化 / 反序列化;
5. 配置层:存储接口地址、超时时间等全局配置参数。
3.2 工程目录结构
项目根目录/
├── core/ # 核心封装层
│ └── http/ # 网络请求封装
├── api/ # 接口层
├── models/ # 数据模型层
├── pages/ # 页面层
├── module.json5 # 鸿蒙权限/编译配置
└── pubspec.yaml # 依赖配置
(详细)项目根目录/
├─ ohos/ # OpenHarmony 原生工程目录(鸿蒙侧配置/原生代码,保持鸿蒙标准结构)
│ ├─ entry/ # 鸿蒙应用入口模块(当前的ohos/entry)
│ │ ├─ src/main/ets/ # 鸿蒙ETS代码(原生页面/能力)
│ │ │ ├─ module.json5 # 鸿蒙权限/配置文件(当前的配置)
│ │ └─ ...(鸿蒙其他原生配置)
│ │─ ...(鸿蒙其他模块)
│ │
├─ lib/ # Flutter 业务代码目录(核心分层,重点优化)
│ ├─ core/ # 核心基础封装(复用性强的底层能力)
│ │ ├─ http/ # 网络请求封装(当前的core/http)
│ │ ├─ http_client.dart # 网络请求工具类
│ │ └─ api_config.dart # 网络配置(baseUrl、超时等)
│ ├─ api/ # 业务接口层(你当前的api)
│ │ └─ food_api.dart # 美食相关接口(getFoodList)
│ │
│ ├─ models/ # 数据模型层(你当前的models)
│ │ ├─ food_model.dart # 美食实体类
│ │ └─ food_model.g.dart # (若用json_serializable,自动生成的模型)
│ │
│ ├─ pages/ # 页面层(按业务模块划分,你当前的pages)
│ │ └─ food/ # 美食业务模块
│ │ └─ food_list_page.dart # 美食列表页面
│ └─ main.dart # Flutter入口文件
├─ pubspec.yaml # Flutter依赖配置
4 网络请求能力集成
4.1 鸿蒙网络权限配置
在工程ohos/entry/src/main/module.json5 的 reqPermissions 节点中声明网络权限:配置ohos.permission.INTERNET权限,确保已配置网络权限。说明鸿蒙权限机制对跨平台工程的约束逻辑。
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
4.2 美食接口配置与网络工具复用
完成工程目录创建(在 VSCode 的lib/下):
首先,通过VS Code软件在工程根目录的lib/文件夹下(例如博主的:D:\Flutter_HmProject\flutter_harmonyos\lib)依次创建core/、api/、models/、pages/这些目录。具体步骤如下:
在lib/目录上右键 → 点击 “新建文件夹...”,依次创建以下目录:
core/目录,再在core/下创建http/子目录;
api/目录;
models/目录;
pages/目录(后续可在pages/下再建food/子目录存放美食清单页面)。



VS Code安装Flutter插件:
打开 VS Code,点击左侧边栏的“扩展”图标;
在搜索框输入“Flutter”,找到官方的“Flutter”插件,点击“安装”;



同样在扩展商店搜索“Dart”,找到“Dart Code”插件,点击“安装”(博主在安装完Flutter插件后会自动提示安装Dart Code,确认安装即可);

安装完成后,重启 VS Code 使插件生效。
在对应目录下创建业务文件:
在刚创建的目录中,分别新建对应的 Dart 文件(右键目录→“新建文件”):
| 目录 | 新建文件名称 | 作用 |
| core/http/ | api_config.dart | 配置接口地址、超时等 |
| core/http/ | api_response.dart | 统一网络响应模型 |
| core/http/ | http_client.dart | 网络请求工具封装 |
| api/ | food_api.dart | 美食列表接口请求实现 |
| models/ | food_model.dart | 美食数据模型(JSON 解析) |
| pages/food/ | food_list_page.dart | 美食清单页面 UI + 逻辑 |
4.2.1 配置本地模拟美食API
博主使用Node.js + Express实现本地模拟美食 API。
1. 安装 Node.js(本地服务运行环境)
下载 Node.js:打开Node.js 官网,下载对应系统的 LTS 版本(Windows/Mac 都支持);
验证安装:打开终端 / 命令提示符,输入node -v和npm -v,能显示版本号即安装成功。
node -v
npm -v

2. 创建本地接口服务项目
新建一个文件夹(比如命名为local_food_api),打开终端并进入该文件夹;
初始化项目:执行命令
npm init -y
安装 Express(轻量 HTTP 服务框架)/安装本地接口服务需要的框架:
npm install express

3. 编写本地接口代码
在local_food_api文件夹中新建server.js文件,编写以下代码:
// 顶部引入cors
const cors = require('cors');
// 导入Express
const express = require('express');
const app = express();
// 配置静态资源目录:让public文件夹里的文件可以通过HTTP访问
app.use(express.static('public'));
// 启用CORS(允许所有域名访问,测试用)
app.use(cors());
// 定义端口(可自定义,比如3000)
const port = 3000;
// 模拟美食列表数据
const mockFoodData = [
{
id: 1,
name: "花生松仁炒蛋",
desc: "鸡蛋+松仁,鲜嫩可口",
image: "/food1.jpg", // 对应public里的food1.jpg
score: 4.8
},
{
id: 2,
name: "白玉木耳炒西蓝花",
desc: "白玉木耳滑嫩脆弹,西蓝花清甜脆爽",
image: "/food2.jpg",
score: 4.9
},
{
id: 3,
name: "糖醋排骨",
desc: "甜甜蜜蜜的糖醋排骨",
image: "/food3.jpg",
score: 4.5
}
];
// 定义美食列表接口:GET请求,路径为/api/localFood/list
app.get('/api/localFood/list', (req, res) => {
res.json(mockFoodData);
});
// 启动服务
app.listen(port, () => { // 改为用port变量
console.log(`Server running on http://localhost:${port}`);
});
4. 启动本地接口服务
在local_food_api文件夹的终端中执行:
node server.js
看到 “本地美食 API 服务已启动:http://localhost:3000” 即成功。

浏览器访问http://localhost:3000/api/localFood/list验证接口数据(注意:localhost也可以改为你本地的ipv4/IP地址访问)。

4.2.2 美食接口地址配置
在VS Code中创建core/http/api_config.dart文件,定义基础接口地址、超时时间与请求头,便于全局统一管理。
结合api_config.dart(URL、超时配置),封装http_client.dart:实现请求拦截、统一超时控制、错误码封装;
1. 首先,打开 VS Code 终端,执行以下命令(将json_annotation添加到运行时依赖):
flutter pub add json_annotation
执行后,pubspec.yaml的dependencies会新增json_annotation: ^x.x.x,解决 “json_annotation不是依赖”的错误。
2. 在终端执行build_runner命令,生成 JSON 序列化的代码文件:
flutter pub run build_runner build
3. Flutter 项目中调用这个本地接口
打开 Flutter 项目的api_config.dart,修改配置:
注意:博主的IP地址为192.168.0.108,所以“static const String baseUrl = "http://192.168.0.108:3000";”中要修改为本地的IP地址。
// core/http/api_config.dart
class ApiConfig {
// 基础接口地址(替换为你电脑的实际IP+Node服务端口)
// 电脑IP查看:Windows用ipconfig,Mac用ifconfig
static const String baseUrl = "http://192.168.0.108:3000"; // 测试接口基地址
// 美食清单数据接口(对应本地Node服务的接口路径)
static const String food_list_url = "$baseUrl/api/localFood/list";
// 其他配置(超时、请求头)
static const int timeout = 10000; // 超时时间(10s,适配鸿蒙网络特性)
static Map<String, String> get baseHeaders {
return {"Content-Type": "application/json;charset=UTF-8"};
}
}
4.2.3 统一响应模型
编写core/http/api_response.dart(统一响应模型),封装接口响应格式,统一处理成功与失败状态:
基于api_response.dart定义统一响应模型:处理“成功/失败/空数据”的标准化返回格式,说明封装的价值。
// core/http/api_response.dart
class ApiResponse<T> {
int code;
String msg;
T data;
bool success; // 实例变量
ApiResponse({
required this.code,
required this.msg,
required this.data,
required this.success,
});
// 成功响应构造方法,静态方法名从“success”改为“successResponse”(避免和实例变量重名)
static ApiResponse successResponse({dynamic data, String msg = "请求成功"}) {
return ApiResponse(code: 200, msg: msg, data: data, success: true);
}
// 失败响应构造方法,失败响应的方法名也可以统一规范
static ApiResponse errorResponse({int code = -1, String msg = "请求失败"}) {
return ApiResponse(code: code, msg: msg, data: null, success: false);
}
}
4.2.4 网络请求封装
编写core/http/http_client.dart(网络请求封装),基于 http 库实现 GET 请求封装,包含超时处理与异常捕获:
// core/http/http_client.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'api_config.dart';
import 'api_response.dart';
class HttpClient {
// GET请求封装
static Future<ApiResponse> getRequest(String url) async {
try {
final response = await http.get(
Uri.parse(url),
headers: ApiConfig.baseHeaders,
).timeout(Duration(milliseconds: ApiConfig.timeout));
return _handleResponse(response);
} catch (e) {
// 同步方法名:error → errorResponse
return ApiResponse.errorResponse(msg: "网络异常: ${e.toString()}");
}
}
// 处理响应结果
static ApiResponse _handleResponse(http.Response response) {
// 打印响应详情(调试用,发布时可以去掉)
print("接口URL: ${response.request?.url}");
print("状态码: ${response.statusCode}");
print("响应体: ${response.body}"); // 看服务器返回的具体错误
if (response.statusCode == 200) {
List<dynamic> result = json.decode(response.body);
// 同步方法名:success → successResponse
return ApiResponse.successResponse(data: result, msg: "请求成功");
} else {
// 同步方法名:error → errorResponse
return ApiResponse.errorResponse(
code: response.statusCode,
msg: "接口请求失败,状态码: ${response.statusCode}",
);
}
}
}
4.2.5 美食清单接口请求实现
在api/目录下编写工具列表请求接口(如food_api.dart):调用封装的http_client.dart,传入api_config中的接口地址,完成请求入参、响应解析的关联。
首先,在vs code终端中输入并执行以下命令:
flutter pub add dio:^5.0.0
美食清单接口请求实现(api/food_api.dart):
// food_api.dart
// 先导入依赖包
import 'package:dio/dio.dart';
import '../core/http/api_config.dart'; // 对应api_config的路径
// 在food_api.dart中定义请求方法
Future<dynamic> getFoodList() async {
try {
Dio dio = Dio();
print("请求接口:${ApiConfig.food_list_url}"); // 打印请求地址
// 调用api_config中配置的接口地址
Response response = await dio.get(ApiConfig.food_list_url);
print("接口返回:${response.data}"); // 打印返回数据
return response.data; // 提取接口返回的美食列表数据
} catch (e) {
print("接口请求失败:$e"); // 打印错误
throw e;
}
}
5 数据清单列表页面实现
5.1 美食数据模型定义
编写models/food_model.dart,完成美食数据模型定义:
适配美食接口的JSON格式,定义“名称/图片/评分”等核心字段,做空安全+鸿蒙数据解析适配。
先添加json_serializable依赖(VS Code终端执行),采用json_serializable实现 JSON 数据与 Dart 对象的自动转换,解决数据解析效率问题。
flutter pub add dev:json_serializable dev:build_runner
然后编写模型代码:
// food_model.dart
import 'package:json_annotation/json_annotation.dart';
part 'food_model.g.dart'; // 关联自动生成的解析代码文件
@JsonSerializable()
class FoodModel {
final int? id;
final String? name; // 对应接口的name
final String? desc; // 对应接口的desc
final String? image; // 对应接口的image
final double? score; // 美食评分(比如4.5、3.8)
FoodModel({
this.id,
this.name,
this.desc,
this.image,
this.score, // 构造函数添加score
});
// 自动生成的JSON转模型方法(由g.dart实现)
factory FoodModel.fromJson(Map<String, dynamic> json) => _$FoodModelFromJson(json);
// 自动生成的模型转JSON方法(由g.dart实现)
Map<String, dynamic> toJson() => _$FoodModelToJson(this);
}
// 美食列表模型
class FoodListModel {
final List<FoodModel> foodList;
FoodListModel({required this.foodList});
// 列表数据解析(简化格式),从JSON数组构建列表模型
factory FoodListModel.fromJson(List<dynamic> json) {
return FoodListModel(
foodList: json.map((e) => FoodModel.fromJson(e)).toList()
);
}
}
在 VS Code 终端执行以下命令,自动生成food_model.g.dart解析文件:
flutter pub run build_runner build
执行后,models/下会自动生成food_model.g.dart(无需手动修改)。
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'food_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
FoodModel _$FoodModelFromJson(Map<String, dynamic> json) => FoodModel(
id: (json['id'] as num?)?.toInt(),
name: json['name'] as String?,
desc: json['desc'] as String?,
image: json['image'] as String?,
score: (json['score'] as num?)?.toDouble(),
);
Map<String, dynamic> _$FoodModelToJson(FoodModel instance) => <String, dynamic>{
'id': instance.id,
'name': instance.name,
'desc': instance.desc,
'image': instance.image,
'score': instance.score,
};
5.2 美食清单页面UI构建
美食清单页面UI构建(pages/food/food_list_page.dart):
重点适配鸿蒙设备的“图片显示/评分组件/列表布局”,避免UI溢出/卡顿。
编写pages/food/food_list_page.dart(页面 UI):
// pages/food/food_list_page.dart
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import '../../../api/food_api.dart';
import '../../../models/food_model.dart';
class FoodListPage extends StatefulWidget {
const FoodListPage({super.key});
@override
State<FoodListPage> createState() => _FoodListPageState();
}
class _FoodListPageState extends State<FoodListPage> {
List<FoodModel> _foodList = [];
final RefreshController _refreshController = RefreshController(initialRefresh: false);
@override
void initState() {
super.initState();
_getFoodListData(); // 初始化加载数据
}
// 获取美食数据
Future<void> _getFoodListData() async {
try {
final data = await getFoodList();
if (mounted) {
// 先判断data是否是List类型
if (data is List) {
final foodListModel = FoodListModel.fromJson(data);
setState(() {
_foodList = foodListModel.foodList;
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("美食列表加载成功")),
);
} else {
throw Exception("接口返回数据不是列表");
}
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("加载失败:${e.toString()}")),
);
}
} finally {
_refreshController.refreshCompleted();
}
}
// 列表项UI构建
Widget _buildFoodItem(FoodModel food) {
// 步骤1:新增打印,确认图片URL是否正确(复制这个URL到手机浏览器能打开)
final imageUrl = "http://192.168.0.108:3000${food.image ?? ""}";
print("图片请求URL:$imageUrl"); // 看Flutter控制台日志,确认URL正确
return Card(
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
child: Padding(
padding: const EdgeInsets.all(10),
child: Row(
children: [
// 步骤2:原生Image.network
Image.network(
imageUrl, // 用上面拼接好的URL
width: 80,
height: 80,
fit: BoxFit.cover, // 让图片填充容器
// 加载中显示转圈
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return const CircularProgressIndicator();
},
// 加载失败打印错误+显示图标
errorBuilder: (context, error, stackTrace) {
print("图片加载失败原因:${error.toString()}");
return const Icon(Icons.fastfood);
},
),
const SizedBox(width: 12),
// 美食信息
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 美食名称
Text(
food.name ?? "未知美食",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 4),
// 美食描述
Text(
food.desc ?? "暂无描述",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
const SizedBox(height: 4),
// 评分组件
_buildNativeRating(food.score ?? 0),
],
),
),
],
),
),
);
}
// 原生星星评分组件(无需第三方包)
Widget _buildNativeRating(double score) {
const int maxStars = 5; // 满分5星
final int fullStars = score.floor(); // 全星数量(比如4.5→4)
final bool hasHalfStar = score - fullStars >= 0.5; // 是否有半星
final int emptyStars = maxStars - fullStars - (hasHalfStar ? 1 : 0); // 空星数量
return Row(
children: [
// 全星
...List.generate(fullStars, (index) => const Icon(
Icons.star,
size: 18,
color: Colors.amber,
)),
// 半星(如果有)
if (hasHalfStar)
const Icon(
Icons.star_half,
size: 18,
color: Colors.amber,
),
// 空星
...List.generate(emptyStars, (index) => const Icon(
Icons.star_border,
size: 18,
color: Colors.amber,
)),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("本地美食清单"), centerTitle: true),
body: SmartRefresher(
controller: _refreshController,
onRefresh: _getFoodListData, // 下拉刷新
child: _foodList.isEmpty
? const Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: _foodList.length,
itemBuilder: (context, index) => _buildFoodItem(_foodList[index]),
),
),
);
}
}
5.3 页面入口配置(工程集成)
在 main.dart 中配置美食页面为入口(或路由),设置美食清单页面为应用首页,配置主题样式:
// main.dart 美食页面入口
import 'package:flutter/material.dart';
import 'pages/food/food_list_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '本地美食清单',
theme: ThemeData(primarySwatch: Colors.blue),
home: const FoodListPage(), // 首页设置,美食清单页面
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
6 测试与验证
6.1 测试环境
测试设备:OpenHarmony 模拟器(API Version 20);
测试工具:DevEco Studio 6.0.0。
6.2 美食清单功能验证
1. 页面启动验证:模拟器启动后,点击图标正常打开“本地美食清单”页面,标题栏/布局无错乱;
2. 网络请求验证:页面自动请求美食数据,无“网络异常”提示(验证 ohos.permission.INTERNET 生效);
3. 列表渲染验证:美食图片/名称/评分完整显示,列表滑动流畅;
5. 交互验证:下拉列表触发刷新。

6.3 问题与解决方案
| 遇到问题 | 解决方案 |
| rating_bar 库不支持空安全 | 替换为 flutter_rating_bar 库 |
| 图片加载卡顿 | 集成 cached_network_image 缓存组件 |
| 网络请求无响应 | 配置 OpenHarmony INTERNET 权限 |
7 总结与展望
7.1 总结
本项目成功实现了基于 Flutter+OpenHarmony 的本地美食清单跨平台应用开发,完成了从环境搭建、依赖配置、模块封装到功能测试的全流程实践。核心成果包括:
1. 构建了兼容 OpenHarmony 的 Flutter 开发环境,解决了第三方依赖的空安全适配问题,完成了“美食场景网络请求集成+多维度列表渲染+鸿蒙设备适配”;
2. 设计了模块化的工程架构,实现了网络请求、数据解析、UI 展示的分层设计;
3. 集成了图片缓存、下拉刷新等实用功能,提升了应用的用户体验;
4. 验证了 Flutter 框架在 OpenHarmony 平台的兼容性与可行性。
7.2 未来展望
本项目仍有可优化与扩展的方向:
1. 功能扩展:比如可以增加美食的分类、收藏、搜索等功能,丰富应用场景;
2. 性能优化:引入状态管理框架(如 Provider),优化数据流转效率;
3. 安全增强:集成 HTTPS 加密传输,提升数据传输安全性;
4. 多端适配:优化不同尺寸鸿蒙设备的界面适配,实现 "一次开发、多端部署" 的完整跨平台体验。
最后,
欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)