【开源鸿蒙跨平台开发先锋训练营】DAY4~DAY6 OpenHarmony版Flutter本地美食清单上拉加载 + 下拉刷新 + 数据加载提示实现
基于开源鸿蒙跨平台工程(Flutter+OpenHarmony) 继续开发“本地美食数据清单”页面。为开源鸿蒙跨平台工程的本地美食列表清单实现上拉加载、下拉刷新及数据加载提示能力,并在DevEco Studio 6.0.0模拟器中完成“上拉加载→下拉刷新”功能运行验证,达成跨平台工程的“上拉加载 + 下拉刷新”能力落地。
【开源鸿蒙跨平台开发先锋训练营】DAY4~DAY6 OpenHarmony版Flutter本地美食清单上拉加载 + 下拉刷新 + 数据加载提示实现
目录
【开源鸿蒙跨平台开发先锋训练营】DAY4~DAY6 OpenHarmony版Flutter本地美食清单上拉加载 + 下拉刷新 + 数据加载提示实现
摘 要
本文基于开源鸿蒙跨平台开发框架,针对本地美食清单应用的列表功能进行扩展优化。完成列表下拉刷新、上拉加载、数据加载提示三大能力实现,并在DevEco Studio上运行验证。通过Dio封装分页API请求(food_api.dart)、pull_to_refresh实现下拉交互、infinite_scroll_pagination管理上拉分页逻辑,结合状态驱动UI设计加载提示;最终通过在DevEco Studio(模拟器)上验证功能兼容性,并按Git规范将完整工程推送到AtomGit公开仓库,确保代码可复现、可复用。
1 概述
1.1 开发背景
Flutter是Google开发的跨平台UI框架,用于通过一套代码库高效构建跨平台应用。它采用Dart 编程语言驱动,支持将应用编译为原生机器代码或 JavaScript,从而实现高性能渲染和多端一致性。Flutter 的核心优势在于一次开发、多端部署,支持移动(iOS/Android)、Web、桌面(Windows/macOS/Linux)及嵌入式设备。OpenHarmony版Flutter 是Flutter针对OpenHarmony系统的适配版本,可以让你用Flutter开发HarmonyOS/OpenHarmony应用。而“本地美食清单”作为生活类应用场景,之前已经实现网络数据请求、图片加载优化、列表交互等核心功能,因此本文还需实现分页加载、刷新交互、状态提示三大核心能力。
1.2 开发目标
1. 完成列表下拉刷新:触发重新获取第一页数据,重置分页状态;
2. 实现列表上拉加载更多:支持分页参数传递,增量加载数据;
3. 数据加载提示:包含初始化加载、刷新中、加载中、加载失败、无更多数据;
4. 完成功能验证:确保功能在模拟器上正常运行;
5. 落地代码提交规范:按Git标准推送完整工程到AtomGit,保障可复现性。
1.3 核心技术栈
基于原有技术栈,新增/优化以下依赖与组件:
| 技术组件 | 版本 | 用途说明 |
| Flutter | 3.27.4(鸿蒙适配版) | 跨平台UI框架,实现列表UI与交互 |
| Dio |
5.0.0 |
网络请求库,封装分页API请求 |
| pull_to_refresh | ^2.0.0 |
下拉刷新交互组件,适配鸿蒙触控逻辑(也可以统一实现下拉刷新与上拉加载功能) |
|
infinite_scroll_pagination |
4.0.0 |
上拉分页管理组件,简化分页状态维护 |
| SnackBar | Flutter内置 |
操作结果与异常提示 |
2 核心功能实现
2.1 功能核心定位
1. 明确页面目标:
基于开源鸿蒙跨平台工程(Flutter+OpenHarmony) 继续开发“本地美食数据清单”页面。
2. 核心目标:
为开源鸿蒙跨平台工程的本地美食列表清单实现上拉加载、下拉刷新及数据加载提示能力,并在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 的终端中(需确保终端路径处于你的 Flutter 工程根目录下),执行以下命令来添加这两个依赖:
注意:在这里博主虽然配置了easy_refresh库,但本文中并没有用到,仅使用了
pull_to_refresh(Flutter 主流下拉刷新 / 上拉加载库,支持自定义加载动画,适配鸿蒙 Flutter 引擎)、infinite_scroll_pagination(专注上拉分页加载,适配鸿蒙数据请求异步逻辑)。
flutter pub add infinite_scroll_pagination:^4.0.0
flutter pub add easy_refresh
操作步骤说明:
1. 打开 VS Code 的终端:点击顶部菜单栏“终端”→新建终端;
2. 确认终端路径:确保终端当前路径是你的 Flutter 工程根目录(比如博主这里显示 PS D:\Flutter_HmProject\flutter_harmonyos> 这样的工程路径);
3. 执行上述命令:命令会自动将这两个依赖添加到 pubspec.yaml 的 dependencies 中,并下载对应的包资源。
执行完成后,pubspec.yaml 里会自动新增这两个依赖项(无需手动修改),可以直接开始后续的逻辑。




