Flutter 移动端架构实践:Widget-Async-Bloc-Service

变成了这样:

异步的方法可以:

  • 1.将零个,一个或多个值添加到输入接收器。

  • 2.返回一个Future的结果,调用的代码可以等待结果并相应地执行某些操作。

  • 3.抛出一个异常,调用的代码可以通过try/catch捕获它,并在需要时展示一个警告。

稍后,我们将看到一个完整的例子,说明它在实践中的用处。

更多关于BLoC的信息

一个Async BLoC可以定义一个StreamController/Stream对,如果使用RxDart,则等效对应定义一个BehaviorSubject/Observable

如果有需要,我们甚至可以执行高级的流操作,例如通过combineLatest将流组合在一起。 但是要明确:

  • 1.如果需要以某种方式组合,我建议在单个BLoC中使用多个流。

  • 2.我不鼓励在一个BLoC中使用多个StreamControllers。相反,我更喜欢将代码分割到两个或更多的BLoC类中,以便更好地分离关注点。

数据层/BLoC中的行为

  • 1.BLoC应该是纯Dart的——没有UI代码,没有导入Flutter相关类和文件,也没有在BLoC中使用BuildContext

  • 2.BLoC不应 直接 调用第三方相关代码,这应该是Service做的。

  • 3.控件和BLoC之间的接口应该和BLoCService之间的接口保证一致,也就是说,BloC可以通过同步/异步方法直接与服务类通信,并通过流通知更新。

服务层

Service类应该具有和BLoC相同的输入/输出接口。但是,ServiceBLoC之间存在一个本质性的区别,那就是:

  • BLoC可以持有和修改状态。

  • Service不能持有和修改状态。

换句话说,我们可以将Service视为 纯粹 的功能组件, 它可以修改和转换从第三方库收到的数据。

示例: Firestore service

  • 我们可以实现一个FirestoreDatabaseService作为Firestore的指定域的API包装器。

  • 输入的数据(读取):将来自Firestore文档的键值对的流转换为强类型的不可变数据Model

  • 数据输出(写入):将数据Model转换为键值对,以便写入Firestore

这种情况下,Service类执行简单的数据操作。与BLoC不同,Service不具有任何状态。

关于术语的说明:对于与三方服务的通信的类,其他文章通常使用Repository来表述;甚至对于Repository的定义也随着时间的推移而发展(有关更多信息,请参阅此文章)。 在本文中,我没有明确区分ServiceRepository

将其聚集在一起:使用Provider包


一旦我们定义了BLoCService,我们就需要将其与控件相关联。

这段时间以来,我一直在使用 Remi Rousselet 的 Provider 包。 这是一个纯粹基于InheritedWidgetFlutter 依赖注入系统

我真的很喜欢它的简洁性,下述代码是如何使用它来添加身份验证服务:

return Provider(

builder: (_) => FirebaseAuthService(), // 实现了AuthService的FirebaseAuthService

child: MaterialApp(…),

);

我们如何使用它来创建BLoC

return Provider(

builder: (_) => SignInBloc(auth: auth),

dispose: (_, bloc) => bloc.dispose(),

child: Consumer(

builder: (_, bloc, __) => SignInPage(bloc: bloc),

),

);

请注意Provider控件是如何对可选的dispose回调进行配置的,我们使用它来处理BLoC并关闭相应的StreamControllers

Provider为我们提供了一个简单灵活的API,我们可以使用它来向控件树添加任何我们想要的东西。它适用于BLoCService、数值甚至更多。

image

我将在稍后的一些文章中更详细地讨论如何使用Provider。 目前为止,我强烈推荐Google IO大会上的这个演讲:

https://www.youtube.com/watch?v=d_m5csmrf7I

实战项目:登录页面


现在我们已经了解了WABS在概念上的工作原理,让我们使用它来构建Firebase的身份验证流程。

以下是我用FlutterFirebase实现的身份验证流程的示例:

image

观察到的结果:

  • 当触发了登录事件,我们禁用了所有按钮并显示CircularProgressIndicator,我们将加载状态设置为true来达到该效果。

  • 登录成功或失败后,我们重新启用所有按钮并恢复标题的内容,我们通过设置loading=false达到该效果。

  • 登录失败时,我们会弹出一个警示的对话框。

