1.引言

写这篇文字目标是让大家轻松上手BloC模式的开发过程,从了解到应用。掌握BloC应用,理解BloC原理。从Bloc模式的设计原理出发,通过一小时的学习,轻松掌握Bloc模式的项目实践。
在此之前你需要具备以下条件:
(1)会Dart语言,尤其对异步和Stream要有了解。
(2)学过Flutter,使用Flutter开发过单独的页面。
(3)最好是实践过MVP模式,熟悉应用分层设计的过程。

2.Flutter中的状态管理

界面编程可以简化为操作触发事件,事件变更状态。
flutter 使用了与很多前端开发框架相同的开发思想,都是声明式编程框架:
状态管理归根到底是对状态数据的管理,在哪里存储、哪里刷新、在哪里修改。

2.1 在State中管理

flutter自身已经为我们提供了状态管理,而且你经常都在用到。没错,它就是 Stateful widget。当我们接触到flutter的时候,首先需要了解的就是有些小部件是有状态的,有些则是无状态的。stateless widget 与 stateful widget。在stateful widget中,我们widget的描述信息被放进了State,而stateful widget只是持有一些immutable的数据以及创建它的状态而已。它的所有成员变量都应该是final的,当状态发生变化的时候,我们需要通知视图重新绘制,这个过程就是setState。这看上去很不错,我们改变状态的时候setState一下就可以了。
在我们一开始构建应用的时候,也许很简单,我们这时候可能并不需要状态管理。

但是随着功能的增加,你的应用程序将会有几十个甚至上百个状态。这个时候你的应用应该会是这样。

2.2 回调函数

创建子控件的时候设置回调函数,子控件在内部调用该函数,在外部的回调函数做对应状态变更处理。

2.3 事件总线EventBus

需要使用单例自己实现,相当于一个全局单例中持有了事件以及订阅者。
每个要用的页面都创建eventBus实例(实际上是同一个实例),调用订阅、通知、移除等方法。
这里订阅参数是回调函数。

2.4 通知Notification

通知是一种自下而上的信息传递机制,首先定义继承Notification 的通知类(包含数据),
在需要监听事件的层级节点开始添加NotificationListener<通知类>(onNotification:通知回调,child:结点树)
在回调函数中修改页面整体状态。在子控件中调用通知类.dispatch(context) 即可通知顶层更新界面。

2.5 InheritedWidget

是一种从上而下传递和共享数据的方式,首先定义继承InheritedWidget的共享数据控件,在用到控件的地方
context.dependOnInheritedWidgetOfExactType() 获取数据共享控件,之后就可以读取
其中的数据。数据变更是通过外层操作修改数据来实现。

2.6 Provider

也需要定义继承ChangeNotifier的数据类,包含要管理的数据和修改和通知数据变更的方法。使用ChangeNotifierProvider《数据类》.value()
来建立管理状态的数据和控件树。在子控件树中使用Provider.of<数据类>(context)来获取共享的数据实体,可以调用其中的方法或使用其中的数据。
Provider 更像是一个依赖注入工具

总结

setState只适用于管理当前Widget中的少量状态。
回调函数的方式让父控件和子控件深度耦合,事件总线的方式集中处理消息,如果所有状态都使用EventBus效率太低。Notification 适用于点击事件的向上传递。InheritedWidget适用从上而下建立数据共享。
每种方案都无法平衡解耦、事件、状态的维护。适合做一些简单事件传递和状态变更。
各种构架MVP, MVVM, MVI, 目的就是数据和逻辑分离, 逻辑和UI分离,
所以初识Flutter的时候对这种万物皆widget, 一个树里面包含一切的方式有点怀疑, UI逻辑数据写成一堆, 程序功能复杂后, 肯定会越写越乱.

设计app的架构经常会引起争论。每个人都有自己喜欢的一套炫酷的架构和一大堆名词。针对flutter应用的设计常见的有BloC、Redux等方案。
分层也好架构设计也罢,目的无非就是把代码各部分职能、组织关系拆分清楚,方便我们的开发和维护。

3.bloc核心概念

3.1 简介