也可在添加后执行flutter pub get安装依赖。

完成上述依赖添加后,博主的pubspec.yaml 的 dependencies 中显示如下:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.8
http: ^1.6.0 # 网络请求核心库(鸿蒙兼容)
cached_network_image: ^3.4.1 # 美食图片缓存(解决鸿蒙设备图片重复加载卡顿)
pull_to_refresh: ^2.0.0 # 下拉刷新(适配鸿蒙触控交互)
flutter_rating_bar: ^4.0.1 # 美食评分组件(鸿蒙UI适配)
dio: 5.0.0
json_annotation: ^4.9.0
infinite_scroll_pagination: 4.0.0 # 专注上拉分页(适配鸿蒙异步逻辑)
easy_refresh: ^3.4.0 # 轻量化刷新(适配鸿蒙触控交互)
2.2.3 配置本地模拟美食API
博主继续使用上期Node.js + Express实现本地模拟美食 API。
1. 编写本地接口代码
在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
},
{
id: 4,
name: "一品红烧肉",
desc: "一碗直达灵魂的红烧肉,肥而不腻,入口即化",
image: "/food4.jpg",
score: 4.5
},
{
id: 5,
name: "自制鸡肉丸",
desc: "肉嫩鲜香,而且营养丰富",
image: "/food5.jpg",
score: 5.0
},
{
id: 6,
name: "胡萝卜炒鸡蛋",
desc: "胡萝卜、鸡蛋、香葱、花生油、盐",
image: "/food6.jpg",
score: 4.8
},
{
id: 7,
name: "红烧茄子",
desc: "红烧茄子口味浓郁",
image: "/food7.jpg",
score: 4.7
},
{
id: 8,
name:"鱼香茄子",
desc:"鱼香茄子是一道川菜,具有咸、酸、甜、辣、香、鲜和浓郁的葱、姜、蒜味的特色。酸酸甜甜的最是开胃。",
image:"/food8.jpg",
score: 4.0
},
{
id: 9,
name:"凉拌银耳",
desc:"菜品酸辣开胃,清淡爽口,入味十足",
image:"/food9.jpg",
score: 4.0
},
{
id: 10,
name:"鱼香肉丝",
desc:"鱼香肉丝是一道经典的中国汉族传统名菜",
image:"/food10.jpg",
score: 5.0
},
{
id: 11,
name:"麻婆豆腐",
desc:"麻婆豆腐是四川省汉族传统名菜之一,属于川菜系",
image:"/food11.jpg",
score: 4.5
},
{
id: 12,
name:"宫保鸡丁",
desc:"红而不辣、辣而不猛、香辣味浓、肉质滑脆",
image:"/food12.jpg",
score: 4.3
}
];
// 定义美食列表接口:GET请求,路径为/api/localFood/list
app.get('/api/localFood/list', (req, res) => {
// 1. 获取前端传递的分页参数(默认page=1,pageSize=10)
const page = parseInt(req.query.page) || 1;
const pageSize = parseInt(req.query.pageSize) || 10; // 统一为pageSize(驼峰)
// 2. 计算分页的“起始/结束索引”,对mock数据切片
const startIndex = (page - 1) * pageSize;
const endIndex = startIndex + pageSize;
// const paginatedFoodList = mockFoodData.slice(startIndex, endIndex);
const paginatedFoodList = [];
// 循环取数:当索引超过mock数据长度时,用“取模”循环重复数据
for (let i = startIndex; i < endIndex; i++) {
const loopIndex = i % mockFoodData.length; // 取模实现循环
paginatedFoodList.push(mockFoodData[loopIndex]);
}
// 3. 返回“包含foodList字段的对象”(前端要这个格式)
res.json({
foodList: paginatedFoodList // 必须是“对象包裹数组”
});
});
// 启动服务
app.listen(port, () => { // 改为用port变量
console.log(`Server running on http://localhost:${port}`);
});
2. 启动本地接口服务
在local_food_api文件夹的终端中执行以下命令:
node server.js
看到 “Server running on http://localhost:3000” 即成功。

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

