所在的位置: ios >> ios优势 >> 用Flutter和Firebase轻松构

用Flutter和Firebase轻松构

作者/VeryGoodVenturesTeam

我们(VeryGoodVentures团队)与Google合作,在今年的GoogleI/O大会上推出了照相亭互动体验(I/OPhotoBooth)。您可以与深受喜爱的Google吉祥物合影:Flutter的Dash、AndroidJetpack、Chrome的Dino和Firebase的Sparky,并用各种贴纸装饰照片,包括派对帽、披萨、时髦眼镜等。当然,您也可以通过社交媒体下载并分享,或者用作您的个人头像!

△Flutter的Dash、Firebase的Sparky、AndroidJetpack和Chrome的Dino

△Flutter的Dash、Firebase的Sparky、AndroidJetpack和Chrome的Dino

我们使用Flutterweb和Firebase构建了I/O照相亭。因为Flutter现在支持打造Web应用,我们认为这将是一个很好的方式,可以让世界各地的与会者在今年的线上GoogleI/O大会上轻松访问这一应用。Flutterweb消除了必须通过应用商店安装应用的障碍,同时用户还可以灵活选择运行应用的设备:移动设备、桌面设备或平板电脑。因此,只要能使用浏览器,用户便可无需下载直接使用I/O照相亭。

尽管I/O照相亭旨在提供Web体验,但所有代码均采用与平台无关的架构编写而成。当相机插件等原生功能的支持在各个平台就绪后,这套代码即可在所有平台(桌面、Web和移动设备)通用。

使用Flutter构建虚拟照相亭

构建Web版Flutter相机插件

第一个挑战即在Web上为Flutter构建摄像头插件。最初,我们联系了Baseflow团队,因为他们负责维护现有的开源Flutter摄像头插件。Baseflow致力于构建适用于iOS和Android的一流摄像头插件支持,我们也很乐于与其合作,使用联合插件方法为插件提供Web支持。我们尽可能符合官方插件接口,以便我们可以在准备就绪时将其合并回官方插件。

我们确定了两个对于在Flutter中构建I/O照相亭相机体验至关重要的API。

初始化摄像头:应用首先需要访问您的设备摄像头。对于桌面设备,访问的可能是网络摄像头,而对于移动设备,我们选择了访问前置摄像头。我们还提供了p的预期分辨率,以根据用户设备类型充分提高拍摄质量。拍照:我们使用了内置的HtmlElementView,该控件使用平台视图将原生Web元素渲染为Flutterwidget。在此项目中,我们将VideoElement渲染为原生HTML元素,这便是您在拍照前会在屏幕上看到的内容。我们还使用了一个CanvasElement,用于在您点击拍照按钮时从媒体流中捕获图像。

FutureCameraImagetakePicture()async{finalvideoWidth=videoElement.videoWidth;finalvideoHeight=videoElement.videoHeight;finalcanvas=html.CanvasElement(width:videoWidth,height:videoHeight,);canvas.context2D..translate(videoWidth,0)..scale(-1,1)..drawImageScaled(videoElement,0,0,videoWidth,videoHeight);finalblob=awaitcanvas.toBlob();returnCameraImage(data:html.Url.createObjectUrl(blob),width:videoWidth,height:videoHeight,);}

摄像头权限

在Web上完成Flutter摄像头插件后,我们创建了一个抽象布局,以根据相机权限显示不同的界面。例如,在等待您允许或拒绝使用浏览器摄像头时,或者如果没有可供访问的摄像头时,我们可以显示一条说明性消息。

Camera(controller:_controller,placeholder:(_)=constSizedBox(),preview:(context,preview)=PhotoboothPreview(preview:preview,onSnapPressed:_onSnapPressed,),error:(context,error)=PhotoboothError(error:error),)

在上面的抽象布局中,placeholder会在应用等待您授予摄像头权限时返回初始界面。Preview则会在您授予权限后返回真实的界面,并显示摄像头的实时视频流。结尾的Error构造语句则可以在错误发生时捕获错误并显示相应的消息。

生成镜像照片

我们的下一个挑战是生成镜像照片。如果我们照原样使用摄像头拍摄的照片,那么您看到的内容将与您在照镜子时所看到的内容不一样。某些设备会提供专门处理这一问题的设置选项,所以,如果您用前置摄像头拍照,您看到的其实是照片的镜像版本。