Flutter带来的一套响应式设计并不能很好的兼容MVC。一个脱胎于这个经典模式的新的架构就出现在了Flutter社区–BloC。
BloC是Business Logic Components的缩写。BloC的哲学就是app里的所有东西都应该被认为是事件流:一部分组件订阅事件,另一部分组件则响应事件。BloC 将event流作为输入,并将它们转换为state流作为输出。

使用BloC的好处
BloC可以比较轻松地将展示层的代码与业务逻辑分开,从而使您的代码快速,易于测试且可重复使用。
Bloc在设计时考虑到了以下三个核心价值:
简单:易于理解,可供技能水平不同的开发人员使用。
强劲:通过将它们组成更小的组件,帮助制作出色而复杂的应用程序。
可测试:轻松测试应用程序的各个方面,以便我们可以自信地进行迭代。

安装流程:
bloc - bloc的核心库
flutter_bloc - 强大的Flutter Widgets可与bloc配合使用,以构建快速,反应灵活的移动端应用程序
(1)在pubspec.yaml中添加bloc 和 flutter_bloc的依赖。

dependencies: bloc: ^6.0.0flutter_bloc: ^6.0.0

(2)拉取依赖flutter packages get
(3)引入依赖
在main.dart中添加

import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

3.2 Stream

Stream是Dart:async库的核心API,可以接收一系列异步事件。Stream这个单词可以代表小河或小溪,那我们就用一个故事理解这个API。


Stream这个小溪上游会时不时(异步)的有水流下了,每次来多大水、多久来一次咱都不知道。具体为啥咱也不敢说,咱也不敢问。我在小溪上安个(订阅listen)小水车,水(onData)流下来的时候就会转动,考虑到有杂物也会流下来,我给水车加个过滤网(onError)来拦截。

有一天我不在的时候小溪流下来一个冰山,差点把我的水车砸了,这怎么能忍啊!我就沿着小溪上游查看,发现小溪上游都是一个工厂(StreamController),工厂的出水口(controller.stream)是小溪源头,混进厂子里面继续查看,厂子里面挺复杂的,看得我头晕转向,只能朝着有光的地方走。果然欧气爆棚,在厂子里面找到了最终的源头(StreamSink)。这SreamSink就是个水槽,有的厂水槽边上排了一组水桶,按照次序一桶一桶往小溪倒水(从迭代器创建Stream);有的厂的水槽靠送水工倒水,这送水工也很不靠谱,有时候半天不来一次,(从Future创建Stream);还有的厂子把水槽的建的老远了,具体谁往里面倒啥东西,我就不去看了,怕看到不该看的。
我找到厂长把冰山的问题说了下,厂长只给我保证流下去的是水(建厂时指定的泛型类型),冰山也是水,还威胁我下次给我流个更大的冰山下去,让我建个更结实的网子拦着。我是气的原地爆炸,想着在厂子里搞点破坏啥的。最后还真让我找到个大杀器,工厂有个核按钮(controller.close()),但不是原地爆炸的那种,按一下可以一键倒闭。也算设计者良心,闭前会给小溪流个标记(done)下去,让小溪上的水坝知道水厂老板跑路了。想了想还是算了,等哪天幼儿园毕业不玩泥巴了就让你丫的跑路。
算了回家去了,顺便看看别人家小溪上有什么玩法,长长见识。

有的小溪只有一个分叉,所以只能有一个监听,这种流叫单订阅流。
有的小溪比较特殊,可以有很多分叉,每个分叉都可以添加一个监听,而且分叉流下来的东西是相同的,这种流叫广播流。
有的小溪上搭建了很多新设备,比如做简单变换的(map),复杂变换的(transform),筛选的(where),控制流速的(take),这些设备流出的还是小溪,可以随意组合加工,玩法多样。
我还发现当我们创建订阅的时候会返回个遥控器(StreamSubscription),不仅可以通过这个遥控器设置事件处理,还有暂停(pause)、恢复(resume)和取消(cancel)键。等我不想玩的时候暂停就好,再也不用担心没人在的时候水车被冲垮了,想玩的时候按下恢复就行,退出的是按下cancel就可以取消订阅。


另一种生成流的方式是生成器,async * 标记的方法都是生成器,返回值只能是Stream类型,方法内部每yield一次都会向返回的流中添加一次数据。

Stream<int> countStream(int max) async* {for (int i = 0; i < max; i++) {yield i;}
}

3.3 Cubit


