Flutter for OpenHarmony 三重闯关踩坑实录:网络请求、列表刷新与底部导航的甜蜜冒险

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

前言:一场意想不到的甜蜜冒险

亲爱的开发者们,当你第一次踏上 Flutter for OpenHarmony 的旅程时,是不是觉得既兴奋又有点小紧张呢?就像第一次尝试烘焙蛋糕,明明按照食谱一步步来,却总是在意想不到的地方遇到小惊喜。今天,让我带你一起回顾这场"三重闯关"的甜蜜冒险——网络请求、列表刷新加载、底部导航栏,这三个看似简单的任务,每一个都藏着让人又爱又恨的小坑点呢!

第一关:网络请求的"迷雾森林"

初入江湖:dio 的温柔陷阱

选择 dio 作为网络请求库,就像选择了一位温柔的舞伴——它支持拦截器、FormData、Cookie 管理,功能强大又优雅。然而,当我在 OpenHarmony 设备上第一次运行时,却遇到了让人脸红的尴尬场面。

踩坑时刻一:网络权限的小秘密

应用启动后,数据迟迟加载不出来,屏幕上冷冰冰地显示着 “Connection error”。明明代码没问题,URL 也是正确的,为什么会这样呢?

原来,OpenHarmony 的权限系统比 Android 更加严格。我忘记在 module.json5 中正确配置网络权限了!

{
  "module": {
    "name": "entry",
    "type": "entry",
    "requestPermissions": [
      {"name": "ohos.permission.INTERNET"}
    ]
  }
}

注意哦,requestPermissions 是一个数组,每个权限对象必须包含 name 字段。我之前错误地把权限配置放在了其他位置,导致系统没有正确识别,真是让人小脸红呢~

踩坑时刻二:超时设置的小心机

在 OpenHarmony 平台上,网络子系统与 Android 存在差异,DNS 解析、TCP 连接建立可能耗时更长。默认的 15 秒超时根本不够用!

final Dio _dio = Dio(
  BaseOptions(
    connectTimeout: const Duration(seconds: 30),
    receiveTimeout: const Duration(seconds: 30),
    sendTimeout: const Duration(seconds: 30),
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
    },
  ),
);

给它多一点耐心,30 秒的超时设置让网络请求变得更加稳定可靠。

踩坑时刻三:错误处理的温柔呵护

网络请求失败时,不要让用户看到冷冰冰的技术错误信息。用温柔的方式告诉他们发生了什么:

String _handleError(DioException e) {
  switch (e.type) {
    case DioExceptionType.connectionTimeout:
      return '连接超时,请稍后重试';
    case DioExceptionType.sendTimeout:
      return '发送超时,请检查网络';
    case DioExceptionType.receiveTimeout:
      return '接收超时,请稍后重试';
    case DioExceptionType.badResponse:
      return '服务器错误: ${e.response?.statusCode}';
    case DioExceptionType.connectionError:
      return '网络连接失败,请检查网络设置';
    default:
      return '未知错误,请稍后重试';
  }
}

第二关:列表刷新加载的"旋转木马"

下拉刷新的小纠结

当网络请求终于跑通后,我满怀期待地实现了列表的下拉刷新和上拉加载。然而,OpenHarmony 设备上的表现却让我有点小失望。

踩坑时刻四:RefreshIndicator 的小脾气

Flutter 自带的 RefreshIndicator 在部分 OpenHarmony 设备上存在稳定性问题,刷新指示器有时显示异常,有时回调根本不触发!

温柔解决方案:设置 physics: const AlwaysScrollableScrollPhysics(),确保列表始终可以滚动。同时,添加一个刷新按钮作为备用入口,让用户多一种选择。

ListView.builder(
  physics: const AlwaysScrollableScrollPhysics(),
  itemCount: _todos.length,
  itemBuilder: (context, index) {
    // ... 列表项构建
  },
)

踩坑时刻五:状态管理的甜蜜陷阱

列表刷新加载涉及多个状态:初始加载、下拉刷新、上拉加载、加载完成、错误状态。如果状态管理不清晰,很容易产生冲突。

我采用了明确的状态变量定义:

List<TodoItem> _todos = [];
bool _isLoading = true;
bool _isLoadingMore = false;
bool _hasMoreData = true;
bool _isRefreshing = false;
String? _errorMessage;
int _currentPage = 0;
static const int _pageSize = 20;

每个状态变量都有明确的用途,互不干扰,就像给每个小精灵都安排了专属的小房间~

踩坑时刻六:上拉加载的边界处理

上拉加载时,最怕的就是重复加载和数据遗漏。我在方法开头加了两个判断条件:

Future<void> _onLoadMore() async {
  if (_isLoadingMore || !_hasMoreData) return;
  
  setState(() {
    _isLoadingMore = true;
  });
  
  try {
    final todos = await _todoService.getTodos(
      start: _currentPage * _pageSize,
      limit: _pageSize,
    );
    
    setState(() {
      if (todos.isEmpty) {
        _hasMoreData = false;
      } else {
        _todos.addAll(todos);
        _currentPage++;
        _hasMoreData = todos.length >= _pageSize;
      }
      _isLoadingMore = false;
    });
  } catch (e) {
    setState(() {
      _isLoadingMore = false;
    });
  }
}

这样就能确保不会重复触发加载,也能正确判断是否还有更多数据。

第三关:底部导航栏的"五重奏"

PageView + BottomNavigationBar 的完美搭配

当列表功能完善后,我决定为应用添加底部导航栏,让用户可以在五个页面之间自由切换。PageView 和 BottomNavigationBar 的组合,就像咖啡和奶泡一样天生一对!

踩坑时刻七:滑动和点击不同步的尴尬

用户滑动页面时,底部导航栏居然不跟着变!就像跳舞时舞伴没跟上节奏一样尴尬~

温柔解决方案:监听 PageView 的 onPageChanged 回调,同步更新底部导航栏的选中状态。

PageView(
  controller: _pageController,
  onPageChanged: (index) {
    setState(() {
      _currentIndex = index;
    });
  },
  children: _pages,
)

踩坑时刻八:内存泄漏的小秘密

应用越用越卡,最后崩溃了!就像气球漏气一样让人心疼~

贴心提醒:别忘了在 dispose 方法中释放 PageController 资源!


void dispose() {
  _pageController.dispose();
  super.dispose();
}

踩坑时刻九:图标风格不统一的遗憾

选中和未选中的图标风格不一样,看起来不够精致。Flutter 提供了 iconactiveIcon 两个属性,让我们可以分别设置:

BottomNavigationBarItem(
  icon: Icon(Icons.home_outlined),     // 未选中:优雅的线条
  activeIcon: Icon(Icons.home),         // 选中:饱满的实心
  label: '首页',
),

这样就能让视觉体验更加精致统一啦~

踩坑时刻十:页面状态保持的小智慧

切换 Tab 后再回来,页面数据又要重新加载?这可不行!

解决方案:使用 StatefulWidget 保持每个页面的状态,配合 PageView 的懒加载机制,让每个 Tab 都有专属的小窝,切换回来时数据还在那里等你呢~

运行截图:见证美好的时刻

【截图1:应用启动 - 加载状态】
在这里插入图片描述

应用启动时,显示优雅的加载动画,告诉用户数据正在努力加载中~

【截图2:下拉刷新】
在这里插入图片描述

轻轻下拉,刷新指示器优雅地旋转,数据更新后自动收起~

【截图3:上拉加载更多】
在这里插入图片描述

滚动到底部,加载指示器出现,新数据无缝衔接~

【截图4:底部导航栏切换】
在这里插入图片描述

页面切换起来丝滑流畅,就像在五个小房间之间自由穿梭~

甜蜜总结

通过这次"三重闯关"的实践,我深刻体会到 Flutter for OpenHarmony 开发的魅力与挑战。让我总结一下这些可爱的小坑点:

网络请求篇

  1. 权限配置:OpenHarmony 的权限系统更加严格,务必正确配置 ohos.permission.INTERNET
  2. 超时设置:给网络请求多一点耐心,30 秒超时让连接更稳定
  3. 错误处理:用温柔的方式告诉用户发生了什么,提升用户体验

列表刷新加载篇

  1. 滚动物理特性:设置 AlwaysScrollableScrollPhysics 确保列表可滚动
  2. 状态管理:明确的状态变量定义,避免状态冲突
  3. 边界处理:上拉加载要做好重复加载和数据遗漏的防护

底部导航栏篇

  1. 状态同步:滑动和点击要手拉手一起走
  2. 资源释放:Controller 用完要说再见
  3. 图标搭配:让视觉体验更精致统一
  4. 状态保持:用 StatefulWidget 保持页面状态

写在最后 ✨

编程就像烘焙一样,需要耐心、细心和一点点创意。当你看到自己的应用在 OpenHarmony 设备上完美运行时,那种成就感就像吃到自己亲手做的蛋糕一样甜蜜!

Flutter for OpenHarmony 为我们提供了一条高效的跨平台开发路径。虽然过程中会遇到各种小坑点,但只要用心去理解平台特性,这些问题都能迎刃而解。希望我的踩坑经历能帮助到正在探索这条路的你,让我们一起为开源鸿蒙跨平台生态贡献自己的力量吧!

本文的完整代码已经托管到 AtomGit 平台(https://atomgit.com),欢迎大家去围观学习,也欢迎在开源鸿蒙跨平台社区分享你的踩坑经历~

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