在我们的第一种方法中,我们尝试捕捉默认的摄像头视图,然后围绕y轴对其进行度翻转。这种方法似乎有效,但后来我们遇到了一个问题,即Flutter偶尔会覆盖这个翻转,导致视频恢复到未镜像的版本。

在Flutter团队的帮助下,我们将VideoElement放在DivElement中,并更新VideoElement以填充DivElement的宽度和高度,解决了这个问题。这样一来,我们能够为视频元素应用镜像,同时因为父元素是div,所以不会被Flutter覆盖翻转效果。如此一来,我们便获得了所需的镜像摄像头视图!

保持宽高比

在大屏幕上保持4:3宽高比,以及在小屏幕上保持3:4宽高比,这个操作起来比看起来更难!保持宽高比非常重要,既要符合Web应用的整体设计,又要确保在社交媒体上分享照片时,令其中的像素呈现出清晰的本色效果。这是一项具有挑战性的任务,因为不同设备上内置摄像头的宽高比差异很大。

为了强制保持宽高比,应用首先使用JavaScriptgetUserMediaAPI从设备摄像头请求可能的最大分辨率。随后,我们将此API传递到VideoElement流中,这便是您在摄像头视图中看到的内容(当然是已镜像的版本)。我们还应用了object-fitCSS属性来确保视频元素能盖住其父级容器。我们使用Flutter自带的AspectRatiowidget来设置宽高比。因此,摄像头不会对显示的宽高比做出任何假设;它始终返回支持的最大分辨率,然后遵守Flutter提供的约束条件(在本例中为4:3或3:4)。

finalorientation=MediaQuery.of(context).orientation;finalaspectRatio=orientation==Orientation.portrait?PhotoboothAspectRatio.portrait:PhotoboothAspectRatio.landscape;returnScaffold(body:_PhotoboothBackground(aspectRatio:aspectRatio,child:Camera(controller:_controller,placeholder:(_)=constSizedBox(),preview:(context,preview)=PhotoboothPreview(preview:preview,onSnapPressed:()=_onSnapPressed(aspectRatio:aspectRatio,),),error:(context,error)=PhotoboothError(error:error),),),);

通过拖放添加贴纸

I/O照相亭的一大重要体验在于与您最喜欢的Google吉祥物合影并添加道具。您能够在照片中拖放吉祥物和道具,以及调整大小和旋转,直到获得您喜欢的图像。您也会发现,在将吉祥物添加到屏幕上时,您可以拖动它们并调整其大小。吉祥物们还是有动画效果的——这种效果由spritesheet来实现。

for(finalcharacterinstate.characters)DraggableResizable(canTransform:character.id==state.selectedAssetId,onUpdate:(update){context.readPhotoboothBloc().add(PhotoCharacterDragged(character:character,update:update,),);},child:_AnimatedCharacter(name:character.asset.name),),

为调整对象的大小,我们创建了可拖动、可调整大小且可以容纳其他Flutterwidget的widget,在本例中,即为吉祥物和道具。该widget会使用LayoutBuilder,根据窗口的约束条件来处理widget的缩放。在内部,我们使用GestureDetector以挂接到onScaleStart、onScaleUpdate和onScaleEnd事件。这些回调提供了必要的手势详细信息,以反映用户对吉祥物和道具的操作。

通过多个GestureDetector回馈的数据,Transformwidget和4D矩阵变换即可根据用户所做的各种手势处理缩放,以及旋转吉祥物和道具。

Transform(alignment:Alignment.center,transform:Matrix4.identity()..scale(scale)..rotateZ(angle),child:_DraggablePoint(...),)

最后,我们创建了单独的package来确定您的设备是否支持触摸输入。可拖动、可调整大小的widget会根据触摸功能做出相应的调整。在具有触摸输入功能的设备上,您并不能看到调整大小的锚点和旋转图标,因为您可以通过双指张合和平移手势来直接操纵图像;而在不支持触摸输入的设备(例如您的桌面设备)上,我们则添加了锚点和旋转图标,以适应单击和拖动操作。

针对Web优化Flutter

使用Flutter针对Web进行开发

这是我们使用Flutter构建的首批纯Web项目之一,其与移动应用具有不同的特征。

