【开源鸿蒙跨平台开发先锋训练营】DAY4~DAY6 OpenHarmony版Flutter本地美食清单上拉加载 + 下拉刷新 + 数据加载提示实现

目录

【开源鸿蒙跨平台开发先锋训练营】DAY4~DAY6 OpenHarmony版Flutter本地美食清单上拉加载 + 下拉刷新 + 数据加载提示实现

摘  要

1 概述

1.1 开发背景

1.2 开发目标

1.3 核心技术栈

2 核心功能实现

2.1 功能核心定位

2.2 基础配置准备

2.2.1 开发环境

2.2.2 核心依赖配置

2.2.3 配置本地模拟美食API

3 API层:分页请求封装

3.1 实现目标

3.2 具体修改内容

3.3 功能作用

4 页面层:核心逻辑实现

4.1 实现目标

4.2 状态变量定义(页面层核心基础)

4.3 下拉刷新逻辑实现

4.4 上拉加载逻辑实现

4.5 数据加载提示实现

5 测试与验证

5.1 测试环境

5.2 功能验证

6 代码提交与 AtomGit 仓库更新

6.1 查看本地修改

6.2 暂存本地修改

6.3 提交本地修改

6.4 拉取远程仓库最新代码

6.5 推送更新到 AtomGit 仓库

6.6 验证推送结果

7 总结与展望

7.1 总结

7.2 扩展方向


摘  要

本文基于开源鸿蒙跨平台开发框架,针对本地美食清单应用的列表功能进行扩展优化。完成列表下拉刷新、上拉加载、数据加载提示三大能力实现,并在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. 为应用全面集成动效能力,覆盖页面转场、组件交互、数据加载等核心场景,提升应用视觉体验与交互流畅度。

最后,

欢迎加入开源鸿蒙跨平台社区:

https://openharmonycrossplatform.csdn.net

Logo

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

更多推荐