[Flutter翻譯]Flutter without Flutter

Sunbreak發表於2020-07-20

原文地址:medium.com/icnh/flutte…

原文作者:medium.com/@eibaan_546…

釋出時間:2020年5月2日 - 3分鐘閱讀

這是一個實驗。讓我們在不使用Flutter框架的情況下,建立在移動裝置上顯示一些東西所需的最小程式碼。

[Flutter翻譯]Flutter without Flutter

不美不美,自成一派

Flutter是一個高階GUI框架,它使用dart:uiSkia引擎的一個抽象)來實際顯示一些東西,並與這些東西所顯示的平臺進行互動。當然,我們自己也可以直接使用這個低階別的dart:ui庫。

設定

讓我們建立一個新的Flutter專案。

$ flutter create flutter_without_flutter
複製程式碼

然後用這段程式碼替換lib/main.dart

import ‘dart:ui’;
void main() {
 window.onBeginFrame = beginFrame;
 window.scheduleFrame();
}
void beginFrame(Duration duration) {
}
複製程式碼

解釋:我們用window單例註冊一個名為beginFrame的全域性函式,然後要求圖形引擎對該函式進行回撥。它應該準備並最終繪製一個幀,也就是在裝置螢幕上顯示一些東西。

繪製一些東西

因為我習慣用邏輯單位而不是物理畫素來工作,所以 beginFrame 的第一步是將裝置的物理螢幕尺寸轉換為更熟悉的數值。

void beginFame(Duration duration) {
 final pixelRatio = window.devicePixelRatio;
 final size = window.physicalSize / pixelRatio;
 final physicalBounds = Offset.zero & size * pixelRatio;
 …
複製程式碼

下一步是設定一個使用邏輯單元的Canvas

final recorder = PictureRecorder();
 final canvas = Canvas(recorder, physicalBounds);
 canvas.scale(pixelRatio, pixelRatio);
 …
複製程式碼

然後讓我們用紅色的顏料畫一個圓圈。

final paint = Paint()..color = Color(0xFFF44336);
 final center = size.center(Offset.zero);
 canvas.drawCircle(center, size.shortestSide / 4, paint);
 …
複製程式碼

最後一步是將呼叫Canvas方法建立的錄音,用它來構建一個所謂的場景,然後由windows單例渲染。

final picture = recorder.endRecording();
 final sceneBuilder = SceneBuilder()
 ..pushClipRect(physicalBounds)
 ..addPicture(Offset.zero, picture)
 ..pop();
 window.render(sceneBuilder.build());
}
複製程式碼

順便說一下,通常的Flutter專案的即時熱程式碼過載仍然有效,這就是為什麼它可能會有用,使用這種設定來建立低水平的圖形應用程式,例如2D遊戲。

移動起來

為了移動圓圈並使其在螢幕上彈跳,我們將把它的中心點儲存在一個全域性變數(稱為center)中,把它的速度儲存在另一個全域性變數(稱為velocity)中,每次呼叫beginFrame時修改這些變數,然後在beginFrame函式結束時使用scheduleFrame請求另一次繪製操作。

下面是相關的修改。

Offset center;
Offset velocity;
void beginFrame(Duration duration) {
 …
 canvas.scale(pixelRatio, pixelRatio);
final radius = size.shortestSide / 4;
 if (center == null) {
 center = size.center(Offset.zero);
 velocity = Offset(3, 5);
 } else {
 if (center.dx < radius || center.dx > size.width — radius)
 velocity = velocity.scale(-1, 1);
 if (center.dy < radius || center.dy > size.height — radius)
 velocity = velocity.scale(1, -1);
 center += velocity;
 }
final paint = Paint()..color = Color(0xFFF44336);
 canvas.drawCircle(center, radius, paint);
…
window.scheduleFrame();
}
複製程式碼

因為我們無法控制beginFrame函式的排程頻率,所以我們可能應該利用傳遞的duration,並將圓的速度與傳遞的時間繫結。為了計算呼叫之間所經過的時間,我們引入另一個全域性變數,叫做lastDuration

Duration lastDuration;
void beginFrame(Duration duration) {
 …
 if (center == null) {
 …
 } else {
 …
 final delta = (duration — lastDuration).inMilliseconds / 1000;
 center += velocity * delta;
 }
 lastDuration = duration;
…
複製程式碼

如果你保持應用程式的執行,只是修改了soure程式碼,圓圈應該已經開始移動,就像魔法一樣。

對觸控的反應

最後但同樣重要的是,讓我們在每次點選螢幕時改變速度。為了檢測觸控,我們需要新增另一個回撥函式onPointerDataPacket。這個函式接收一個PointerData物件的列表,這些物件描述了觸控向下、觸控移動和觸控向上的事件。不過我們只對 PointerChange.up 型別的事件感興趣。

這裡是新的主函式。

void main() {
 window.onBeginFrame = beginFrame;
 window.onPointerDataPacket = pointerDataPacket;
 window.scheduleFrame();
}
複製程式碼

這裡是新的回撥函式。

void pointerDataPacket(PointerDataPacket packet) {
 for (final data in packet.data) {
 if (data.change == PointerChange.up) {
 velocity = Offset.fromDirection(
 _random.nextDouble() * pi * 2,
 _random.nextDouble() * 800400,
 );
 }
 }
}
final _random = Random();
複製程式碼

利用這個基礎,你應該可以從頭開始建立自己的類似Flutter的GUI框架。

我將此作為一個練習留給讀者:-)


通過www.DeepL.com/Translator(免費版)翻譯

相關文章