因为这次定义了分页逻辑,所以只能显示前10条数据,在浏览器地址栏输入http://localhost:3000/api/localFood/list?page=2,才能够显示后续数据。

3 API层:分页请求封装
API层:分页请求封装,修改文件:lib/api/food_api.dart。
3.1 实现目标
为“美食列表请求”添加分页参数(page/pageSize),通过Dio传递查询参数,支持后端分页逻辑,解决一次性加载海量数据卡顿问题。
3.2 具体修改内容
1. 导入依赖:引入dio.dart与api_config.dart,关联网络配置;
2. 类与方法定义:定义FoodApi类,新增带分页参数的静态方法getFoodList;
3. 分页参数构造:通过queryParameters传递page和pageSize到后端接口;
4. 日志与异常处理:打印请求地址、参数与返回数据(调试用),捕获并抛出异常(供页面层处理)。
核心代码实现(food_api.dart):
// food_api.dart
// 先导入依赖包
import 'package:dio/dio.dart';
import '../core/http/api_config.dart'; // 对应api_config的路径
// 定义FoodApi类
class FoodApi {
// 改为类的静态方法,并接收分页参数page/pageSize
static Future<dynamic> getFoodList({
required int page,
required int pageSize,
}) async {
try {
Dio dio = Dio();
// 构造分页请求参数(传递给接口的query参数)
final queryParams = {
"page": page,
"pageSize": pageSize,
};
print("请求接口:${ApiConfig.food_list_url},参数:$queryParams"); // 打印请求地址
// 发起请求时携带分页参数,调用api_config中配置的接口地址
Response response = await dio.get(
ApiConfig.food_list_url,
queryParameters: queryParams, // 关键:把分页参数传给后端接口
);
print("接口返回:${response.data}"); // 打印返回数据
return response.data; // 提取接口返回的美食列表数据
} catch (e) {
print("接口请求失败:$e"); // 打印错误
throw e;
}
}
}
3.3 功能作用
1. 统一API请求入口:所有美食列表请求均通过FoodApi.getFoodList发起,便于维护;
2. 支持分页控制:通过page和pageSize参数实现“增量加载”,减少网络带宽占用与内存消耗;
3. 调试友好:日志打印请求详情,快速定位接口问题(如参数错误、地址错误)。
4 页面层:核心逻辑实现
页面层:负责核心逻辑实现,修改文件:lib/pages/food/food_list_page.dart。
4.1 实现目标
整合“下拉刷新、上拉加载、加载提示”三大功能,通过状态管理实现UI与数据。
4.2 状态变量定义(页面层核心基础)
1. 修改内容:新增分页控制、加载状态、控制器变量,统一管理交互逻辑。
2. 核心代码(对应_FoodListPageState类顶部):
class _FoodListPageState extends State<FoodListPage> {
List<FoodModel> _foodList = [];
final RefreshController _refreshController = RefreshController(initialRefresh: false);
// 分页参数与加载状态
final int _pageSize = 6; // 每页加载的数据量:6条数据
bool _isLoading = false; // 加载锁(防止重复请求,如快速下拉/上拉)
// 无限分页核心控制器,初始为1
final PagingController<int, FoodModel> _pagingController = PagingController(firstPageKey: 1);
@override
void initState() {
super.initState();
_getFoodListData(isRefresh: true); // 初始化加载数据,初始化时执行「下拉刷新」逻辑(加载第1页数据)
// 新增:绑定上拉分页监听 - 滑到底部自动触发加载下一页,核心逻辑
_pagingController.addPageRequestListener((pageKey) {
_fetchPage(pageKey);
});
}
// ...后续方法实现
}
3. 功能作用:
_refreshController:控制下拉刷新的状态(开始/完成/失败);
_pagingController:管理上拉分页的页码、数据追加、加载状态(无更多/失败);
_isLoading:防止“同时触发下拉刷新与上拉加载”或“快速重复上拉”导致的重复请求。
4.3 下拉刷新逻辑实现
1. 修改内容:实现_getFoodListData方法,绑定到SmartRefresher的onRefresh回调,触发时重置状态并重新加载第一页数据。
2. 核心代码:
// 获取美食数据(新增isRefresh参数,区分刷新/加载)
Future<void> _getFoodListData({required bool isRefresh}) async { // _getFoodListData 方法
// 加载锁:防止重复请求
if (_isLoading) return;
_isLoading = true;
try {
if(isRefresh){ // 下拉刷新时:重置页码、数据、状态
_pagingController.refresh(); // 重置分页控制器,清空历史分页状态
await Future.delayed(const Duration(seconds: 2)); // 下拉刷新→加载中 停留2秒
} else {
await Future.delayed(const Duration(seconds: 2));// 上拉加载时加延迟(延长“加载中...”显示时间)
}
// 调用API层分页请求,下拉刷新永远请求第1页
final data = await FoodApi.getFoodList(
page: 1, // 下拉刷新永远请求第1页
pageSize: _pageSize,
);
if (mounted) {
if (data is Map<String, dynamic>) { // 先判断data是否是List类型
// 解析后端返回的Map格式数据(适配后端结构)
final foodListModel = FoodListModel.fromJson(data);
setState(() {
if (isRefresh) {
_foodList = foodListModel.foodList; // 下拉刷新:替换原有数据
} else {
_foodList.addAll(foodListModel.foodList); // 上拉加载:追加新数据
}
});
// 新增:下拉刷新成功后,给分页控制器赋值第一页数据
_pagingController.value = PagingState(// 更新分页控制器:设置第一页数据与下一页页码
nextPageKey: foodListModel.foodList.length >= _pageSize ? 2 : null,
itemList: foodListModel.foodList,
);
} else {
throw Exception("接口返回数据格式错误,不是对象格式");
}
}
} catch (e) {
if (mounted) {// 加载失败:显示错误提示
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("加载失败:${e.toString()}")),
);
}
} finally {
_isLoading = false;
if (isRefresh) {// 下拉刷新成功提示
Future.delayed(const Duration(seconds: 2), () {
if (mounted) {
_refreshController.refreshCompleted();// 结束下拉刷新动画(必须调用,否则刷新状态不会重置)
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("美食列表加载成功")),
);
}
});
}
}
}
3. 功能作用:
状态重置:下拉时重置_pagingController,避免分页状态混乱;
数据替换:重新请求第1页数据,覆盖原有列表,实现“刷新”效果。
4.4 上拉加载逻辑实现
1. 修改内容:实现_fetchPage方法,绑定到_pagingController的分页监听,滑到底部时自动请求下一页数据。
2. 核心代码:
// 上拉加载更多专用方法(pageKey为当前要加载的页码)
// 新增:infinite_scroll_pagination 专用分页请求方法
Future<void> _fetchPage(int pageKey) async {
if (_isLoading) return;
_isLoading = true;
try {
await Future.delayed(const Duration(seconds: 2));// 延长加载提示显示
final data = await FoodApi.getFoodList( // 调用API层,请求当前页码数据
page: pageKey,
pageSize: _pageSize,
);
if (mounted && data is Map<String, dynamic>) {
final foodListModel = FoodListModel.fromJson(data);
final newItems = foodListModel.foodList;
// 判断是否为最后一页(返回数据量 < 每页大小 → 无更多数据)
final isLastPage = newItems.length < _pageSize;
if (isLastPage) {
// 无更多数据:通知分页控制器停止上拉加载
_pagingController.appendLastPage(newItems);
} else {
// 有更多数据,自动加载下一页,页码+1,无限上拉核心逻辑
final nextPageKey = pageKey + 1;
_pagingController.appendPage(newItems, nextPageKey);
}
}
} catch (e) {
// 加载失败:通知分页控制器显示失败提示
_pagingController.error = e;
if(mounted){
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("加载失败:${e.toString()}")),
);
}
} finally {
_isLoading = false;
}
}
3. 功能作用:
自动分页:滑到底部时_pagingController自动触发_fetchPage,请求下一页;
边界处理:通过isLastPage判断是否停止上拉,避免无效请求;
失败兼容:加载失败时通过_pagingController.error显示失败提示,无需手动维护失败UI。
4.5 数据加载提示实现
1. 修改内容:通过SmartRefresher(下拉提示)与PagedChildBuilderDelegate(上拉/初始化提示)覆盖所有加载场景。
2. 核心代码(build方法中):
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("本地美食清单"), centerTitle: true),
body: SmartRefresher( // 下拉刷新容器(pull_to_refresh组件)
controller: _refreshController,
// 下拉刷新:调用带isRefresh=true的请求
onRefresh: () => _getFoodListData(isRefresh: true), // 绑定下拉刷新
// 自定义下拉头部(实现“刷新中/刷新完成”提示 + 颜色/字体优化)
header: CustomHeader(
builder: (context, mode) {
String headerText = "";
Color textColor = Colors.black87; // 初始文字颜色
// 根据刷新状态切换文案和颜色
if (mode == RefreshStatus.refreshing) {
headerText = "刷新中"; // 刷新中显示“刷新中”
textColor = Colors.blueAccent; // 加载中用橙色,更醒目
} else if (mode == RefreshStatus.completed) {
headerText = "刷新完成"; // 刷新完成显示“刷新完成”
textColor = Colors.grey[600]!; // 刷新完成用绿色,更友好
} else {
headerText = "下拉刷新"; // 初始状态显示“下拉刷新”
textColor = Colors.grey[600]!;
}
return Container(
height: 60,
alignment: Alignment.center,
child: Text(headerText, // 给Text添加style,应用textColor和字体样式
style: TextStyle(
color: textColor, // 应用定义的文字颜色
fontSize: 16, // 加大字号,更醒目
fontWeight: FontWeight.w400, // 加粗字体
),
), // 显示对应的提示文本
);
},
),
// 上拉分页列表(infinite_scroll_pagination组件)
child: PagedListView<int, FoodModel>(
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<FoodModel>(
// 列表项UI(复用原有方法)
itemBuilder: (context, item, index) => _buildFoodItem(item),
// 上拉加载中提示(转圈+文字)
newPageProgressIndicatorBuilder: (_) => Container(
height: 60,
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
CircularProgressIndicator(strokeWidth: 2),
SizedBox(width: 8),
Text("加载中..."),
],
),
),
noMoreItemsIndicatorBuilder: (_) => Container( // 无更多数据提示
height: 60,
alignment: Alignment.center,
child: const Text("已加载全部数据"),
),
// 初始化加载失败提示
firstPageErrorIndicatorBuilder: (_) => Container(
height: 60,
alignment: Alignment.center,
child: const Text("加载失败,下拉重试"),
),
// 上拉加载失败提示
newPageErrorIndicatorBuilder: (_) => Container(
height: 60,
alignment: Alignment.center,
child: const Text("加载失败,上拉重试"),
),
),
),
),
);
}
3. 功能作用:
全场景覆盖:包含“下拉刷新中/完成”、“上拉加载中”、“无更多数据”、“初始化/上拉失败”5种场景,用户操作有明确反馈;
鸿蒙适配:提示UI简洁,字体大小(16号)、颜色(灰色/蓝色)符合鸿蒙设备交互规范,避免视觉杂乱。
5 测试与验证
5.1 测试环境
测试设备:OpenHarmony 模拟器(API Version 20);
测试工具:DevEco Studio 6.0.0。
5.2 功能验证
1. 下拉刷新 + 数据加载提示:
下拉列表触发刷新,下拉拖动后首先页面顶部显示“下拉刷新”提示;触发刷新后顶部显示“刷新中”提示,等待刷新;刷新完成后顶部显示“刷新完成”提示。



