多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

OpenHarmony上Flutter网络请求与页面渲染完整指南

OpenHarmony上Flutter网络请求与页面渲染完整指南 带训练营这期项目的时候学员拿到任务的第一反应基本都一样“OpenHarmony上不是有ArkTS吗为什么还要用Flutter来写” 这个问题问得特别实在。我在实际项目里被问过无数遍这篇就把“在OpenHarmony设备上用Flutter请求网络接口、把数据渲染到页面上”这条完整链路拆开讲一遍包含选型逻辑、环境配置、网络层封装、渲染方案、常见坑和性能优化。内容对标训练营里的实操任务也适合刚接触OpenHarmony的Flutter开发者、想做跨平台方案评估的技术负责人。要啃完这篇你得有一些Flutter基础哪怕不太熟也没关系代码我会给全关键路径我都会标注为什么这么做。1. 为什么在OpenHarmony上选Flutter做跨平台1.1 先回答那个最经典的问题ArkTS不香吗OpenHarmony应用层推荐的是ArkTS和ArkUI这没问题。但你要看清楚它解决的是“OpenHarmony生态内”的问题。我们训练营的项目目标是“一套代码多端复用”除了OpenHarmony还要跑Android、iOS甚至桌面端。ArkTS在这件事上天生帮不了你——它不是为跨端设计的。很多团队试过用ArkTS写一遍再用Kotlin写一遍再用Swift写一遍最后发现三个端三个Bug清单维护成本直接翻倍。Flutter不一样它是自绘引擎UI不依赖系统控件。同一套Dart代码在OpenHarmony、Android、iOS上渲染出来的视觉效果基本一致。实际测试下来在香橙派5 Pro这类OpenHarmony开发板上Flutter页面跑起来非常顺主要原因是它的渲染走的是GPU合成跟设备厂商的自有控件体系没有绑定关系。这里要插一句OpenHarmony自身的底层主要是C/C写的上层框架是ArkTS/ArkUI而Flutter是在这个上层框架之外通过OpenHarmony的适配引擎把自己作为独立渲染层跑起来所以各个版本的兼容性关键看适配引擎的成熟度而不是看ArkUI的接口。1.2 Flutter在OpenHarmony上的适配现状Flutter对OpenHarmony的支持不是Google官方直接维护的而是OpenHarmony开源社区里SIG组在推进。你能用到的工具链是Flutter SDK的一个OpenHarmony适配分支构建产物最终会封装成hap或hsp主工程再用DevEco Studio去集成。说得直白一点Flutter在OpenHarmony上的角色更像是一个“跨端渲染引擎 Dart运行环境”你在Flutter里写的业务逻辑、网络层、状态管理全都跨平台复用只有涉及调用OpenHarmony特有能力的场景才需要写桥接代码。有些团队在Flutter和Slint之间纠结。Slint在嵌入式设备上有它的优势内存占用更小、渲染更轻量适合强资源受限的场景。但训练营这个项目要的是快速迭代、丰富组件库、成熟生态Flutter在开发效率和社区资源上的优势是碾压级的。选型上我始终建议如果你的核心诉求是“快速覆盖多端、业务迭代频繁”直接选Flutter如果你做的是传感器面板、设备控制这类极度精简的嵌入式界面再看Slint这类轻量方案。1.3 训练营项目到底要做什么训练营的任务设定是在OpenHarmony开发板上用Flutter写一个资讯流页面从远端接口拉取文章列表展示标题、摘要、封面图支持下拉刷新和加载更多。听起来简单但里面涉及到的内容正好构成一个完整的入门链路环境搭建、网络请求、JSON解析、状态管理、列表渲染、异常处理、性能优化。这篇文章会带着你把这个链路完整走一遍。顺带说一句这个任务跟一些开源项目里“跨平台音乐管理系统v2.0”的结构非常像——都是列表 网络请求 详情页你把这个流程吃透那些项目源码看起来就不会一头雾水了。2. 环境准备与项目初始化2.1 版本匹配是第一个大坑OpenHarmony上跑Flutter版本匹配比普通Flutter开发严格得多。因为适配分支一般跟着特定Flutter版本走你用最新的Flutter SDK直接跑OpenHarmony设备大概率会撞上一堆编译错误。训练营里我选的是OpenHarmony适配分支对应的Flutter稳定版太新的版本依赖引擎更新适配层还没来得及跟上。第一个建议去OpenHarmony的Flutter适配仓库看README那里会明确写支持哪个Flutter版本。不同小版本的差异也会影响你后面能不能顺利打出hap包。确认好版本之后把本地Flutter SDK切到对应分支或对应tag上再配置OpenHarmony SDK路径。这里容易搞错的一点是OpenHarmony SDK和Android SDK不是一个东西别只配了Android环境就开始跑——Flutter在OpenHarmony上构建hap包时依赖的是DevEco Studio里自带的OpenHarmony SDK需要在环境变量里单独指过去。实际操作中很多学员卡在“flutter新建项目后跑不起来”。这个问题的原因至少一半出在版本错配上要么是Dart SDK和Flutter SDK不匹配要么是OpenHarmony SDK版本跟DevEco Studio对不上。项目报错信息里会有一串路径你要仔细看是哪一层在报错而不是盲目重装。训练营里我要求的排查顺序是先看flutter doctor再看OpenHarmony SDK路径最后看项目的ohos目录有没有生成成功。2.2 创建OpenHarmony平台目录用普通Flutter命令创建的工程默认只有android、ios、web这些目录。OpenHarmony支持需要额外生成ohos目录。适配分支里通常提供了扩展命令比如flutter create加上指定参数或者在已有工程里执行生成命令。生成成功之后你的工程目录会多出一个ohos文件夹里面的结构和Android工程类似有entry模块有module.json5配置文件。这里有个细节很多文章不会提ohos目录生成之后你需要用DevEco Studio打开这个目录在IDE里做一次Sync否则一些资源文件和签名配置不会自动同步到构建流程里。如果你跳过了这步后面构建hap包的时候会遇到各种“找不到模块”的诡异报错。另外建议签名配置直接用DevEco的自动签名。OpenHarmony的真机调试对签名校验是硬性的不签名装不到设备上。训练营里有学员想省事跳过签名结果项目在DevEco Studio里跑不起来只能回头补配置。2.3 别忘了网络权限OpenHarmony应用访问网络是要在module.json5里显式声明权限的。很多新手在Android上习惯了AndroidManifest.xml里加INTERNET权限到了OpenHarmony上忘了对应操作结果一请求接口就报“网络连接失败”。Android的权限模型和OpenHarmony的权限声明方式类似但不同文件这个坑高频到值得单列一节。{ module: { name: entry, requestPermissions: [ { name: ohos.permission.INTERNET } ] } }module.json5是OpenHarmony模块的配置文件requestPermissions就是声明权限的数组。除了INTERNET如果你后面要访问本地网络、读取设备信息都要对应添加权限。我建议在项目一开始就把INTERNET权限加上别等到真机联调接口时才回来改。这里顺便说一句用Visual Studio写Flutter是可行的Flutter本身的Dart插件对IDE支持还算灵活。但如果你要连OpenHarmony设备调试、打hap包还是绕不开DevEco Studio。我的习惯是写Dart代码用VS Code构建和跑设备用DevEco Studio两边互补效率很高。当然这只是个人习惯你团队里统一用DevEco也没问题重点是把两套工具的角色分清写代码的归写代码构建打包的归构建打包。3. 网络请求层从http到dio的完整落地3.1 请求库选型dio为什么是首选Flutter里发网络请求入门教程最爱用的是http包。http包确实简单几行代码就能拿到响应字符串。但你在真实项目里很快就会遇到需求统一超时时间、统一错误处理、打印请求日志、上传下载进度。这些东西用http包实现起来全部要自己造轮子代码量会迅速膨胀。训练营这个项目我也让学员先试了一下http目的就是让他们感受到“为什么需要dio”。dio是Flutter生态里最常用的网络库它在OpenHarmony上的表现也很稳定。核心优势有三点拦截器机制、全局BaseOptions配置、丰富的错误类型。拦截器能让你把token注入、日志打印、异常上报统一放到一个地方不用每个请求都重复写。配置BaseOptions的时候你要想清楚你项目的接口风格baseUrl写对了后面所有请求都只要写相对路径。训练营里有一个典型场景不同开发环境的接口地址不一样今天连测试环境明天连联调环境。你可以通过拦截器在运行时动态切换baseUrl也可以打不同的构建flavor。这个灵活度在用http包时是做不到的所以你如果打算长期维护这个项目直接在dio上投入时间更划算。3.2 构建数据模型与JSON解析接口返回的数据长什么样决定了你的Model层怎么设计。以资讯流为例后端返回的JSON结构大概是这样的{ id: 1, title: 开源鸿蒙跨平台开发实践, summary: 这篇文章介绍如何在OpenHarmony上使用Flutter。, cover_url: https://example.com/cover.png }你需要在Dart里建一个对应的Article类同时实现fromJson工厂方法。这里我想强调一个点训练营里我要求学员手写fromJson而不是直接上json_serializable。原因很简单手写能让你真正理解JSON反序列化的过程知道as int和as String这些类型转换背后的原理。等你项目越来越大、字段越来越多时再引入代码生成工具不迟。过早引入工具会让你对数据流的感知变钝出了问题反而不容易定位。class Article { final int id; final String title; final String summary; final String? coverUrl; Article({ required this.id, required this.title, required this.summary, this.coverUrl, }); factory Article.fromJson(MapString, dynamic json) { return Article( id: json[id] as int, title: json[title] as String, summary: json[summary] as String, coverUrl: json[cover_url] as String?, ); } }注意coverUrl我声明成了String?因为列表里有些文章没有封面图。如果后端返回null而你还用强类型as String程序直接崩。训练营里这个错误出现频率奇高大家习惯性认为后端字段一定是完整的实际上接口里空值太常见了。你在写fromJson时凡是可能为空的字段都要用可空类型接收并且做好默认值兜底。3.3 网络层封装统一入口不建议每个页面直接new Dio然后发请求。更好的做法是把Dio实例做成单例配置统一好页面只管调用。这个封装方案在Flutter社区里非常常见并不复杂。把拦截器、超时时间、错误映射都塞进去后面维护起来会庆幸自己当时没偷懒。class ApiClient { ApiClient._internal() { _dio Dio( BaseOptions( baseUrl: https://api.example.com, connectTimeout: Duration(seconds: 10), receiveTimeout: Duration(seconds: 10), ), ); _dio.interceptors.add( InterceptorsWrapper( onRequest: (options, handler) { // 这里可以统一加token或公共参数 handler.next(options); }, onError: (error, handler) { // 统一错误处理比如网络异常时弹提示 handler.next(error); }, ), ); } static final ApiClient instance ApiClient._internal(); late final Dio _dio; FutureListArticle fetchArticles() async { final response await _dio.get(/articles); final data response.data as List; return data .map((item) Article.fromJson(item as MapString, dynamic)) .toList(); } }这段代码里有两个关键点。第一Dio实例放在单例里避免每次创建都重复执行BaseOptions初始化。第二拦截器里的handler.next必须调用否则请求会卡在你这里。训练营里有人复制网上代码漏了handler.next结果所有请求都发不出去排查半天。超时时间的设定也要结合实际情况。如果接口偶尔慢connectTimeout和receiveTimeout都设10秒比较稳妥。有些页面拉数据时要显示骨架屏超时时间可以适当放宽但不要让用户无限等下去。网络错误类型的区分也很重要DioException里有connectionTimeout、connectionError、badResponse、cancel几种你用它们做分场景提示效果比笼统的“网络错误”好得多。3.4 没有后端怎么办本地Mock和在线接口训练营里有一个学习节奏问题部分学员不会写后端联调环境也没架好这时候就要用到Mock方案。最轻量的做法是直接在项目里放一份JSON文件用Dart的rootBundle加载模拟接口返回。等到正式后端就绪后只要把调用ApiClient.fetchArticles()替换成从网络获取页面代码完全不用改。经验提前把数据层抽象出来开发期用Mock联调期切换真实接口这个习惯可以省掉大量等待时间。另一个做法是用公开的测试接口比如一些在线的JSONPlaceholder服务。这个适合验证网络层代码通不通但我不建议过度依赖因为测试接口的字段结构跟你真实业务差别太大你练完还得重新改Model。最好的路径是先Mock后联调最后真机验证。Mock阶段把页面和状态管理写明白联调阶段专注网络层调通真机阶段专注性能和兼容性问题每个阶段目标清晰效率自然高。4. 页面渲染从FutureBuilder到Provider4.1 FutureBuilder快速出效果拿到数据后怎么渲染到页面上最简单的方案是FutureBuilder。你把Future传给FutureBuilder它会根据Future的状态自动帮你重建Widget。第一次接触Flutter的同学会觉得这个做法非常神奇但你要理解背后的原理FutureBuilder是一个状态监听器当Future完成时它会触发重建builder里根据snapshot.connectionState和snapshot.hasError来决定显示什么。FutureBuilderListArticle( future: ApiClient.instance.fetchArticles(), builder: (context, snapshot) { if (snapshot.connectionState ! ConnectionState.done) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Center(child: Text(加载失败${snapshot.error})); } final articles snapshot.data ?? []; return ListView.builder( itemCount: articles.length, itemBuilder: (context, index) ArticleListItem(article: articles[index]), ); }, )这个写法的好处是简洁适合页面只拉一次数据、不做复杂交互的场景。但它的问题也很明显Future一旦创建它的生命周期就不受Widget控制。页面重建时FutureBuilder会重新执行future参数指向的Future如果这个Future是每次构建时新建的就会造成重复请求。解决办法是把Future存到State变量里只创建一次。这个坑训练营里遇到过多次值得留意。4.2 用Provider管理加载状态和刷新项目一旦涉及“下拉刷新”“加载更多”“点赞收藏”这类交互FutureBuilder就会变得吃力。因为你不仅要管理初始加载状态还要管理刷新状态、错误状态、数据更新状态。这时候就该上状态管理了。Flutter社区里状态管理方案很多但训练营我特意选了Provider理由很简单它基于InheritedWidget概念容易理解代码量少而且和ChangeNotifier配合非常自然。用Provider的思路是把业务数据放到一个ChangeNotifier的Store里页面通过context.watch或context.read去读取和触发更新。网络请求不再直接放在Widget的build里而是封装成Store的方法。这样做的好处是数据和UI解耦页面只负责展示业务逻辑在Store里集中管理。class ArticleStore extends ChangeNotifier { ListArticle _articles []; bool _loading false; bool _hasMore true; int _page 1; ListArticle get articles _articles; bool get loading _loading; bool get hasMore _hasMore; Futurevoid refresh() async { _page 1; _loading true; notifyListeners(); try { _articles await ApiClient.instance.fetchArticles(page: _page); _hasMore _articles.length _pageSize; } catch (error) { // 记录错误可以在UI层用SnackBar提示 } finally { _loading false; notifyListeners(); } } Futurevoid loadMore() async { if (_loading || !_hasMore) return; _page; final more await ApiClient.instance.fetchArticles(page: _page); _articles.addAll(more); _hasMore more.length _pageSize; notifyListeners(); } }Store里我加了分页的字段_page和_hasMore因为训练营任务里要求“加载更多”。这里要注意一点_loading这个字段承担了两种职责既是初始加载也是加载更多。如果在loadMore里也设置_loadingtrueUI层就会显示全屏loading体验很差。正确做法是单独用一个_loadMore字段或者用枚举区分初始加载和分页加载。训练营里有学员在这个上面踩了坑一开始加载更多时列表内容被Loading遮掉非常影响体验。你可以在页面里根据_loading和_loadMore两个状态分别处理。4.3 组件通信刷新按钮怎么触发StoreProvider模式下组件通信的核心就变成了“读取数据和触发事件”。比如页面顶部放一个刷新按钮点击后要触发Store的刷新逻辑。这时候你用context.read ()拿到的就是同一个Store实例只要上层用ChangeNotifierProvider包过。注意这里用read而不是watch因为按钮点击事件不需要引起按钮本身重建用read只在事件触发时取一次Store实例即可。IconButton( icon: const Icon(Icons.refresh), onPressed: () { context.readArticleStore().refresh(); }, )如果你在这个页面的另一个组件里需要根据加载状态显示进度条那就用context.watch ()当Store执行notifyListeners()时这个组件会重建并读取最新状态。可以简单总结出规律展示数据用watch触发动作用read。这个习惯养成之后你写组件通信会非常顺手不会因为误用了watch导致整个页面莫名其妙地频繁重绘。还有一个小场景训练营里有学员问列表项里要显示“加载失败”的提示这个提示是放在列表内部还是单独组件里我的建议是组件化。把加载失败状态单独抽成一个Widget里面放一个“重试”按钮点击后触发store.refresh()。这样页面主体代码干净错误态和正常态互不干扰。将来你想替换“重试”为自动重试也只要改一个组件的地方。4.4 列表渲染的三个优化细节ListView.builder不是ListView这个几乎成了Flutter入门必考题。builder是按需构建只渲染屏幕可见区域的子项长列表性能完全够用。如果你误用了ListView(children: ...)一次性把所有item都构建出来列表一长就会卡顿在低端OpenHarmony开发板上尤其明显。训练营里我会让学员查看列表在100条数据时的帧率表现用builder和不用builder差异巨大。图片加载也要懒加载。文章列表里的封面图如果直接使用Image.network会在ListView构建时立即发起请求并且没有缓存控制滑回来还会重复加载。推荐用cached_network_image这个库它自带缓存策略图片不会反复请求。不过要提醒一点在OpenHarmony上引入cached_network_image时注意它的原生依赖是否支持当前设备架构如果遇到编译失败可以用Image.network加上简单的内存缓存方案但使用体验会差一些。另一个细节是item的key。列表项要尽量加上稳定的key比如ValueKey(article.id)这样Flutter在做列表diff时能准确识别哪些item是新增、哪些是复用、哪些需要删除。不加key会导致状态错乱比如你滑到第50条数据刷新后页面可能跳回顶部。训练营里有学员遇到过这个问题加了id作为key之后就正常了。5. 常见问题与性能优化实录5.1 问题速查表训练营这几期跑下来你可能会遇到的问题我整理成一张表按命中率排了个序现象常见原因解决办法请求发不出去报网络连接失败没在module.json5声明INTERNET权限配置requestPermissions返回数据中文乱码响应编码解析不对服务器返回非UTF-8在dio配置responseType和编码转换构建hap包时报找不到模块ohos目录未用DevEco Studio同步用DevEco打开工程执行Sync使用Provider时报ProviderNotFoundException上层没有用ChangeNotifierProvider包裹检查根部MultiProvider是否包含ArticleStore页面下拉刷新时列表跳到顶部item没有稳定key给ListView.builder的item加ValueKey真机跑起来很卡帧率低用了非builder列表、图片未缓存换成ListView.builder cached_network_imageFlutter新建项目后跑不起来Flutter版本与OpenHarmony适配分支不匹配检查适配仓库指定的Flutter版本并切换这张表背后你会发现大多数问题都有一个共性对Flutter在OpenHarmony上的特殊约束不了解。你按纯Android/iOS的方式处理一定会遇到边界情况。建议把这张表存在项目README里团队新人进来先看一遍能省很多互相问问题的时间。5.2 Impeller渲染引擎的兼容性问题Flutter从某个版本开始默认启用Impeller渲染引擎目的是替换旧的Skia渲染管线减少Shader编译卡顿。Impeller在iOS上的表现很好在Android部分设备上还有一些兼容性问题在OpenHarmony上同样要谨慎看待。训练营里用的是香橙派5 Pro之类的开发板GPU驱动和移动设备不完全一样Impeller的兼容性不能一概而论。如果遇到页面渲染异常、白屏、文字模糊这类现象可以在创建Flutter引擎时关闭Impeller回退到Skia渲染管线。具体做法是给Flutter引擎配置传入--no-enable-impeller参数或者在工程配置文件里关闭对应开关。当然这不是让你无脑关闭而是先在你的目标设备上做一次对比测试记录帧率和稳定性再决定用哪套管线。毕竟软件渲染的开销在某些场景下可能低于GPU环境不完善的设备上的Impeller表现。训练营里有一台老款开发板用Impeller跑列表页面上滑时会出现掉帧关了之后反而流畅了。选型这事永远以你的目标设备实测为准。5.3 数据缓存和弱网优化资讯类应用对弱网环境的要求比较高。你不可能每次都等接口超时再给用户报错在弱网下合理的做法是先展示缓存数据再后台刷新。Flutter里的缓存方案可以简单用shared_preferences存JSON字符串也可以上数据库。训练营项目的数据量不算大我用shared_preferences就够了。思路是Store初始化时先读本地缓存有缓存就先渲染然后再发起网络请求请求成功之后更新缓存和页面。Futurevoid loadWithCache() async { final prefs await SharedPreferences.getInstance(); final cache prefs.getString(articles_cache); if (cache ! null) { final list (jsonDecode(cache) as List) .map((e) Article.fromJson(e as MapString, dynamic)) .toList(); _articles list; notifyListeners(); } await refresh(); }refresh成功之后把最新的文章列表转成JSON字符串再写回prefs。这里有个技巧缓存写回时用jsonEncode而不是自己拼字符串避免转义错误。弱网提示也很重要在请求失败时如果你有缓存数据就不应该让页面显示全屏错误而是显示缓存内容加一条SnackBar提示“网络异常展示缓存数据”。这个交互比直接拦腰截断体验好太多。5.4 分页加载的防重复请求训练营任务里的“加载更多”功能看着简单实际隐藏了一个并发问题用户快速上滑时loadMore可能被连续触发导致同一页请求发送多次。防重复请求的思路是在Store里加一个_isLoadingMore标志请求未完成时直接return。这个比用hasMore判断更可靠因为hasMore是业务数据跟请求状态无关。Futurevoid loadMore() async { if (_loading || _loadingMore || !_hasMore) return; _loadingMore true; notifyListeners(); try { final more await ApiClient.instance.fetchArticles(page: _page 1); _articles.addAll(more); _hasMore more.length _pageSize; _page; } finally { _loadingMore false; notifyListeners(); } }同时你要在UI层给ListView.builder的footer加一个“已经到底了”的状态。很多新手会忘了这个细节结果列表滑到底部时没有任何提示用户以为加载失败了。把_hasMore和_loadingMore组合起来footer显示三种状态加载中、没有更多了、上滑加载更多。6. 训练营之外落地到真实项目还要注意什么6.1 分层设计不要把所有逻辑塞进页面训练营项目规模不大你可以用FutureBuilder快速完成任务。但真实项目一旦涉及多个页面、多个接口、复杂交互页面里塞满逻辑就会失控。我建议你在训练营阶段就养成一个习惯View层只做展示和事件转发Store层管理数据和状态API层管理网络请求Model层管理数据结构。四层各司其职后面扩展功能时非常舒服。拿资讯流页面举例页面里只有一个ArticleListView和ArticleStoreArticleStore负责调用ApiClient的fetchArticles方法ApiClient负责实际的Dio请求和拦截器处理Article这个Model只负责JSON解析和字段定义。以后要增加一个“收藏列表”页面你只需要新增一个FavoriteStore复用ApiClient和Article模型View层新写一个列表基本就是CtrlC和CtrlV的事。6.2 关于集成方式Flutter模块与鸿蒙主工程怎么配合这里顺带提一下flutter aar的热搜词。在OpenHarmony上Flutter工程不直接“变成”一个hap全部跑完很多时候你是在一个已有的鸿蒙应用里嵌入Flutter模块用Flutter渲染部分页面业务主体还是ArkTS。这时候你需要把Flutter模块打包成aar或har集成进鸿蒙主工程。这个动作在纯Flutter开发里不常用但在OpenHarmony真实落地上很常见。训练营里我们演示过一种简单方式先构建Flutter模块得到构建产物再在DevEco Studio里把产物作为依赖引入。这里要注意Flutter模块和鸿蒙主工程的版本兼容性要提前确认否则会出现运行期崩溃。我的建议是把Flutter模块当成一个独立的渲染组件看待它和鸿蒙侧通过平台通道通信业务边界要清楚别把鸿蒙能力调用直接写在Flutter UI里最好封装成统一接口方便替换。6.3 测试与兼容性验证别跳过OpenHarmony生态有个特殊的东西叫XTS认证设备要想通过OpenHarmony兼容性测试应用层面也有一些基本要求。很多Flutter开发者以为这只是硬件厂商的事其实你做的应用如果要在合规的OpenHarmony设备上跑一些基础测试和兼容性适配也要提前跑一跑。训练营里我让学员至少在本地跑通三个环节启动稳定性、列表长时间滑动无内存暴涨、网络异常时的生命周期处理。这三个环节过了拿到设备上基本不会出现大的兼容性问题。还要注意OpenHarmony设备的屏幕尺寸和Android手机差距很大开发板可能是平板尺寸也可能是横屏交互终端。你的布局如果写死尺寸到不同设备上肯定要出问题。建议用MediaQuery和Flexible来处理布局不要死写Container的width。训练营里有一半学员的页面在手机模拟器上正常拉到开发板上就溢出都是布局没做弹性适配。6.4 组件通信的最后补充前面讲了Provider里的read和watch这里再补充一个常见场景跨页面通信。比如你在Article列表页点击一篇文章进入详情页详情页里用户点击收藏这个收藏状态要同步回列表页。用Provider的话最简单的方式是把ArticleStore提升到两个页面共同的父级两个页面共享同一个Store实例。详情页修改收藏状态后调notifyListeners()列表页通过watch读取收藏状态自动更新UI。如果两个页面不在同一个父级下你可以用全局单例EventBus的方式做消息广播但EventBus用多了代码会难维护建议还是把共享状态提升到公共Provider里。训练营里我要求学员尽量用Provider解决组件通信问题而不是一上来就引入EventBus目的就是让大家意识到状态提升是更可控的方案事件广播是最后手段。到这一步你在OpenHarmony上用Flutter请求接口、渲染页面的整个链路就完整了。再往后你可以做更复杂的缓存策略、离线能力、动态化下发或者把页面迁移到鸿蒙原生和Flutter混编的架构里。我个人这几期带训练营下来最深的体会是Flutter在OpenHarmony上的潜力很大但它和原生网页开发、Android开发都不一样很多坑只能靠实测来填补。你如果现在正准备在OpenHarmony设备上跑Flutter项目先把这篇里列的环境匹配、权限配置、Provider渲染、缓存和防重复请求这几点吃透再开始写业务代码能少走很多弯路。
返回列表