Cubit是一种特殊的Stream,用来维护UI的状态,是Bloc模式的基石。通常我们会用一个变量来标记UI的状态,还要设计一些方法来控制这个状态的变化,状态变化后还得等界面刷新的时候更新UI。
Cubit将这些结合在一起了,可以把Cubit当做一类状态的集合,你调用他的方法就会改变Cubit当前的状态,状态对应控件所需展示的数据都在状态中。如果状态对应复杂的数据结构,可以自定义状态类型。甚至可以定义一个基类,然后用不同的子类代表不同的状态,每个子类内部可以有各自不同的数据结构。
我们从一个int变量来表示状态的案例讲起。

(1)状态维护

首先Cubit维护的泛型类型就对应原本状态标记的类型,通过继承Cubit,并通过构造函数来设置初始状态。

class CounterCubit extends Cubit<int> {CounterCubit(int initialState) : super(initialState);
}
final cubitA = CounterCubit(10); // 状态从 10 开始

在需要更新状态的控件中,通过cubitA.state就可以拿到状态值用于显示了。

(2)状态变更

在子类Cubit外部暴露的业务方法中可以通过emit方法来实现修改状态。

class CounterCubit extends Cubit<int> {CounterCubit() : super(0);void increment() => emit(state + 1);
}

在按钮的点击事件中调用cubitA.increment() 就修改了状态值。纳尼,这么用和直接使用一个int变量有什么不同?

onPressed: () {setState(() {_cubitA.increment();});
},

这其实已经将页面中的一部分逻辑转移到Cubit中了,页面中不存在修改数据的逻辑了。其次,如果这个业务逻辑是要请求网络的,那这部分代码也可以从页面中剥离出来,页面只需要处理显示和事件响应就行了。而且状态的变化是流来实现的,完全是异步的。
注意:Bloc 和 Cubits 都会忽略重复的状态。

(3)观察状态

