资讯动态

鸿蒙跨平台Flutter实战:dio网络与列表状态管理

发布时间:2026/10/9 10:47:53 来源:尧图企业网站定制
如果你跟我一样坚持到了第三天说明前面两天的基础已经过了一遍环境能跑、页面能出、组件能交互。今天的任务很明确——让应用真正具备联网能力用Flutter的dio网络请求库去拉一个真实的游戏列表接口然后通过列表页展示出来并且工程还跑在Open Harmony平台上。这个目标听起来不复杂但实际动手会碰到一堆和纯Android/iOS开发不太一样的细节。这篇博文不是官方文档的搬运而是我这三天的实际学习记录。我会把网络层怎么封装、列表状态怎么管理、在OpenHarmony上运行需要处理哪些坑包括我在Windows环境下踩过的Gradle和工程创建问题都原原本本写出来。适合正在跟着做Flutter for OpenHarmony系列学习的开发者也适合那些Dart语法已经会了、但还没系统写过一个完整网络请求项目的朋友——你看完可以直接照着敲一遍。1. 为什么我在OpenHarmony项目里选了Flutter dio选型背后的真实考量1.1 “ArkTS和Flutter谁更流行”这个问题本身就是个伪命题最近总看到有人在问“ArkTS和Flutter谁更流行”尤其是我这种用Flutter做鸿蒙应用的人经常会被当成“站队”的一方。但如果你真的动手做过几个应用就会明白这根本不是二选一的事。ArkTS是OpenHarmony原生应用的主力开发语言它和系统API结合得最紧密如果你要做的是纯鸿蒙生态内的应用比如要调用大量系统能力、想要最小的包体积那ArkTS一定是首选。而Flutter的好处在于跨端复用一套代码跑Android、iOS、Windows现在也能跑OpenHarmony。对我个人来说团队里已经有现成的Flutter技术积累OpenHarmony这边又正好有对应的Flutter引擎分支选择Flutter是成本和风险都更可控的方案。并不是说Flutter比ArkTS更“先进”而是要看你的实际处境。我这次选择Flutter是因为我想在OpenHarmony上验证一个跨平台UI框架到底能适配到什么程度。如果你是从零开始、只做鸿蒙端我反而建议你先学ArkTS——语言本身不难生态工具也跟得很快。1.2 Impeller引擎Flutter在OpenHarmony上绕不开的渲染话题在这个系列学习过程中“Impeller”这个词出现的频率不低。它是Flutter新一代的渲染引擎目标是替换掉Skia解决之前在某些平台上出现的线程不稳定和帧率抖动问题。Impeller的核心思路是把着色器编译前置到构建阶段减少运行时编译带来的卡顿同时用Metal和Vulkan之类的现代图形API替代老旧的OpenGL通道。放到OpenHarmony的语境里情况会稍微复杂一点。OpenHarmony的Flutter分支版本更新通常滞后于主分支所以Impeller是否默认启用、启用后渲染表现是否稳定完全取决于你拉取的分支和SDK版本。我自己实测时用默认配置跑游戏列表里的缩略图滚动动效基本是顺滑的但如果你的设备驱动对Vulkan支持不好反而可能出现闪屏这时候就需要在工程配置里调整渲染后端。这块不用过度焦虑。大多数时候默认配置都能跑真遇到问题再按官方说明切回Skia也不迟。重要的是你得知道Impeller是什么将来别人提到“Flutter在鸿蒙上卡不卡”时你能判断到底是框架的问题还是渲染引擎配置的问题。1.3 dio对比http为什么网络层用dio而不是自带的http包Dart生态里最基础的网络请求库是http简单直接写个小Demo完全够用。但到游戏列表这种需要统一baseUrl、统一超时、打印日志、处理异常的场景http就有点力不从心了。我对比过两者在日常项目里的体验能力维度diohttp全局BaseUrl配置支持写在BaseOptions里每次请求都要拼完整地址请求/响应拦截器支持可插桩日志、鉴权、重试没有拦截器概念超时时间设置连接超时和接收超时分开配置需要自己用timeout包一层取消请求支持CancelToken需要手动处理FormData/文件上传原生支持需要自己拼multipart响应转换自带JSON解码可使用泛型需要手动jsonDecode当然http也有它的价值零依赖、逻辑简单、适合快速验证一个接口通不通。但我要做的是一个完整应用后面还要在这个基础上加详情页、收藏、分页网络层必须有一个稳定的底座所以我直接选了dio。后面几节你会看到dio的拦截器在排查网络问题时真的帮了大忙。2. 游戏列表应用的项目骨架与数据源规划2.1 这一天的目标不是“写个UI”而是通一条数据链路开始之前先给Day 3定个范围。我最终要做的是这样一个页面顶部是应用标题下方是可以滚动的游戏卡片列表每张卡片包含缩略图、游戏名称、类型和一句简介。列表支持下拉刷新滚动到底部会继续加载更多数据。第一次请求失败时页面显示错误信息和重试按钮而不是白屏。这个目标拆开来看其实覆盖了三条核心链路数据链路dio发起请求拿到JSON解析成Dart模型。状态链路Provider管理加载中、成功、失败、加载更多这些状态。UI链路ListView消费状态渲染卡片响应用户下拉和点击操作。这三条链路一旦打通后面的游戏详情页、收藏功能、搜索功能都是在这个骨架上加肉学起来非常快。2.2 数据源一个无需API Key的免费游戏接口做学习项目最怕什么最怕你想请求的接口需要注册、申请Key、还要处理各种鉴权。这次我用的数据源是FreeToGame提供的公开API它的/api/games接口直接返回一份游戏列表不需要任何Key非常适合教学和Demo演示。响应数据长这样[ { id: 452, title: Call of Duty: Warzone, thumbnail: https://www.freetogame.com/g/452/thumbnail.jpg, short_description: A free-to-play battle royale game..., genre: Shooter, publisher: Activision, developer: Infinity Ward, release_date: 2020-03-10 } ]我在项目里只需要关心这些字段id、title、thumbnail、short_description、genre和release_date足够把UI撑起来了。为了万无一失我在代码里会做一个本地JSON的兜底方案万一哪天网络环境访问不了这个接口项目也能用本地数据继续开发。2.3 工程目录结构一开始就把分层想清楚有很多初学者建完Flutter项目就把所有代码都塞进main.dart这个习惯在项目稍微变大之后会特别痛苦。我从Day 1开始就给项目做了分层规划lib/ ├── main.dart ├── models/ │ └── game.dart ├── services/ │ └── game_api.dart ├── providers/ │ └── game_provider.dart ├── pages/ │ └── home_page.dart └── widgets/ └── game_card.dartmodels放数据模型services放网络请求层providers放状态管理pages放页面widgets放可复用的UI组件。这样设计不是为了好看而是为了让你改动任何一个环节时不会牵一发而动全身。比如今天想换数据源只改services层明天想换状态管理库只改providers层后天想改卡片样式只动widgets层。3. dio网络层封装请求、拦截与异常处理的完整套路3.1 添加依赖pubspec.yaml里的两行代码第一步是在项目根目录的pubspec.yaml里添加dio和provider的依赖。我用的是当前稳定版本版本号这里写清楚方便大家对齐dependencies: flutter: sdk: flutter dio: ^5.4.0 provider: ^6.1.1保存文件后在项目根目录执行flutter pub get依赖就会自动下载。这一步在OpenHarmony分支的Flutter上也是通用的因为Dart包管理器和平台是解耦的不存在“鸿蒙专用版dio”这种说法。3.2 定义Game模型JSON到Dart对象的映射写网络请求之前得先把数据模型定下来。这一步很多人觉得枯燥但模型写得好不好直接决定后面解析逻辑的复杂度。class Game { final int id; final String title; final String thumbnail; final String shortDescription; final String genre; final String releaseDate; Game({ required this.id, required this.title, required this.thumbnail, required this.shortDescription, required this.genre, required this.releaseDate, }); factory Game.fromJson(MapString, dynamic json) { return Game( id: json[id] as int? ?? 0, title: json[title] as String? ?? , thumbnail: json[thumbnail] as String? ?? , shortDescription: json[short_description] as String? ?? , genre: json[genre] as String? ?? Unknown, releaseDate: json[release_date] as String? ?? , ); } }这里有两个细节值得说。第一我用??做了空值兜底这是从实际报错里学来的——有些游戏在接口里没有genre字段直接用json[genre] as String会抛转换异常整个列表就崩了。第二short_description字段在JSON里是snake_case我把它映射成了Dart的驼峰命名shortDescription后面所有代码引用起来都更符合Dart风格。3.3 封装GameApi全局BaseUrl、超时和拦截器接下来是网络层的核心。我建了一个GameApi类把请求的通用配置都集中在这里。import package:dio/dio.dart; import ../models/game.dart; class GameApi { GameApi() { _dio Dio( BaseOptions( baseUrl: https://www.freetogame.com/api, connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 10), ), )..interceptors.add( LogInterceptor(requestBody: true, responseBody: false), ); } late final Dio _dio; FutureListGame fetchGames() async { try { final response await _dio.get(/games); if (response.statusCode ! 200) { throw Exception(服务器返回异常${response.statusCode}); } final data response.data as List; return data .map((item) Game.fromJson(item as MapString, dynamic)) .toList(); } on DioException catch (e) { throw Exception(网络请求失败${e.message}); } } }你可能注意到了我把LogInterceptor的responseBody设置成了false。因为游戏列表接口一次会返回大量数据如果每次请求都把完整响应体打出来控制台会刷到卡顿。在调试时我更推荐先开responseBody: false看状态码和耗时需要确认具体数据结构时再临时改成true。这个类的设计理念是“内部消化dio的复杂度”。UI层和Provider在调用时只关心“给我一个游戏列表”或者“抛一个异常”不关心dio的DioException长什么样。这样将来就算换网络库也只改这一个文件。3.4 异常处理矩阵把网络问题拆开来看在实际调试中我发现很多新手对网络异常的处理就是“catch到就弹个Toast”这远远不够。我整理了一个异常处理对照表在代码里我也会按这个思路去逐层判断异常类型常见原因处理建议DioExceptionType.connectionTimeout设备网络慢或接口地址不可达提示“连接超时”并提供重试DioExceptionType.receiveTimeout服务端响应数据量太大提示“等待超时”建议缩小单页数据量DioExceptionType.badResponse接口状态码非2xx展示具体状态码方便后端排查DioExceptionType.unknown断网、DNS解析失败提示“网络不可用”引导检查飞行模式非DioException业务层抛出的异常捕获后统一走通用错误提示一种很实用的做法是把这些判断收敛到一个函数里比如mapDioErrorToString这样不管是加载游戏列表还是将来加载游戏详情调用方都只需要写一行代码。3.5 日志拦截器网络排错的隐形助手我这次在OpenHarmony设备上调试时遇到过一个奇怪的问题模拟器上请求游戏列表一切正常换到真机上就时不时报unknown异常。当时百思不解后来打开LogInterceptor才发现真机和模拟器走的是不同的网络通道真机上的HTTPS握手偶尔会失败。这种问题如果没有日志纯靠肉眼盯数据是根本定位不到的。所以我建议你把LogInterceptor当成网络层的标配而不是一个“调试完就删掉”的临时工具。上线前可以把它放到debug环境下用KDebugMode之类的判断包一下生产环境不打印就行。4. 列表页与Provider状态管理UI层如何优雅地消费网络数据4.1 为什么我选了Provider而不是setState和FutureBuilder在做列表页之前我先做个简单的状态管理方案对比因为这直接决定代码怎么写。setState是Flutter最基础的状态刷新方式适合页面内零散的状态变化比如一个按钮的选中态。但游戏列表这种场景涉及“加载中、成功、失败、加载更多、刷新中”多个状态如果全部用setState管理页面会堆满_loading、_error、_games这类字段并且这些状态还要在多个Widget之间传递很麻烦。FutureBuilder能省掉一部分手动状态管理我只需要提供一个Future给UI层它就能自动重建。但问题在于FutureBuilder不适合“同一个Future需要被多次触发”的场景比如下拉刷新、加载更多。你会发现FutureBuilder在这种需求下非常别扭——每次触发都需要重新创建Future稍不留神还会触发重复请求。Provider的思路完全不同它把状态和数据放到一个单独的GameProvider类里UI层通过context.watch或context.read订阅自己关心的部分。谁需要状态谁就去读不需要靠构造函数一路传参。4.2 用GameProvider管理列表状态详细代码我的GameProvider维护了六类状态import package:flutter/foundation.dart; import ../models/game.dart; import ../services/game_api.dart; class GameProvider extends ChangeNotifier { final GameApi _gameApi GameApi(); ListGame _games []; bool _isLoading false; bool _isLoadingMore false; String? _error; int _visibleCount 10; ListGame get games _games.sublist(0, _visibleCount.clamp(0, _games.length)); bool get isLoading _isLoading; bool get isLoadingMore _isLoadingMore; String? get error _error; Futurevoid loadGames() async { _isLoading true; _error null; notifyListeners(); try { final result await _gameApi.fetchGames(); _games result; _visibleCount 10; _error null; } catch (e) { _error e.toString(); } finally { _isLoading false; notifyListeners(); } } void loadMore() { if (_isLoadingMore || _visibleCount _games.length) return; _isLoadingMore true; notifyListeners(); Future.delayed(const Duration(milliseconds: 500), () { _visibleCount 10; _isLoadingMore false; notifyListeners(); }); } Futurevoid refresh() loadGames(); }这里有个设计细节我并没有一股脑把所有游戏都渲染出来而是用_visibleCount控制当前显示的条数每次滚动到底部再增加10条。这么做有两个好处第一是模拟真实项目的分页加载形态防止以后接后端分页接口时手足无措第二是减少一屏加载过多图片对渲染性能的影响在OpenHarmony真机上尤其明显——一次渲染几百张网络缩略图滚动卡顿是必然的。4.3 页面接入Providermain.dart里的配置在main.dart里我用MultiProvider把GameProvider挂到应用顶层void main() { runApp( MultiProvider( providers: [ ChangeNotifierProvider(create: (_) GameProvider()..loadGames()), ], child: const GameListApp(), ), ); }create里写GameProvider()..loadGames()意味着应用启动后立刻触发第一次请求页面一打开就能看到加载状态。这是一种常用的启动加载方式当然你也可以改成懒加载等到页面进入时再触发。4.4 组件通信从父传子、子回调到跨页面传参游戏列表页非常典型地体现了Flutter组件通信的几种方式这些都是网上高频问题“flutter组件通信”的答案。第一层父组件传数据给子组件。在itemBuilder里我创建GameCard时把Game对象通过构造函数传进去GameCard( game: game, onTap: () { Navigator.push( context, MaterialPageRoute( builder: (_) GameDetailPage(game: game), ), ); }, )第二层子组件通知父组件。GameCard里不直接处理导航逻辑而是通过一个onTap回调把“用户点击了卡片”这个事件交给父组件。这样GameCard就变成了一个纯展示组件将来想复用到搜索页、收藏页都很容易。第三层跨页面共享状态。如果游戏详情页需要标记“已收藏”而列表页需要跟着刷新这时就可以用Provider来共享状态。收藏状态放在FavoritesProvider里页面A修改页面B自动收到通知。这套组合拳覆盖了组件通信的绝大部分场景构造参数向下传数据回调向上传事件Provider实现跨层共享。4.5 ListView的下拉刷新与加载更多页面主体我用的是ListView.separated加RefreshIndicator。下拉刷新用RefreshIndicator包ListView这是Flutter最标准的做法RefreshIndicator( onRefresh: provider.refresh, child: ListView.separated( controller: _scrollController, itemCount: provider.games.length (provider.isLoadingMore ? 1 : 0), separatorBuilder: (_, __) const Divider(height: 4), itemBuilder: (context, index) { if (index provider.games.length) { return const Padding( padding: EdgeInsets.all(16), child: Center(child: CircularProgressIndicator()), ); } return GameCard(game: provider.games[index], onTap: _handleTap); }, ), )加载更多需要监听滚动位置。我给ListView挂了一个ScrollControllervoid _setupScrollListener() { _scrollController.addListener(() { if (_scrollController.position.pixels _scrollController.position.maxScrollExtent - 300) { provider.loadMore(); } }); }这个- 300的提前量很关键。如果等到完全滚动到底部才触发加载用户会明显感觉到“卡了一下”提前300像素触发等用户滚到底时新数据基本已经渲染好了体验会顺滑很多。4.6 卡片UI的小细节图片占位、错误图标和统一间距GameCard的构建看起来很直观但图片加载部分有几个细节我特别想提Image.network( game.thumbnail, fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress null) return child; return Container( color: Colors.grey[200], child: const Center( child: SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ), ), ); }, errorBuilder: (context, error, stackTrace) Container( color: Colors.grey[300], child: const Icon(Icons.videogame_asset, size: 32), ), )很多初学者直接写Image.network(game.thumbnail)图片没加载出来时就是一片空白非常难看。加了loadingBuilder后加载过程中会显示一个小转圈加了errorBuilder后图片加载失败会显示一个游戏手柄的图标占位视觉上稳定很多。卡片的圆角我用了ClipRRect包住图片文字部分用Expanded控制保证不同游戏名称的长度不会把布局撑破。遇到特别长的标题时用maxLines: 1加ellipsis截断这是列表UI的基础素养。5. 在OpenHarmony上跑起来的适配工作从网络权限到渲染引擎5.1 网络权限OpenHarmony应用默认不能访问互联网Flutter工程在OpenHarmony上跑起来后如果直接执行网络请求大概率会碰到权限问题。原因很简单OpenHarmony应用默认不授予网络访问权限你需要在模块配置里显式声明。以DevEco Studio创建的OpenHarmony工程为例在entry/src/main/module.json5这个文件里找到requestPermissions字段添加requestPermissions: [ { name: ohos.permission.INTERNET } ]如果你的应用还要访问本地局域网的开发调试接口可能还需要ohos.permission.GET_WIFI_INFO之类的配套权限具体看你的调试需求。这一步很容易被忽略因为Flutter本身不强制你处理平台权限——你在Android上不声明网络权限也会崩溃但报错时间更晚、提示更隐晦排查成本反而更高。5.2 Flutter引擎和AAR的集成方式简单理解一下说到Flutter在OpenHarmony上运行很多人会提到“flutter aar”这个热词。其实AAR是Android Library的一种打包格式OpenHarmony生态里的Flutter引擎包也沿用了这套机制——引擎被打包成一个AAR或类似的库文件你的Flutter代码最终会跟这个引擎库一起编译进HAP应用包。理解这个概念有什么用它决定了你在遇到编译问题时的排查方向。如果你的Flutter代码本身没有报错但打开应用一直白屏多半是引擎库和你的Flutter SDK版本不匹配。这时候不是去改Dart代码而是检查你的flutter_flutter分支是否更新到了对应版本然后重新编译。5.3 Impeller在OpenHarmony上的现状和我的建议前面已经介绍了Impeller是什么这里说说我在OpenHarmony上实测的情况。当前OpenHarmony的Flutter分支对Impeller的支持并不是默认开启的不同版本之间差异也比较大。我的建议是先使用项目默认的渲染配置跑通功能。如果滚动列表时出现明显掉帧再尝试切换Impeller或Skia。切换渲染引擎的方式通常是在工程配置文件里加一个开关具体参数要看分支版本的说明。我在测试游戏卡片滚动时默认配置下帧率是满足日常使用的。真正让我改渲染配置的是在真机上加载大量网络图片时图片跳动感比较明显后来通过给图片组件增加缓存复用的方式解决了就没去动Impeller。5.4 OpenHarmony设备调试hdc和flutter设备的配合在OpenHarmony真机上调试你需要确认flutter devices能识别到设备。如果识别不到先检查hdc工具是否安装并且已经加入了系统PATH再看设备有没有开启开发者模式并授权。这个流程和Android的adb远程调试思路类似但工具链名称不同。设备识别成功之后flutter run -d的流程和Android几乎一样。热重载在OpenHarmony分支上可用这个真的帮了大忙——我调卡片样式时基本没重新跑过完整应用每次改动不到两秒就能看到新效果。但要注意网络请求代码、本地JSON解析这类改动最好还是热重启而不是热重载因为涉及变量初始化逻辑时热重载会残留旧状态产生一种“改了等于没改”的错觉。6. 踩坑实录从新建项目跑不起来到Gradle apply报错的抢救过程6.1 坑flutter新建项目后跑不起来的排查链路Day 1的时候我就卡在“新建项目后跑不起来”这个经典问题上。症状是flutter create成功生成了工程但执行flutter run时要么找不到设备要么Gradle同步失败要么报乱码的底层错误。我后来梳理了一套排查链路分享给你先确认flutter自身没问题执行flutter doctor看有没有红色的叉。最常见的是没有配置OpenHarmony SDK路径。再确认设备能被识别执行flutter devices。如果列表里没有OpenHarmony设备说明hdc没配置好或者设备没授权。接着看Gradle日志执行flutter run -v输出会详细很多。很多“新建项目跑不起来”的问题本质是工程模板和本地Gradle版本不匹配。最后看代码层把入口文件改成最简单的一个Text居中显示如果这个能跑起来再逐步加代码缩小问题范围。我用这套方法排查后发现自己的问题出在第二个环节——hdc配置好了但设备授权弹窗没点确认属于纯环境问题不是代码问题。这种排查思路以后会反复用到值得养成习惯。6.2 坑You are applying flutters main gradle plugin imperatively using the apply method这个报错我想专门写一下因为它的出现概率太高了而且错误信息又长又吓人You are applying flutters main gradle plugin imperatively using the apply method, which is no longer supported. Replace the apply method with the plugins block in your settings.gradle file.它的原因很简单新版Flutter工程模板不再推荐用apply这种方式加载Flutter Gradle插件转而推荐使用plugins块。如果你基于旧模板创建项目然后又复制了一些新版本的配置代码就容易出现两套写法混在一起的情况。解决办法分两步。第一步打开android/settings.gradle把apply方式改成plugins方式plugins { id com.android.application version 8.1.0 apply false id dev.flutter.flutter-plugin-loader version 1.0.0 }第二步检查android/app/build.gradle头部确认它是plugins写法而不是apply写法plugins { id com.android.application id dev.flutter.flutter-gradle-plugin }改完之后重新跑flutter clean再flutter pub get这个报错基本就消失了。记住一个原则同一个工程里不要混用两代Gradle插件写法报错只是迟早的问题。6.3 坑Windows环境下的编辑器选择与路径问题在Windows上做Flutter开发大多数人会装VS Code加Flutter插件但总有新人把Visual Studio和VS Code搞混。Visual Studio本身也能做Flutter开发但更重平时维护Flutter工程用VS Code就足够了。除了编辑器Windows上还有一个很隐蔽的坑工程路径里不能有中文或空格。比如你建个项目放在D:\游戏列表应用\后面Gradle编译时大概率会报一些莫名其妙的路径错误。这个不是玄学是Gradle对中文路径的兼容性问题把项目移到纯英文路径下就能解决。另外一个和Visual Studio相关的问题是如果你的电脑同时装了多个版本的C Redistributable偶发会遇到NDK编译失败这时候CtrlR运行appwiz.cpl清理旧版本组件可能就恢复正常了。6.4 坑用Provider时报“Looking up a deactivated widgets ancestor is unsafe”这个错误属于Provider使用不当的经典问题。我在加载更多功能时遇到过用户快速下拉刷新又立刻点进详情然后退出列表页Provider已经被dispose了UI还在尝试读取它的状态。解决方案有两个层面。第一使用普通页面管理时页面顶部的Provider要确保生命周期足够长如果是整个应用共享的状态放到MultiProvider顶层是最安全的。第二异步操作返回后不要盲目notifyListeners()先判断一下当前页面组件是否还在挂载可以用mounted属性做保护if (!mounted) return;这个习惯养成之后能帮你避免很多偶发的状态崩溃问题不只是Provider用Bloc、Riverpod也一样。6.5 小技巧如何在OpenHarmony上调试dio请求的底层细节说一个调试经验。OpenHarmony设备上跑Flutterdio的LogInterceptor是一个层面但有时候网络层显示成功了UI层却还是没数据这时候就要怀疑是不是JSON解析出了问题。我的做法是写一个临时解析函数在请求成功后把response.data先转成字符串打印出来然后手动挑两条数据检查字段名是否和模型一致。FreeToGame接口的short_description字段名里带着下划线我一开始模型里写成shortDescription其实映射逻辑没问题但一旦我看错原始字段名解析就会返回空字符串列表看起来没数据但接口明明有返回。这种排查思路在其他网络上也很通用先确认数据到了再确认解析对了最后才改UI。顺序千万不能反。最后再说两句给同样在学Flutter for OpenHarmony的人三天的学习走下来最大的感触是跨平台框架的“跨”不是写一遍代码到处能跑就完了每个平台都有自己的脾性。Flutter把UI部分的差异抹平了但网络权限、Gradle构建、设备调试、渲染引擎适配这些平台特性还是得一个个去碰。关于这个游戏列表项目我下一步打算把游戏详情页补完整用同一个GameApi去做请求再把收藏功能做成本地持久化让列表页和详情页通过Provider共用一份收藏状态。这个方向既能把组件通信的操作练扎实也不会让项目膨胀到失控。如果你也是跟着这个系列一路学过来的建议你手边准备好两个东西一个能联网的OpenHarmony真机或模拟器一份你熟悉的公开JSON接口文档。工具就位之后照着这篇文章把网络层和列表页敲一遍再试着把数据源换成你自己的接口——遇到问题不用急看看LogInterceptor的日志绝大多数答案都在里面。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