A low-cost Flutter screen adaptation solution(一個極低成本的 Flutter 螢幕適配方案)
一 概述
由於 Flutter 的應用場景很多,不只是 android 還有 ios 以及 web,現在的手機品牌和型號越來越多,導致我們平時寫佈局的時候會在個不同的移動裝置上顯示的效果不同,今天介紹一種方案,可以是一個低成本,但是 100% 還原UI的一種辦法,無需使用工具類或者是擴充套件函式去 轉換,直接寫 UI設計圖給的大小即可
二 github 歡迎 star
flutter_autosize_screen 歡迎 star 以及 提出建議
三 先看效果 , 設定的標準寬度是 360
掘金圖片排版有問題,請看github
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(),
);
}
複製程式碼