Cubit还可以通过覆盖Cubit中onChange 方法来记录和观察状态的变更,每次状态变更都会调用该方法,入参Change不仅包含了当前的状态,还包含了变化前的状态,在这里打日志可以很方便的追踪状态的变更。

  @overridevoid onChange(Change<int> change) {print(change);// 打印日志super.onChange(change);}

(4)错误处理

在进行业务处理时,如果出现错误的情况,可以通过addError方法来创建状态。

  void increase() {if (this.state > 10) {addError("计数不能大于10!", StackTrace.current);} else {this.emit(this.state + 1);}}

同样可以覆写onError方法来打印错误日志。

  @overridevoid onError(Object error, StackTrace stackTrace) {print('$error, $stackTrace');super.onError(error, stackTrace);}

3.4 Bloc


如果你理解了Cubit那么对Bloc也会很容易理解,因为Bloc就是Cubit的子类,主要的变化是触发状态变更的不再是方法调用,而是事件的传递。
这么做好处是,可以更清晰的记录是什么事件触发了状态A到状态B的变更。

(1)定义事件

事件是根据界面的交互来定义的,不同的操作即便会得到相同的状态也得定义不同的事件对象,如果事件需要传业务数据,那么就需要定义一个事件基本类型,在不同事件子类中添加字段进行数据传递。如果只需要标记不同事件类型,那么用枚举或者基本变量也行的。事件也有可能是与Bloc交互的其他层来产生,比如来自数据层的事件。
此处针对++按钮和–按钮点击,定义一个increase和decrease事件。

enum CounterEvent { increase,decrease}

或者定义子类也可以

@immutable
abstract class CountEvent {}
class IncreaseCountEvent extends CountEvent{}
class DecreaseCountEvent extends CountEvent{}

(2)定义Bloc

定义Bloc是需要指定两个泛型参数,第一个是事件类型,第二个是状态类型,构造函数中同样需要指定初始状态,最后需要实现mapEventToState来实现事件到状态的转变,只需要在其中处理好event,根据业务逻辑yield对应的状态即可。

class CountBloc extends Bloc<CountEvent, int> {CountBloc() : super(0);@overrideStream<int> mapEventToState(CountEvent event,) async* {if (event is IncreaseCountEvent) {yield state + 1;} else if (event is DecreaseCountEvent) {yield state - 1;}}
}

(3)使用Bloc

现阶段而言使用Bloc和Cubit的流程是一样的,首先在页面中定义Bloc实例。
final _countBloc = CountBloc();
在需要显示数据的地方使用当前状态
Text("${_countBloc.state}",),
有区别的地方是之前调用cubit方法的地方现在变成通过bloc实例add事件来实现。

onPressed: () {setState(() {_countBloc.add(IncreaseCountEvent());});
},

(4)监听变化

Bloc 是Cubit的子类,同样具有onChange 和onError回调方法,用法和之前一样就不说了。Bloc新增了onTransition方法,可以在覆写这个方法来打印事件和状态的变更。

  @overridevoid onTransition(Transition<CountEvent, int> transition) {print(transition);super.onTransition(transition);}

(5)全局观察者

Bloc中有一个BlocObserver 类型的静态变量,

static BlocObserver observer = BlocObserver();

这里的BlocObserver 是框架提供给全局事件观察的接口,里面有如下方法,会在事件的状态变更的过程中被调用。

class BlocObserver {void onCreate(Cubit cubit) {}void onEvent(Bloc bloc, Object event) {}void onChange(Cubit cubit, Change change) {}void onTransition(Bloc bloc, Transition transition) {}void onError(Cubit cubit, Object error, StackTrace stackTrace) {}void onClose(Cubit cubit) {}
}

我们可以继承BlocObserver 来覆写其中的部分方法,进行日志打印。

class SimpleBlocObserver extends BlocObserver {@overridevoid onChange(Cubit cubit, Change change) {print('${cubit.runtimeType} $change');super.onChange(cubit, change);}@overridevoid onTransition(Bloc bloc, Transition transition) {print('${bloc.runtimeType} $transition');super.onTransition(bloc, transition);}@overridevoid onError(Cubit cubit, Object error, StackTrace stackTrace) {print('${cubit.runtimeType} $error $stackTrace');super.onError(cubit, error, stackTrace);}
}

然后在main()方法中添加Bloc.observer = SimpleBlocObserver();即可在全局打印日志。

Transition { currentState: 0, event: Instance of 'IncreaseCountEvent', nextState: 1 }
CountBloc Transition { currentState: 0, event: Instance of 'IncreaseCountEvent', nextState: 1 }
Change { currentState: 0, nextState: 1 }
CountBloc Change { currentState: 0, nextState: 1 }

注意: 这里onTransition 比 onChange 先执行,同名方法在Bloc中覆写的比全局的观察者先执行。

4. flutter_bloc核心概念

上面的Bloc已经实现了将业务逻辑剥离到Bloc中,但仍旧没法实现进一步的解耦。而且使用上很不方便。
比如:要在子Widget中使用Bloc实例,那就得想办法把bloc传递到子控件中。这需要修改子控件实现,无形中又增强了UI层和业务层的耦合关系。
此外即便是使用了bloc库,也面临着子控件中触发的事件,需要传递事件到父控件中进行setState 操作。
要解决视图层对Bloc实例代码上的依赖,我们需要更灵活的依赖注入。
Flutter中有个InheritedWidget组件,可以顶层创建共享数据,子组件中可以直接通过context拿到共享的数据。
flutter_bloc这个库就是为了方便在flutter应用开发中更方便的使用bloc模式而进行设计的。

4.1 BlocProvider

BlocProvider 是Flutter部件(widget),可通过BlocProvider.of (context)向其子级提bloc。它被作为依赖项注入(DI)部件(widget),以便可以将一个bloc的单个实例提供给子树中的多个部件(widgets)。
BlocProvider的使用就两个要点:
一是在页面找到合适的位置创建Bloc,一般是在APP页面的home节点开始创建。这样做的好处是在后续子结点中所有控件都可以拿到该Bloc实例。

class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',home: BlocProvider(create: (context) => CountBloc(),child: MyHomePage(title: 'Flutter Demo Home Page'),),);}
}

二是在后续界面读取状态值,通过如下方式可以拿到state数据。
Text("${BlocProvider.of(context).state}"),
如果想要向Bloc添加事件,同样也得先获取Bloc实例,再调用add方法。