2. 上拉加载 + 数据加载提示:
上拉页面至底部显示“加载中...”提示(含转圈动画),2秒后追加新的美食数据,加载完成后列表自动滚动至新增数据区域。

6 代码提交与 AtomGit 仓库更新
结合我前面创建已有的 AtomGit 仓库(classmate-h/flutter_HmProject),更新项目的步骤如下(基于 VS Code 终端操作,确保终端路径处于项目根目录):
6.1 查看本地修改
查看本地修改(确认要提交的内容):
在 VS Code 终端执行命令,查看当前修改的文件(比如我在本文中修改的food_list_page.dart、pubspec.yaml等)。
git status

博主通过上图显示从终端输出可以看到,git status 已经成功执行(命令正常返回了仓库状态信息)。
输出信息说明当前仓库的状态:
1. 分支:处于main分支,且本地main分支与远程origin/main分支是 “同步的(up to date)”;
2. 存在未暂存的修改文件(如lib/main.dart、pubspec.yaml等);
3. 存在未被 Git 跟踪的新文件 / 目录(如.vscode/、lib/api/等)。
简单来说,git status命令本身执行成功了,它只是告诉你当前仓库里有未处理的修改和新文件。
6.2 暂存本地修改
将所有修改的文件暂存到 Git 缓存区(若只想提交部分文件,可替换.为具体文件名,如git add lib/pages/food/food_list_page.dart):
git add .