我们需要确保该应用对任何设备上的任何浏览器都具有响应性和自适应性。也就是说,我们必须确保I/O照相亭可以根据浏览器大小进行缩放,并且能够处理移动设备和Web端的输入。我们通过以下几种方式做到了这一点:

响应式调整大小:用户能够随意调整浏览器的大小,并且界面能做出响应。如果您的浏览器窗口为纵向,则相机会从4:3的横向视图翻转为3:4的纵向视图。响应式设计:针对桌面浏览器,我们设计为在右侧显示Dash、AndroidJetpack、Dino和Sparky,而对于移动设备,这些要素则会显示在顶部。我们针对桌面设备,在摄像头右侧设计使用了抽屉式导航栏,而对于移动设备,则使用了BottomSheet类。自适应输入:如果您使用桌面设备访问I/O照相亭,则鼠标点击操作将被视为输入,如果您使用的是平板电脑或手机,则使用触摸输入。在调整贴纸大小并将其放置在照片中时,这一点尤其重要。移动设备支持双指张合和平移手势,桌面设备支持点击和拖动操作。

可扩展架构

我们还为此应用构建了可扩展的移动应用。我们的I/O照相亭在创建之初就具有稳固的基础,包括良好的空安全性、国际化,以及从第一次提交开始就做到的%单元和widget测试覆盖率。我们使用flutter_bloc进行状态管理,因为它支持我们轻松测试业务逻辑,并观察应用中的所有状态变化。这对于生成开发者日志和确保可追溯性特别有用,因为我们可以准确地观察到从一个状态到另一个状态的变化,并更快地隔离问题。

我们还实现了由功能驱动的单一代码库结构。例如,贴纸、分享和实时相机预览,均在各自的文件夹中得到实现,其中每个文件夹包含其各自的界面组件和业务逻辑。这些功能也会用到外部依赖,例如位于package子目录中的相机插件。利用这种架构,我们的团队能够在互不干扰的情况下并行处理多个功能,最大限度地减少合并冲突,并有效地重用代码。例如,界面组件库是名为photobooth_ui的单独package,相机插件也是单独的。

通过将组件分成独立的package,我们可以提取未与此特定项目绑定的各个组件,并将其开源。与Material和Cupertino组件库类似,我们甚至可以将界面组件库package做开源处理,以供Flutter社区使用。

Firebase+Flutter=完美组合

FirebaseAuth、存储、托管等

照相亭利用Firebase生态系统进行各种后端集成。firebase_authpackage支持用户在应用启动后立即匿名登录。每个会话都使用FirebaseAuth创建具有唯一ID的匿名用户。

当您来到共享页面时,此设置即会开始发挥作用。您可以下载照片以保存为个人头像,也可以直接将其分享到社交媒体。如果您下载照片,则该照片将存储在您的本地设备上。如果您分享照片,我们会使用firebase_storagepackage将照片存储在Firebase中,以便稍后检索并生成帖子通过社交媒体发布。

我们在Firebase的存储分区上定义了Firebase安全规则,确保照片在创建后不可变。这可以防止其他用户修改或删除存储分区中的照片。此外,我们使用GoogleCloud提供的对象生命周期管理,定义了一个删除30天前所有对象的规则,但您可以按照应用中列出的说明请求尽快删除您的照片。

此应用还使用FirebaseHosting快速安全地进行托管。我们可以借助action-hosting-deployGitHubAction,根据目标分支,将应用自动部署到FirebaseHosting。当我们将变更合并到主分支时,该操作会触发一个工作流,用于构建应用的特定开发版本,并将其部署到FirebaseHosting。同样,当我们将变更合并到发布分支时,该操作也会触发部署生产版本。通过结合使用GitHubAction与FirebaseHosting,我们的团队能够快速迭代,并始终得到最新版本的预览。

最后,我们使用Firebase性能监测来监控主要的Web性能指标。

使用CloudFunctions进行社交

在生成您的社交帖子之前,我们首先会确保照片内容是像素级完美的。最终图像包含漂亮的边框,以呈现I/O照相亭特色,并按4:3或3:4的宽高比进行裁剪,以便在社交帖子上呈现出色的效果。

我们使用OffscreenCanvasAPI或CanvasElement来合成原始照片、吉祥物和道具的图层,并生成您可以下载的单个图像。这个处理步骤由image_


转载请注明:http://www.aierlanlan.com/grrz/3978.html