[Flutter翻译]Flutter without Flutter

4,162 阅读3分钟

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

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

发布时间:2020年5月2日 - 3分钟阅读

这是一个实验。让我们在不使用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(免费版)翻译