博主在执行完命令后,从上面图片显示结果界面可以判断,git add . 已经成功执行。
1. 终端没有出现 “error” 类的错误提示,仅显示 “LF will be replaced by CRLF” 的换行符兼容警告(这是 Git 的正常提示,不是错误,不影响操作);
2. 左侧资源管理器中,修改 / 新增的文件旁边出现了 A(新增文件)、M(修改文件)的标记,说明这些文件已经成功被暂存到 Git 缓存区。
简单说:git add . 已经完成了“暂存所有修改文件”的任务,接下来可以执行 “提交”操作了。
6.3 提交本地修改
提交本地修改(写清楚更新内容):
按照 Git 提交规范,编写清晰的 commit message(说明本次更新了什么):
git commit -m "Initial commit: Flutter+鸿蒙跨平台工程"

博主执行完上述命令后从终端输出可以明确,git commit -m "Initial commit: Flutter+鸿蒙跨平台工程" 已经成功执行。
判断依据:
1. 终端显示了提交的标识:[main 11f11ba] Initial commit: Flutter+鸿蒙跨平台工程(其中11f11ba是本次提交的唯一哈希值,代表提交已被 Git 记录);
2. 输出了提交的文件变更统计:13 files changed, 1163 insertions(+), 67 deletions(-),以及具体文件的创建 / 修改状态(如create mode 100644 lib/pages/food/food_list_page.dart),说明 Git 已成功将暂存的文件提交到我的本地 Git 仓库。
6.4 拉取远程仓库最新代码
先拉取 AtomGit 上的最新代码(防止远程有更新导致推送冲突):
git pull origin main
若提示 “Already up to date”:说明远程代码和本地一致,直接进行下一步;
若出现冲突:打开冲突文件(终端会提示冲突文件路径),解决代码冲突后,重新执行git add .→git commit -m "XXXXXX",再继续。

