首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

  • 24-11-26 09:06
  • 2882
  • 77021
juejin.cn

2024 了, Flutter 终于"醒悟",开始规划 Widget Previews#159342 ,在 Jetpack Compose 和 SwiftUI 都支持 IDE Preview 的情况下,一直以来 Flutter 缺乏预览能力是被吐槽最多的问题之一。

目前只是进入规划阶段,还没正式落地,但是可以作为基础架构参考。

在当前设计上,预期通过在函数上代入 @Preview 注解来开启 Widget 预览,被预览的 Widget 是直接在 Flutter 应用中被渲染,所以一般情况下它们是完全交互式的,可用于预览 UI 布局和动画。

dart
代码解读
复制代码
@Preview() List myFirstPreview() { return [ WidgetPreview( name: 'Full App Preview', height: 700, device: Devices.ios.all.first, child: GalleryApp(), ), ];

在这点上看和 Compose 的 Preview 很类似:

按照目前架构文档上的描述,整个预览存在以下几个关键节点:

  • Widget Preview:在预览环境中显示 Widget,用于开发工作流程
  • Preview Scaffold:用于生成 Flutter 应用,显示项目中定义的 widget 预览
  • Preview Environment: 托管 Preview Scaffold 的原生 Flutter 桌面应用
  • Preview Viewer:一个 Flutter Web 应用,可将帧从 Preview Environment 流式传输到 IDE ,并将用户交互流式传输到 Preview Environment

从这点看,可以理解为预览其实是通过 Flutter Web + Flutter PC 来实现。

而实现后的大致效果就是,当客户端连接到 Preview Environment 的 Web 服务器时,服务器会立即注册一个持久性帧回调,该回调负责在渲染时捕获每个帧,然后捕获的帧将通过 web socket 连接转发到客户端:

dart
代码解读
复制代码
/// Sends the current frame to the preview viewer for rendering. Future<void> sendFrame() async { if (_sendingFrame) { return; } _sendingFrame = true; final RenderView renderView = WidgetsBinding.instance.rootElement!.renderObject! as RenderView; final OffsetLayer layer = renderView.debugLayer! as OffsetLayer; final ui.Image image = await layer.toImage( Offset.zero & (renderView.size * renderView.flutterView.devicePixelRatio), ); final Uint8List data = (await image.toByteData())!.buffer.asUint8List(); image.dispose(); _client.sendFrame(frame: data); _sendingFrame = false; }

通过对应容器,Flutter 开发者就可以在对应 IDE 中的预览环境对他们的 Widget 进行预览和交互, Preview 支持缩放或者平移,从而让开发者可以在像素级别检查 UI,同时开发人员还可以使用 package:device_frame 将他们的 widget 包装在一个设备框架中,该框架可以使用设备的显示特性来呈现 widget。

Flutter tool 同时新增了 flutter widget-preview 命令,该命令负责为项目生成 preview scaffold 并与preview environment 交互,并负责为给定项目创建 preview scaffold 并管理 preview environment 。

正常来说,首次在用户设备上为项目运行命令时,工具将执行以下任务:

  • 在 .dart_tool 目录下创建一个新的 Flutter 项目(当前名为 preview_scaffold),它目前被配置为 Flutter 桌面 App

  • 使用 preview_scaffold entrypoint 覆盖 lib/main.dart ,该入口能够托管开发人员项目中的 widget previews ,文件会导入 lib/generated_preview.dart,它将包含一个函数,函数用于返回项目的 widget 预览集

  • 初始化 preview_scaffold 的 pubspec.yaml,在开发者的项目中添加路径依赖,并列出开发者项目中的资源,此步骤还处理导入 package:flutter_gen 以获得本地化支持,当然如果宏编程正式发布,就不需要package:flutter_gen 了。

  • 使用 package:analyzer,在返回 List 的 Top 函数上搜索 @Preview() 注释的实例,并记录预览函数名称以及它们的库

  • 使用开发人员项目中的预览搜索结果生成 lib/generated_preview.dart , package:code_builder 用于生成代码,该代码导入并调用每个 widget 预览函数用于返回预览列表,例如:

dart
代码解读
复制代码
// ignore_for_file: no_leading_underscores_for_library_prefixes import 'package:gallery/main.dart' as _i1; import 'package:gallery/demos/material/list_demo.dart' as _i2; import 'package:flutter/widget_preview.dart'; List previews() => [ ..._i1.preview(), ..._i2.preview(), ];

Flutter Tool 最终会在开发者的项目目录上初始化一个文件观察器,以检测源代码的更改,analyzer API 会检测更新文件中添加或删除 Widget 预览定义,并在必要时重新生成 lib/generated_preview.dart,同时 Flutter Tool 将使用 Flutter Tool 守护进程协议与预览环境通信,从而触发热重载,更新预览环境。

IDE 插件将负责使用 flutter widget-preview 命令启动活动项目的预览环境,为了在 IDE 中显示 Preview Environment 的内容,Preview Environment 需要将帧和交互事件流式传输到 Web 的应用(VSCode 仅支持嵌入基于 Web 的工具),这个 Web 应用将是一个简约的 Flutter Web 应用,它渲染预览环境发送的帧,并使用现有的 Widget 如 KeyboardListener 和 Listener 捕获和转发用户交互(例如光标移动、点击、滚动和按键),流式处理将通过 websocket 连接完成,使用 JSON RPC 协议将用户交互传达到 Preview Environment 。

目前,@Preview 注解类只是一个标记,表示以下函数应该由预览环境导入和显示,将来这个注解应该会增加比如指定应应用于某些预览内容设置,如语言区域、主题详情的属性,或以编程方式生成多个预览,例如类似 Compose:

目前 WidgetPreview 类是一个 wrapper ,用于初始化各种状态和属性,允许在 Widget 预览环境中呈现 Widget:

dart
代码解读
复制代码
class WidgetPreview extends StatefulWidget { const WidgetPreview({ super.key, required this.child, this.name, this.width, this.height, this.device, this.orientation, this.textScaleFactor, this.platformBrightness, }); final String? name; /// The [Widget] to be rendered in the preview. final Widget child; /// Artificial constraints to be applied to the [child]. final double? width; final double? height; /// An optional device configuration. final DeviceInfo? device; /// The orientation of [device]. final Orientation? orientation; /// Applies font scaling to text within the [child]. final double? textScaleFactor; /// Light or dark mode (defaults to platform theme). final Brightness? platformBrightness; @override State createState() => _WidgetPreviewState(); }

目前接口允许开发人员指定如下属性:

  • 要在预览环境中与预 preview 一起显示的描述
  • 预览的高度和宽度,将覆盖 MediaQuery 返回的大小,从而允许预览自适应 UI
  • package:device_frame 中的设备,它在 device_frame 中渲染预览的 Widget,并应用了正确的显示属性,还可以通过方向来指定设备最初应以横向模式还是纵向模式显示
  • 用于调整默认字体缩放行为的文本缩放
  • 用于控制主题选择的平台亮度(例如浅色与深色模式)

而对于交互协议,目前允许将以下交互转发到预览环境:

  • Pointer location
  • Hover location
  • Tap up / down
  • Scrolling with a mouse wheel
  • Scrolling with a trackpad
  • Keypress events (down, up, repeated)
  • Window size changes

最后,可以看到目前整个预览的基础架构还比较粗糙,另外类似 device_frame 这种第三方包直接在 Flutter 引用是否合适也存在一些讨论,还有 native assets 等支持等,可以预见 preview 的落地难度还是有的,但是踏出这一步后,相信离最后的落地就不远了。

参考连接:github.com/flutter/flu…

注:本文转载自juejin.cn的恋猫de小郭的文章"https://juejin.cn/post/7441006286765064218"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

140
Android
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top