这里是用于驱动这些逻辑的SignInBloc的简单实现:

import ‘dart:async’;

import ‘package:firebase_auth_demo_flutter/services/auth_service.dart’;

import ‘package:meta/meta.dart’;

class SignInBloc {

SignInBloc({@required this.auth});

final AuthService auth;

final StreamController _isLoadingController = StreamController();

Stream get isLoadingStream => _isLoadingController.stream;

void _setIsLoading(bool isLoading) => _isLoadingController.add(isLoading);

Future signInWithGoogle() async {

try {

_setIsLoading(true);

return await auth.signInWithGoogle();

} catch (e) {

rethrow;

} finally {

_setIsLoading(false);

}

}

void dispose() => _isLoadingController.close();

}

请注意,该BLoC仅向外暴漏了StreamFuture的公共API

Stream get isLoadingStream;

Future signInWithGoogle();

这符合我们对Async BLoC的定义。

所有的魔法都发生在signInWithGoogle()方法中。让我们通过注释再次回顾这些代码:

Future signInWithGoogle() async {

try {

// 首先通过将loading=true交给流的接收器

_setIsLoading(true);

// 然后登录并等待结果

return await auth.signInWithGoogle();

} catch (e) {

// 登录失败,将调用代码的异常重新抛出

rethrow;

} finally {

// 登录成功或者失败, 将loading=false交给流的接收器

_setIsLoading(false);

}

}

和一般的BLoC一样,该方法会向接收器添加值;但除此之外,它也可以异步返回一个值,或抛出一个异常。

这意味着我们可以在SignInPage中写出这样的代码:

Future _signInWithGoogle(BuildContext context) async {

try {

await bloc.signInWithGoogle();

// 处理成功

} on PlatformException catch (e) {

// 处理失败

}

}

这段代码看起来很简单,事实上也确实如此,因为我们需要的仅仅是async/ awaittry/catch

然而,对于仅使用接收器和流的“严格”版本的BLoC,这是不可能的。仅供参考,在Redux中实现这样的功能…嗯…并不是那么有趣!?

——虽然看起来Async-BLoC似乎对BLoC来说只是一个很小的改进,但它们完全不同。

处理异常时的注意事项

处理异常的另一种可行性是向流中添加一个error的对象,如下所示:

Future signInWithGoogle() async {

try {

// 首先通过将loading=true交给流的接收器

_setIsLoading(true);

// 然后登录并等待结果

return await auth.signInWithGoogle();

} catch (e) {

// 向流中添加一个error

_isLoadingController.addError(e);

} finally {

// 登录成功或者失败, 将loading=false交给流的接收器

_setIsLoading(false);

}

}

这样,在widget类中,我们可以编写如下代码:

class SignInPage extends StatelessWidget {

SignInPage({@required this.bloc});

final SignInBloc bloc;

// 由按钮的onPressed回调方法进行调用

Future _signInWithGoogle(BuildContext context) async {

await bloc.signInWithGoogle();

}

void build(BuildContext context) {

return StreamBuilder(

stream: isLoadingStream,

builder: (context, snapshot) {

if (snapshot.hasError) {

// 展示error

showDialog(…);

}

// 基于快照渲染UI

}

)

}

}

但这样并不优雅,原因有二:

  • 1.它在StreamBuilderbuilder中显示了一个对话框,这不是很好,因为builder只应该返回一个控件,而不是执行任何命令式的代码。

  • 2.代码可读性并不高,我们显示错误的地方与执行登录的地方并不一致。

所以,不要这样做,也不要使用上文所展示的try/catch。?

我们能通过WABS创建异步服务吗?


当然,正如我之前所说的:

  • BLoC可以持有和修改状态。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

学习福利

【Android 详细知识点思维脑图(技能树)】

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

详细整理在GitHub可以见;

Android架构视频+BAT面试专题PDF+学习笔记​

Android 详细知识点思维脑图(技能树)】**

[外链图片转存中…(img-cfS8YbQf-1710659067215)]

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中…(img-4NCRL6Pz-1710659067215)]

详细整理在GitHub可以见;

Android架构视频+BAT面试专题PDF+学习笔记​

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值