在三月份过去的flutter engage大会中,除了重量级的flutter 2,让flutter适配的机型遍布移动,桌面,web端外,还有支持现在各大厂商踊跃推出的折叠屏的[多屏适配特性](https://devblogs.microsoft.com/surface-duo/flutter-dual-screen-foldable/)。 这特性提出的原因是是微软的员工使用flutter给自家的Surface Duo多屏设备时,无法很好的适应大屏以及多屏设备。 目前项目工程还是在测试阶段,官方中的[安装方法](http://name.com)年久失修,无法体验的所以笔者趟了坑,在此介绍大致说明多屏特性的原理和体验方式。 ## 特性现状 因为依赖Android的 [Jetpack-androidx.window:window](https://developer.android.com/jetpack/androidx/releases/window) 库,该库也还在alpha阶段,因此flutter主工程尚未接受此PR([framework PR ](https://github.com/flutter/flutter/pull/77156)、[engine PR](https://github.com/flutter/engine/pull/24756))合入flutter主干。所有目前想要体验就必须自己编译engine。 ## 支持哪些设备类型? - Cutout 刘海屏(普通设备) ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/2a58e8aa-2be5-4b51-8d7e-455d2c02a1ac.png) - Hinge多屏折叠设备(中间有物理铰链作为区分的) ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/c7becd04-dcbc-41c2-92e5-6485d719cc9b.png) - Fold单屏折叠设备 ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/d10ae3a8-6f31-4aa9-b248-4444011906ee.png) ## 如何编写适配多种屏幕的代码 对于屏幕信息描述数据结构,沿用[Jetpack-androidx.window:window](https://developer.android.com/jetpack/androidx/releases/window)库对于屏幕描述: ```dart class DisplayFeature { final Rect bounds; final DisplayFeatureType type; final DisplayFeatureState state; } ``` - `bounds` : 绘制区域的矩形大小 - `type` : 设备类型: - `hinge` 多屏折叠设备 - `Cutout`刘海屏 - `Fold`单屏折叠设备 -`state`:设备的此时的折叠姿势 - `halfOpened`折叠90度 - `flat` 摊开180 - `flipped`折叠一页,另一页不可见 - `unknown` 未知 通过使用`MediaQuery.of(context).displayFeatures`接口来获取屏幕个数。`MediaQuery.of(context).hinge`来判断当前的绘制有无中间物理铰链。 下面是最简单的一个分屏代码。通过判断是否存在铰链,走不同的绘制逻辑。 ```dart import 'package:flutter/material.dart'; import 'dart:ui' as ui; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: MyPage(), ); } } class MyPage extends StatelessWidget{ @override Widget build(BuildContext context) { List displayFeatures = MediaQuery.of(context).displayFeatures; print('displayFeatures count:${displayFeatures.length}'); final hinge = MediaQuery.of(context).hinge; if (hinge==null) { print('No hinge'); return Container(child: Text("Single Page"),); } else { print('Hinge is ${hinge.bounds.width} logical pixels wide'); return Container(child: Text('Hinge is ${hinge.bounds.width} logical pixels wide'),); } } } ``` 当应用只在单个屏幕显示时,`MediaQuery.of(context).displayFeatures` 数组大小为0。 `final hinge = MediaQuery.of(context).hinge;`因为没跨屏,为`null` ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/dd28473d-7e69-48f6-a126-ee0590b09201.png) 当用户把应用拖拽到跨屏幕时,`MediaQuery.of(context).displayFeatures` 数组大小为1。 `final hinge = MediaQuery.of(context).hinge;`不为空。 ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/1121afab-e6c7-4764-8846-b24845dd066b.gif) 自此就可以根据不同的设备、屏幕的个数来定制不同情况的界面。 ## 如何编译 需要体验这个功能,还需要准备flutter engine的编译环境,这里不展开了。[https://github.com/flutter/flutter/wiki/Setting-up-the-Engine-development-environment](https://github.com/flutter/flutter/wiki/Setting-up-the-Engine-development-environment) 环境搭建好了后 #### 1.需要重新修改.gclient文件,将engine版本指向微软开发的折叠特性分支中 ```bash solutions = [ { "managed": False, "name": "src/flutter", "url": "https://github.com/andreidiaconu/engine.git@foldable_support", "deps_file": "DEPS", "safesync_url": "", }, ] target_os = ['ios','android'] ``` #### 2.在engine目录执行 `gclient sync ` 这一步是下载对应的依赖库 #### 3.在src/flutter/tools/cipd/android_embedding_bundle 下执行gradle updateDependencies 完成后 将src/flutter/tools/cipd/android_embedding_bundle/lib/window-java-1.0.0-alpha08.jar 移动到 src/tools/cipd/android_embedding_bundle/ *为什么要手动拷贝?* 因为添加了androidx.window:window-java依赖。按正常逻辑,需要将Android的依赖jar包上传到cipd服务器上,但是只有google员工有上传权限,所以这里只能直接放在在src/tools/cipd/android_embedding_bundle/以直接提供给gn编译。 新增的android依赖库 src/flutter/tools/cipd/android_embedding_bundle/build.gradle ``` android { compileSdkVersion 30 dependencies { ... embedding "androidx.window:window-java:1.0.0-alpha08" } } ``` #### 4.运行编译 ``` ./flutter/tools/gn --android --android-cpu x64 --unoptimized ./flutter/tools/gn --unoptimized ./flutter/tools/gn --android --unoptimized ninja -C out/host_debug_unopt ninja -C out/android_debug_unopt_x64 ninja -C out/android_debug_unopt ``` #### 5 运行本地engine环境配置: engine编译好后,需要把AndroidStudio以及工程中使用flutter指向本地的engine。 ##### 5.1 添加参数指向本地engine --local-engine-src-path /your/engine/src --local-engine android_debug_unopt_x64 ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/1b6efdff-6d5a-4684-a824-dcde2ed096a7.png) ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/c4949e98-2aa2-4e3a-9857-e0214c59debe.png) ##### 5.2 将AndroidStudio的flutter工程指向 https://github.com/andreidiaconu/flutter.git 这一步需要将库clone下来,并切换到对应分支 ``` git clone https://github.com/andreidiaconu/flutter.git git checkout -b foldable_support origin foldable_support ``` ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/e92cb3d7-c9f6-4ad7-835b-ec162e3d1b20.png) ##### 5.3 pubspec.yaml中将系统库sky_engine和sky_services都指向本地 pubspec.yaml ``` dependency_overrides: sky_engine: path: /Users/xx/Documents/code/engine/src/out/host_debug_unopt/gen/dart-pkg/sky_engine sky_services: path: /Users/xx/Documents/code/engine/src/out/host_debug_unopt/gen/dart-pkg/sky_services ``` ##### 5.4 下载Surface Duo模拟器 * [下载模拟器](https://docs.microsoft.com/zh-cn/dual-screen/android/emulator/surface-duo-download?tabs=windows) * 解压到任意文件夹 * 运行SurfaceDuoEmulator_2021.630.4目录中的 .run.sh 即可运行 6. 最后运行工程即可 ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/82b6f5bf-bf72-4d48-9ef0-c0d22f5778aa.png) ## 存在的问题以及讨论 * 接口不稳定 这套多屏特性是基于AndroidX的[Jetpack-androidx.window:window](https://developer.android.com/jetpack/androidx/releases/window)库进行开发,而这个库还在不停的迭代中,接口可能有变化,所有flutter官方未采纳这个PR合入到主干 * 未支持iOS 虽然iOS尚未有多屏设备,但是这个库中有也有针对大屏做的分屏特性,对于iPad开发有非常好的应用场景。 ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/1321b76a-de2a-4971-b76f-77592a34df87.png) * 用户量少,存在bug可能较多 目前因为使用的用户量较少,测试的场景未得到充分的测试,可能会存在较多的bug。 * 对应多屏设备或者大屏设备开发友好 通过试例代码可以知道,多屏的逻辑可以在同一份代码中编写,不同屏幕的绘制代码在同一份文件里面。数据的交互非常友好。 ## 最后 和微软这位作者的邮件聊天中得知,他现在非常欢迎大家使用本特性,在使用的过程有各种不爽或者bug都非常欢迎提出意见。 引用: > 总介绍 > https://devblogs.microsoft.com/surface-duo/flutter-dual-screen-foldable/ > Surface Duo 安卓模拟器 > https://docs.microsoft.com/zh-cn/dual-screen/android/emulator/?WT.mc_id=docs-surfaceduoblog-andreidiaconu > Demo > https://github.com/microsoft/flutter-dualscreen > framework PR > https://github.com/flutter/flutter/pull/77156 > engine PR > https://github.com/flutter/engine/pull/24756 > Android折叠屏相关api > https://developer.android.com/codelabs/android-window-manager-dual-screen-foldables?hl=zh-cn#1 > 折叠屏状态 > https://developer.android.com/guide/topics/ui/foldables#postures > androidx.window版本信息 > https://developer.android.com/jetpack/androidx/releases/window