执行完命令后,从上面图片中终端输出可以看到,git pull origin main 已经成功执行。
输出里的Already up to date.表示:本地的main分支与远程 AtomGit 仓库的main分支已经是同步的状态(没有新的更新需要拉取)。
这是正常的执行结果,说明当前本地代码和远程仓库的代码是一致的,接下来可以执行git push origin main将本地的新提交推送到远程仓库。
6.5 推送更新到 AtomGit 仓库
将本地最新提交推送到 AtomGit 的main分支:
git push origin main

从终端输出可以明确,git push origin main 已经成功执行。
判断依据:
1. 终端完成了“枚举对象、计数、压缩、写入”等推送流程,无任何错误提示;
2. 最后输出了推送结果:To https://gitcode.com/classmate-h/flutter_HmProject.git + 提交哈希的同步记录(19b4ef5...11f11ba main -> main);
3. 出现了[PASSED]标识,说明远程仓库的钩子校验也已通过。
现在可以刷新 AtomGit 仓库的网页,就能看到最新的提交记录和更新时间了。
6.6 验证推送结果
打开 AtomGit 仓库页面(在 AtomGit 创建的个人公开仓库),刷新页面:
确认仓库的“提交记录”中出现刚写的commit message;
查看lib/pages/food/food_list_page.dart等文件,确认最新代码已同步。
刷新后的页面也能看到最新的提交记录和更新时间。

全部步骤命令:
# 查看修改
git status
# 暂存修改
git add .
# 提交修改(替换为你的更新说明)
git commit -m "XXXXXX"
# 拉取远程代码
git pull origin main
# 推送更新
git push origin main
7 总结与展望
7.1 总结
1. 功能落地:完成开源鸿蒙Flutter列表的“下拉刷新、上拉加载、数据加载提示”三大核心能力,解决数据加载卡顿、用户操作无反馈等问题;
2. 验证通过:在DevEco Studio(API 20模拟器)上完成场景验证,功能兼容、交互流畅;
3. 代码规范:遵循开源鸿蒙编码标准与Git提交规范,代码可维护、可复现,推送至本人AtomGit公开仓库后可直接拉取运行。
7.2 扩展方向
1. 通过新增底部选项卡及完善对应页面实现,丰富应用交互维度与服务能力,比如新增美食分类、收藏、搜索功能;
2. 为应用全面集成动效能力,覆盖页面转场、组件交互、数据加载等核心场景,提升应用视觉体验与交互流畅度。
最后,
欢迎加入开源鸿蒙跨平台社区:
更多推荐


所有评论(0)