onPressed: () {setState(() {BlocProvider.of<CountBloc>(context).add(IncreaseCountEvent()});
},

4.2 BlocBuilder

通过使用BlocProvider可以不用在页面中创建Bloc实例成员变量了,子控件中也可以通过依赖注入拿到Bloc实例,但向Bloc添加事件的时候仍然需要setState才可以生效,而且页面中调用该方法会重新build整个页面。如果每次我只想重新构建状态变更影响的那一个控件该怎么处理呢?
BlocBuilder 就是用于依据状态来重新构建Widget的控件。首先BlocBuilder本身就是一个StatefulWidget,它可以根据状态来构建不同的子视图,最终实现局部刷新的目的。
使用BlocBuilder要泛型指定绑定的Bloc类型和State类型,然后在构造函数中创建builder,builder是一个函数类型,传入的参数有context和当前的状态。
如下是将之前布局中的Text控件使用BlocBuilder来包装。

children: <Widget>[BlocBuilder<CountBloc, int>(builder: (context, state) {return Text("${state}");},buildWhen: (previousState, currentState) {if (previousState + currentState > 10) {return false;} else {return true;}},),
],

BlocBuilder的构造函数中还有一个buildWhen参数,这个函数有两个参数,之前的状态和当前的状态,可以根据前后状态的差异来决定本次是否重新构建,只有当返回true时才会调用builder。
有了BlocBuilder之后在添加事件的时候就无需使用setState了。

onPressed: () {BlocProvider.of<CountBloc>(context).add(IncreaseCountEvent());
},

4.3 BlocListener

在APP开发过程中不仅有数据的展示,也有一些在页面层级之上调用一些函数的功能,比如Toast、Dialog、SnackBar、导航跳转等。这些都是依赖页面整体的功能,而不是和具体某个控件相关。
BlocListener 就是应对这种场景的控件,一般是在当前页面的body部分创建,比如将BlocListener作为Scaffold的body结点。创建BlocListener 同样也要指定监听的Bloc类型和状态类型,其次需要定义listener方法,Bloc的每个状态变更都会调用该方法。将之前的页面根节点作为child参数。

   return Scaffold(body: BlocListener<CountBloc, int>(listener: (context, state) {if (state > 10) {Scaffold.of(context).showSnackBar(SnackBar(content: Text("当前数量已经大于10!!"),));}},child: Center(。。。。。)
);

同样BlocListener 也有listenWhen参数,根据前后状态来决定是否触发listener。

4.4 RepositoryProvider

之前开发过APP的同学可能用过Repository模式,就是将APP中的数据需求抽取成Repository接口,可以根据不同的数据源来定义不同的实现,比如LocalRepository和NetRepository,通过代理来得到最终的Repository实现。通常会把Repository作为单例来让全局共享数据。数据缓存本来就是很占内存的,如果使用单例会导致内存利用效率不高。
Bloc也对数据的共享也做了支持,可以更好的控制数据仓库的生命周期。RepositoryProvider就是这样的控件,可以将存储库的单个实例提供给子树中的多个部件(widgets)。RepositoryProvider不需要指定泛型类型,只用指定create方法和child结点即可。

  home: RepositoryProvider(create: (context) => RepositoryA(context),child: BlocProvider(create: (context) => CountBloc(),child: MyHomePage(title: 'Flutter Demo Home Page'),),),

当需要在Bloc中使用Repository是可以按如下方式,值得注意的是,这要求Bloc实例中要能拿到context。

RepositoryProvider.of<RepositoryA>(context)

4.5 MultiBlocProvider

上面的案例界面比较简单,维护的状态也比较单一。如果一个页面有多个Bloc,那么在创建Bloc的时候只能通过嵌套BlocProvider来实现,MultiBlocProvider 就是用于将创建多个Bloc的过程合并在一起。

MultiBlocProvider(providers: [BlocProvider<BlocA>(create: (BuildContext context) => BlocA(),),BlocProvider<BlocB>(create: (BuildContext context) => BlocB(),),BlocProvider<BlocC>(create: (BuildContext context) => BlocC(),),],child: ChildA(),
)

4.6 MultiBlocListener

MultiBlocListener是为了将多个BlocListener合并在一起而设计的,同样无需嵌套即可合并。

MultiBlocListener(listeners: [BlocListener<BlocA, BlocAState>(listener: (context, state) {},),BlocListener<BlocB, BlocBState>(listener: (context, state) {},),BlocListener<BlocC, BlocCState>(listener: (context, state) {},),],child: ChildA(),
)

4.7 MultiRepositoryProvider

MultiRepositoryProvider 也是为了方便合并多个Repository时创建的,用法和其它Muilt控件一致。

MultiRepositoryProvider(providers: [RepositoryProvider<RepositoryA>(create: (context) => RepositoryA(),),RepositoryProvider<RepositoryB>(create: (context) => RepositoryB(),),RepositoryProvider<RepositoryC>(create: (context) => RepositoryC(),),],child: ChildA(),
)

4.8 BlocConsumer

BlocConsumer类似于BlocListener和BlocBuilder的组合,用于一个状态改变既要重绘UI也要提示Toast或执行一些操作的场景。

BlocConsumer<BlocA, BlocAState>(
listenWhen: (previous, current) {},
listener: (context, state) {},
buildWhen: (previous, current) {},
builder: (context, state) {}
)

5. 项目实践

使用Bloc开发应用首先要进行分层设计,类似于MPV模式。同样分为三层,表现层(Presentation)、业务逻辑(Business Logic)、数据层(Data)。

5.1 架构设计

(1)数据层
首先定义基础的数据类型,创建数据表或者网络请求的数据结构,在Repository中创建围绕这些数据结构增删改查的方法,为业务逻辑层提供可调用的方法。

class Repository {final DataProviderA dataProviderA;final DataProviderB dataProviderB;Future<Data> getAllDataThatMeetsRequirements() async {final RawDataA dataSetA = await dataProviderA.readData();final RawDataB dataSetB = await dataProviderB.readData();final Data filteredData = _filterData(dataSetA, dataSetB);return filteredData;}
}

产出app_repository.dart
(2)业务逻辑层
定义业务逻辑层的过程类似于MVP模式中定义View和Presenter接口方法的过程。MVP中我们会分析用户在View层的操作,每个操作会触发什么样的逻辑处理,以此定义P层的接口方法。然后根据每个操作之后界面该有什么样的响应,定义View层的接口方法,供业务处理后由P层调用。
针对一个Bloc,要分析这个Bloc负责的视图控件部分在用户操作时会触发什么Event,每个Event要传递什么数据。考虑每个Event需要如何处理数据,最终产生什么样的State,界面展示State需要更新哪些数据。
然后开始定义Event。
创建app_event.dart ,定义基类Event,创建继承该基类的子类事件。推荐子类命名使用过去时,因为从bloc的角度来看,事件是已经发生的事情。动词过去时+Event后缀。
创建app_state.dart,定义基类状态,创建继承该基类的子类状态,状态命名推荐使用名词+State后缀。
创建app_bloc.dart,定义继承Bloc的子类,需要指定Event和State泛型类型,实现mapEventToState方法。

注意:根据页面交互的复杂度,可以考虑定义一至多个Bloc。Bloc也是Stream,
针对多个bloc有状态联动时,可以在一个Bloc中监听另一个Bloc的State变化。

class MyBloc extends Bloc {final OtherBloc otherBloc;StreamSubscription otherBlocSubscription;MyBloc(this.otherBloc) {// 在通过构造函数传入依赖的其他bloc
otherBlocSubscription = otherBloc.listen((state) {// 处理state,并通过add()向当前bloc添加事件。 });}@overrideFuture<void> close() { // 在当前Bloc关闭的时候停止订阅。otherBlocSubscription.cancel();return super.close();}
}

(3)表现层
表现层就是UI层,这里就是flutter_bloc这个包的天下了。先考虑在什么位置创建bloc,如果bloc和页面的生命周期一致,那么在创建Scaffold的时候调用BlocProvider创建Bloc实例就行。
其次需要考虑状态变化时要更新哪部分界面,变化的部分用BlocBuilder包装就好,还可以通过buildWhen来精细化的控制更新过程。
最后针对需要全局展示的内容或调用的方法,可以在创建BlocProvider 的子结点上创建BlocListener,处理Dialog、Toast、Snackbar的展会。

todo 项目案例

Flutter应用架构之BloC模式实践相关推荐

  1. flutter bloc_如何使用BLoC模式处理Flutter中的状态

    flutter bloc Last year, I picked up Flutter and I must say it has been an awesome journey so far. Fl ...

  2. iOS进阶之架构设计MVVM模式实践(11)

    1.下面通过一个实例来体会一下MVVM架构模式,下面是该工程的一级目录如下,每层之间的交互是用Block的形式来实现的 工程目录说明: 1.Request:文件夹下存储网络请求的类,下面会给出具体的实 ...

  3. Flutter 应用开发之Bloc模式

    基本概念 响应式编程 所谓响应式编程,指的是一种面向数据流和变化传播的编程范式.使用响应式编程范式,意味着可以在编程语言中更加方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进 ...

  4. 【Flutter实战 BLoC模式 RxDart Provider模式】

    我认为的BLoC模式 观察者模式 + 线程调度 + Dart异步语法特性 + Flutter的封装 Dart异步语法特性: Dart的API, Stream.Future Flutter封装:Stre ...

  5. Flutter之Bloc模式

    Flutter之Bloc模式 全称Business Logic Component,业务逻辑组件 BLoC 是独立处理业务逻辑(网络数据请求.数据处理等等的逻辑),通过流Stream的Sinks,st ...

  6. 使用github API的简单github个人资料页面显示应用程序,以及使用bloc模式的flutter...

    In this article, you will learn about state management using BloC in Flutter along with fetching API ...

  7. 业务与信息系统深度融合,改变业务运营和创新模式:阿里巴巴企业级互联网架构(Aliware)实践...

    摘要:本文的整理自2017云栖大会-南京峰会上阿里中间件高级架构师宁晓民的分享讲义,讲义主要介绍了 的业务发展历程,并分享了阿里巴巴互联网架构的实践经验,最后还结合实际案例对于企业级互联网架构Aliw ...

  8. Serverless:微服务架构的终极模式(文末赠书)

    微服务的生态和实践已经比较成熟,其设计方法.开发框架.CI/CD工具.基础设施管理工具等,都可以帮助企业顺利实施微服务.然而,微服务远没有达到完美,它在架构.开发.基础设施方面仍然面临新的挑战. 微服 ...

  9. Flutter 在铭师堂的实践

    简介 Flutter 是 Google 的一套跨平台 UI 框架.目前已经是 1.7 的 Release 版本.在移动端双端投入人力较大,短期紧急需求的背景下.跨端技术会成为越来越多的移动端技术栈选择 ...

最新文章

  1. 东芝3005您的机器需要保养_两联供之东芝中央空调天氟地水详细讲解
  2. 疯狂kotlin讲义连载之Kotlin的基础类型--null安全
  3. 编写高效的Android代码
  4. 正如孙正义的时间机器的理论
  5. deepin-安装问题:unable to find a medium containing a live file
  6. Error:fatal: Not a git repository (or any of the parent directories): .git
  7. cad高程测绘图lisp_CAD地形图高程信息快速提取的技术与实现
  8. 【软件开发规范七】《Android UI设计规范》
  9. python实现百度贴吧自动签到
  10. 关于选课系统的的界面设计、类图设计、数据库设计。
  11. win10家庭版调出组策略_正版Win10免费送!支持无限次数重装
  12. Excel无法跨表筛选,也不能多列筛选,要如何突破限制呢?本教材有方法
  13. 基于C++的AGV机器人无线控制
  14. windows将程序做成服务
  15. SQL Server-检索数据
  16. Linux挂载新磁盘到根目录/
  17. 魔兽争霸:混乱之治+冰封王座 原版ISO镜像下载地址
  18. CString与string转换
  19. 项目管理-进度网络图
  20. 有理数集合是可数集合,无理数集合是不可数集合

热门文章

  1. 考试自动显示答案的软件或者源码
  2. 【TI-mmWave】三、CCS Projects导入报错:This project requires product SYS/BIOS v6.73.1.01, or equivalent......
  3. 游戏反外挂技术原理讲解
  4. pandas的loc[ ]和iloc[ ]方法解析
  5. 中科创达发布融合智能泊车技术于解决方案
  6. php里怎么输入,PHP是怎么进行输入输出的
  7. 晋级 7 问 - 技术晋级答辩中常见问题解析
  8. 什么是Use Case?
  9. 十进制怎样转二进制?
  10. Git之Feature分支