A low-cost Flutter screen adaptation solution(一个极低成本的 Flutter 屏幕适配方案)
一 概述
由于 Flutter 的应用场景很多,不只是 android 还有 ios 以及 web,现在的手机品牌和型号越来越多,导致我们平时写布局的时候会在个不同的移动设备上显示的效果不同,今天介绍一种方案,可以是一个低成本,但是 100% 还原UI的一种办法,无需使用工具类或者是扩展函数去 转换,直接写 UI设计图给的大小即可
flutter_autosize_screen 欢迎 star 以及 提出建议
三 先看效果
IOS
iPhone 8 ------------------------------- iPhone 11 :
iPhone 12 pro max --------------------- ipad air :
android 图
768x1280-320dpi----------10801920-420dpi----------1440x2560-560dpi
web 图
随着拉长屏幕 ,慢慢的 宽度会大于高度,当大于的时候 ,会以 高度 为 基准。
三 使用
3.1 引用
flutter_autosize_screen: ^{LAST_VERSION}
3.2 初始化
- 在main方法的第一行就初始化,下面的基准一般以宽度为基准,直接写Ui设计图的宽度尺寸,如果是横屏的状态的 下面的 360 就是以高度为基准
void main() {
// 设置基准
AutoSizeUtil.setStandard(360);
// 使用 runAutoApp 来代替 runApp
// import 'package:flutter_autosize_screen/binding.dart';
runAutoApp(const MyApp());
}
- 替换根 MaterialApp 的 MediaQuery
MaterialApp(
title: 'Autosize Demo',
/// 替换,这样可以在以后 使用 MediaQuery.of(context) 得到 正确的Size
builder: AutoSizeUtil.appBuilder,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: HomePage(),
),
)
- 获取Size
AutoSizeUtil.getScreenSize()
//或者
MediaQuery.of(context).size
- 直接按照设计图的尺寸写即可
Container(
alignment: Alignment.center,
height: 60,
width :60,
color: Colors.redAccent,
child: Text(
"直接按照设计图写尺寸",
),
)
- 切记
不能使用 window 获取 size 或者是 获取 MediaQuery
window.physicalSize
MediaQueryData.fromWindow(window)
四 原理
4.1 Flutter 入口 runApp(Widget app)
当我们调用 runApp 的时候,会做三件事请 1.实例化WidgetsFlutterBinding类,2.创建组件树attachRootWidget(app),3.启动预热帧scheduleWarnUpFrame()。
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
4.2 WidgetsFlutterBinding
正如此类的名字一样, WidgetsFlutterBinding正是绑定widget 框架和Flutter engine的桥梁,WidgetsFlutterBinding继承自BindingBase 并混入了很多Binding
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance!;
}
}
4.2.1 我们看看各个混入的 Binding的作用
- GestureBinding:提供了window.onPointerDataPacket 回调,绑定Framework手势子系统,是Framework事件模型与底层事件的绑定入口。
- ServicesBinding:提供了window.onPlatformMessage 回调, 用于绑定平台消息通道(message channel),主要处理原生和Flutter通信。
- SchedulerBinding:提供了window.onBeginFrame和window.onDrawFrame回调,监听刷新事件,绑定Framework绘制调度子系统。
- PaintingBinding:绑定绘制库,主要用于处理图片缓存。
- SemanticsBinding:语义化层与Flutter engine的桥梁,主要是辅助功能的底层支持。
- RendererBinding: 提供了window.onMetricsChanged 、window.onTextScaleFactorChanged 等回调。它是渲染树与Flutter engine的桥梁。
- WidgetsBinding:提供了window.onLocaleChanged、onBuildScheduled 等回调。它是Flutter widget层与engine的桥梁。
4.3 重点是 RendererBinding
初始化了 第一个 RenderView 。这个RenderView就是渲染树(render tree)的根节点,其次 是 渲染屏幕,里面有个重要的方法 createViewConfiguration,看 源码上面的注释 Bindings 可以重写这个方法来更改大小或设备像素,所以我们可以从这个上面入手
void initRenderView() {
assert(!_debugIsRenderViewInitialized);
assert(() {
_debugIsRenderViewInitialized = true;
return true;
}());
renderView = RenderView(configuration: createViewConfiguration(), window: window);
renderView.prepareInitialFrame();
}
/// Bindings can override this method to change what size or device pixel
/// ratio the [RenderView] will use. For example, the testing framework uses
/// this to force the display into 800x600 when a test is run on the device
/// using `flutter run`.
ViewConfiguration createViewConfiguration() {
final double devicePixelRatio = window.devicePixelRatio;
return ViewConfiguration(
size: window.physicalSize / devicePixelRatio,
devicePixelRatio: devicePixelRatio,
);
}
4.4 写个类 AutoWidgetsFlutterBinding 继承 WidgetsFlutterBinding
重写 createViewConfiguration 的方法,更改 devicePixelRatio 以及 屏幕的Size,如下,因为调整了 devicePixelRatio,所以对于 Event 事件,需要额外对事件的坐标进行对应比例的转换,这个就看源码就可以了
class AutoWidgetsFlutterBinding extends WidgetsFlutterBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null) AutoWidgetsFlutterBinding();
return WidgetsBinding.instance!;
}
@override
ViewConfiguration createViewConfiguration() {
return ViewConfiguration(
size: AutoSizeUtil.getSize(),
devicePixelRatio: AutoSizeUtil.getDevicePixelRatio(),
);
}
五 感谢
|