FWidget 用心提供粗劣的组件,助您构建精美的利用。
FWidget 始终致力于为开发者们精心打造易于构建精美利用的 Widget。
迄今为止,FWidget 曾经收到了来自开发者们的 1215 个 Star,感激开发者们的反对,这对 FWidget 来说至关重要。
明天,FWidget 为开发者们带来了一个全新的组件 FBroadcast,以帮忙开发者们可能更轻松的构建更为简单精美的利用。
例如,在 FWidget 的官方网站 https://fwidget.cn 中,通过 FBroadcast 很轻松的就实现了老本极低的,且易于保护的全局实时语言切换。它非常简略无效和明确!
✨ 个性
来看看 FBroadcast 为开发者提供了那些不堪设想的能力反对:
- 反对发送和接管指定类型的音讯
- 音讯反对携带任意类型数据包
- 提供环境注册,一行代码即可移除环境内所有接收者
- 不堪设想的粘性播送
- 双向通信反对
- 易于构建简略明确的部分和全局状态治理
???? 传送区
???? 【传送门:FBroadcast Github 主页】
???? 【传送门:FBroadcast 文档】
???? 献给开发者的 FBroadcast
FBroadcast 是一套高效灵便的广播系统,能够帮忙开发者轻松、有序的构建具备极具复杂性的关联交互和状态变动的精美利用。
FBroadcast 将为构建简单的精美利用带来那些不言而喻的扭转呢?
- Widget/模块间的齐全解耦
通过 FBroadcast 高效的广播系统,开发者能够轻易的实现Widget/模块的解耦。在利用构建的时候,常常须要 Widget/模块A、B、C、.. 之间依据交互操作相互变更状态或数据,开发者们不得不为此让各个Widget/模块相互依赖或者为它们建设对立的状态治理,这能解决问题,但这让构建变得麻烦,也让变更变得难以进行。
FBroadcast 通过建设起简略、无效、明确广播系统,使得在任意Widget/模块中任意时刻/地位的扭转可能被动收回播送,而须要依据这些变更作出响应或更新视图的Widget/模块只须要注册相应的信息接收器,就能够在变更产生时,接管到音讯,作出响应。这使得关联模块间不再须要相互依赖,或是为它们设计建设对立的状态管理器。
非常简略,轻量,和易于变更。当一个Widget/模块不在须要依据另一个Widget/模块的变更而更新时,只需移除其中的接收器即可,而不必为此而大改依赖关系或是状态管理器。
- 简略、灵便、明确、易治理
FBroadcast 为开发者提供了能够在任意时刻发送播送,和注册/移除接收器的能力,毫无束缚和灵便。
播送和接收器之间通过明确的类型(字符串)来相互确认身份,指定类型的播送,只能被指定类型的接收器接管。
FBroadcast 提供了环境注册反对,开发者能够在环境解构时,通过 [unregister()] 函数一次性移除环境中的所有类型接收器,而无需记忆和关怀到底须要移除那些接收器。例如,开发者能够在 Widget 的
dispose()
中,将注册在该 Widget 中的所有接收器一次性全副移除。借助古代IDEA的能力,开发者能够为广播系统建设一张(或多张)对立的播送类型索引表,通过IDEA的援用索引,开发者能够轻松的、高深莫测的看到该类型的播送在那些中央被发送过,在那些中央注册了接收器,非常易于治理和保护。而应用字符串来作为类型标识,使得开发者能够将不同类型的播送含意形容的足够清晰明确。
- 粘性播送反对
FBroadcast 提供了发送粘性播送的反对。在还没有注册任何接收器的状况下,开发者能够在事件产生时,事后发送一条粘性播送。粘性播送会被临时滞留在广播系统中,当有接收器被注册时,即会立刻播送。这有助于帮忙开发者在做逻辑设计时采取更清晰无效的思路。
例如,当一个管制模块中的开关按钮被关上,而此时开关所管制的模块还没有被构建,就能够先发送一条粘性播送,在模块被构建实现注册了接收器后,就会立刻接管到粘性播送而进入开启状态(这与相互依赖、定义对立状态治理或是参数传递,而后查看开关状态的思路有本质区别)。
???? 这真不是个别的 Broadcast
???? Base Broadcast
通信就要简略,明确
通过 FBroadcast 来注册,发送播送十分简便。
/// 注册接收器/// /// registerFBroadcast.instance().register(Key_Message, (value, callback) { /// do something});/// 发送音讯/// /// send messageFBroadcast.instance().broadcast(Key_Message);
FBroadcast 容许开发者在发送音讯的时候,带有数据。
/// 注册接收器/// /// registerFBroadcast.instance().register(Key_Message, (value, callback) { /// 获取数据 /// /// get data var data = value;});/// 发送音讯和数据/// /// send message and dataFBroadcast.instance().broadcast( /// 音讯类型 /// /// message type Key_Message, /// 数据 /// /// data value: data,);
开发者能够抉择将特定类型的音讯进行长久化,这样就能轻易实现广播式的全局状态治理。
FBroadcast.instance().broadcast( /// 音讯类型 /// /// message type Key_Message, /// 数据 /// /// data value: data, /// 将音讯类型长久化 /// /// Persist the message types persistence: true,);
当开发者将一个音讯类型长久化后,就能够在任意地位,通过 FBroadcast.value(String key)
来获取广播系统中该类型音讯的最新的数据。而更新广播系统中的数据只须要通过 broadcast()
即可实现。
⚠️留神,一个音讯类型一旦长久化就只能通过 FBroadcast.instance().clear(String key)
来从广播系统中移除该类型的音讯。
???? Sticky Broadcast
更多的抉择,构建更精美的利用
FBroadcast 反对开发者发送粘性播送。
FBroadcast.instance().stickyBroadcast( /// 音讯类型 /// /// message type Key_Message, /// 数据 /// /// data value: data, );
当广播系统中没有对应类型的接收器时,粘性播送 将会临时滞留在零碎中,直到有该类型的接收器被注册,则会立刻收回播送(当广播系统中有对应类型的接收器时,就和一般播送具备雷同的体现)。
⛓ Two-way communication
双向沟通,双倍效率
FBroadcast 反对在播送发送点接管接收器返回的音讯。
/// 发送音讯/// /// send messageFBroadcast.instance().broadcast( /// 音讯类型 /// /// message type Key_Message, /// 数据 /// /// data value: data, /// 接收器返回的音讯 /// /// The message returned by the receiver callback: (value){ // do something });/// 注册接收器/// /// registerFBroadcast.instance().register(Key_Message, (value, callback) { /// 获取数据 /// /// get data var data = value; /// do something var result = logic(); /// 返回音讯 /// /// return message callback(result);});
通过 FBroadcast 可能给非常轻松的实现双向通信。
???? Bind Context
一码卸载,快捷精准
FBroadcast 反对在注册接收器时传入一个环境对象(能够是任意类型),这会将接收器注册到环境中,当环境解构时,开发者能够不便的一次性移除所有在该环境中注册的接收器。
/// 注册接收器/// /// registerFBroadcast.instance().register( /// 音讯类型 /// /// Message type Key_Message1, /// Receiver /// /// Receiver (value, callback) { /// do something }, /// 更多接收器 /// /// more receiver more: { /// 音讯类型: 接收器 /// /// Message type: Receiver Key_Message2: (value, callback) { /// do something }, Key_Message3: (value, callback) { /// do something }, Key_Message4: (value, callback) { /// do something }, }, /// 环境对象 /// /// context context: this,);/// 移除环境中的所有接收器/// /// Remove all receivers from the environmentFBroadcast.instance().unregister(this);
???? 应用 FBroadcast 能够做些什么?
???? 消息传递
场景:点击 Start,Runner 开始 Run,显示屏须要实时更新运动员的状态。
???? 1. 创立 Runner:
/// Runnerclass Runner { Runner() { /// register FBroadcast.instance().register(Key_RunnerState, (value, callback) { if (value is String && value.contains("Run")) { /// receive start run message FBroadcast.instance().broadcast(Key_RunnerState, value: "0m.."); run(20); } }); } run(double distance) { /// send running message Timer(Duration(milliseconds: 500), () { FBroadcast.instance().broadcast(Key_RunnerState, value: "${distance.toInt()}m.."); var newDistance = distance + 20; if (newDistance > 100) { FBroadcast.instance().broadcast(Key_RunnerState, value: "Win!\nTotal time is 2.5s"); } else { run(newDistance); } }); }}
???? 2. 创立 UI:
Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Stateful( /// init initState: (setState, data) { FBroadcast.instance().register( Key_RunnerState, (value, callback) { /// refresh ui setState(() {}); }, /// bind context context: data, ); }, builder: (context, setState, data) { return FSuper( ... /// get running message text: FBroadcast.value(Key_RunnerState) ?? "Preparing..", ); }, ), const SizedBox(height: 100), FButton( text: "Start" ... onPressed: () { /// send start run message FBroadcast.instance().broadcast(Key_RunnerState, value: "Running..."); }, ), ],)
在下面的示例中,通过 FBroadcast 简略清晰的实现了 Runner 和 UI 之间的通信。
- 点击 Start 按钮,通过 FBroadcast 发送起跑音讯给 Runner;
- Runner 收到音讯后,开始 Run,同时一直通过 FBroadcast 收回 Running info;
- UI 因为注册了接收器,在接管到 Running info 时,通过
FBroadcast.value()
获取音讯数据,自动更新视图。
整个过程中,Runner 和 UI 之间是齐全解耦的,且 UI 只需在 init
中注册接收器(receiver 中调用 setState((){})
),就能依据音讯数据的变动,主动实时的更新视图,而无需开发者关怀整个过程。
⛓ 双向通信
场景:点击按钮申请定位,定位胜利后接管后果,刷新定位点
???? 1. 全局定位服务提供商
class LocationServer { LocationServer() { init(); } init() { /// register Key_Location receiver FBroadcast.instance().register(Key_Location, (value, callback) async { var loc = await location(); /// return message callback(loc); }); } /// Analog positioning Future<List<double>> location() async { await Future.delayed(Duration(milliseconds: 2000)); return [Random().nextDouble() * 280, Random().nextDouble() * 150]; }}
???? 2. 点击发送定位申请,接管返回音讯
FButton( ... text: "Location", onPressed: () { FLoading.show(context, color: Colors.black26, loading: buildLoading()); /// request location FBroadcast.instance().broadcast(Key_Location, callback: (location) { /// The message returned by the receiver setState(() { FLoading.hide(); this.location = location; }); }); },)
FBroadcast 可能进一步简化须要双向通信的场景。开发者能够看到,在这个例子中,通过 FBroadcast 可能轻松的实现定位申请这种双向通信的场景,而且使得定位服务提供商和UI实现的齐全的解耦。
UI交互点只须要发送定位申请的播送,任何注册该播送的定位服务提供商就能够接管该申请进行解决,而后返回后果到UI交互点。也就是说,随着我的项目的演进,开发者能够随时提供新的定位服务提供商,而无需关怀任何的UI变更。
???? 部分状态治理
场景:点击扭转UI色彩
???? 1. 点击收回事件
FButton( text: "Change Color", ... onPressed: () { /// send change color message FBroadcast.instance().broadcast(Key_Color, value: reduceColor()); },)
???? 2. UI 注册接收器
Stateful( /// init initState: (setState, data) { /// register FBroadcast.instance().register( Key_Color, (value, callback) { /// refresh ui setState(() { }); }, /// bind context context: data, ); }, builder: (context, setState, data) { return FSuper( ... /// get color value backgroundColor: FBroadcast.value<Color>(Key_Color) ?? mainBackgroundColor, ); },)
通过 FBroadcast 能够很轻易的实现 UI 交互之间的部分状态更新。下面的示例展现了色彩的变更,数据对象只有一个参数,理论开发过程中,开发者能够依据须要将通信的数据对象进行丰盛扩大。
开发者只须要在须要更新 UI 的 Widget 中注册接收器,调用一次 setState((){})
,在交互点收回音讯。而不必去被动的将触发逻辑和 setState((){})
在所有的交互点都写一次。
???? 全局状态治理
场景:点击头像跳转登陆页,当账号密码不为 null 时,登陆按钮才能够点击。点击登陆按钮发送登陆申请,登陆胜利后,返回上一页,刷新用户信息。
???? 1. 用户信息Widget注册接收器
class Avatar extends StatefulWidget { @override _AvatarState createState() => _AvatarState();}class _AvatarState extends State<Avatar> { User user; int msgCount = 0; @override void initState() { super.initState(); FBroadcast.instance().register( Key_MsgCount, /// register Key_MsgCount reviver (value, callback) => setState(() { msgCount = value; }), more: { /// register Key_User reviver Key_User: (value, callback) => setState(() { /// get value user = value; }), }, /// bind context context: this, ); } @override Widget build(BuildContext context) { return FSuper( ... backgroundImage: (user == null || _textIsEmpty(user.avatar)) ? null : AssetImage(user.avatar), redPoint: user != null && msgCount > 0, redPointText: msgCount.toString(), text: user != null ? null : "Click Login", onClick: user != null ? null : () => Navigator.push(context, MaterialPageRoute( builder: (context) => LoginPage())), ); } @override void dispose() { super.dispose(); /// remove all receivers from the environment FBroadcast.instance().unregister(this); }}
登陆页中注册 Key_User
接收器,当接管到登陆音讯时,取出其中的数据,刷新UI。
???? 2. 构建数据模型
class User{ String name; String avatar; int messageCount = 0; String info;}
???? 3. 构建逻辑解决对象
class LoginHandler { String _userName; String _password; /// set user name, check to see if login is allowed set userName(String v) { _userName = v; if (_textNoEmpty(_userName) && _textNoEmpty(_password)) { FBroadcast.instance().broadcast(Key_Login, value: true); } else { FBroadcast.instance().broadcast(Key_Login, value: false); } } /// set user password, check to see if login is allowed set password(String v) { _password = v; if (_textNoEmpty(_userName) && _textNoEmpty(_password)) { FBroadcast.instance().broadcast(Key_Login, value: true); } else { FBroadcast.instance().broadcast(Key_Login, value: false); } } /// login void login() { Timer(Duration(milliseconds: 1500), () { /// login success,send login success message —— Key_User FBroadcast.instance().broadcast( Key_User, value: User() ..avatar = "assets/logo.png" ..name = _userName ..info = "Seriously provide exquisite widget to help you build exquisite application.", /// Persistence Key_User persistence: true, ); }); }}
将逻辑解决转移到 LoginHandler 中进行隔离,所有的处理结果都通过 FBroadcast 播送进来,使注册到广播系统中的对应接收器可能响应。
???? 4. 登陆页
class LoginPage extends StatefulWidget { @override _LoginPageState createState() => _LoginPageState();}class _LoginPageState extends State<LoginPage> { /// Logic handler LoginHandler handler = LoginHandler(); /// input controller FSearchController _controller1 = FSearchController(); FSearchController _controller2 = FSearchController(); @override void initState() { super.initState(); _controller1.setListener(() { /// update userName handler.userName = _controller1.text; }); _controller2.setListener(() { /// update password handler.password = _controller2.text; }); } @override Widget build(BuildContext context) { return { ... /// userName input FSearch( controller: _controller1, ... ), ... /// userName input FSearch( controller: _controller2, ... ), ... Stateful( initState: (setState, data) { /// register login receiver FBroadcast.instance().register( Key_Login, /// refresh ui (value, callback) => setState(() {}), more: { /// register user receiver Key_User: (value, callback) { FLoading.hide(); Navigator.pop(context); }, }, /// bind context context: data, ); }, builder: (context, setState, data) { return FButton( ... text: "LOGIN", /// Key_Login value=true is allowed to click login onPressed: !(FBroadcast.value(Key_Login) ?? false) ? null : () { _controller1.clearFocus(); _controller2.clearFocus(); FLoading.show(context); /// Execute login logic handler.login(); }, ); },), ... }; }}
注册接收器时,只需在接管回调中调用 setState((){})
,后续所有的数据变动刷新,开发者就能够不必关注了。而给 UI 赋值能够不便的通过 FBroadcast.value() 获取对应数据来进行。
⚠️留神,对于须要全局应用的状态/数据模型,它们对应的播送类型,在发送时,须要至多有一次将 persistence 设置为 true。下面示例中,就在登陆胜利后,对 Key_User
类型的播送进行了长久化。
/// login success,send login success message —— Key_UserFBroadcast.instance().broadcast( Key_User, value: User() ..avatar = "assets/logo.png" ..name = _userName ..info = "Seriously provide exquisite widget to help you build exquisite application.", /// Persistence Key_User persistence: true,);
下面的示例中展现了通过 FBroadcast 能够轻松疾速的实现消息传递,进行 部分、全局状态治理和刷新,很好的将各个模块,逻辑以及UI 进行解耦。FBroadcast 提供了简洁易懂,而且非常灵便的广播系统,极少的解放让开发者能够疾速上手,轻松实现简单逻辑的简化,帮忙开发者构建出易于保护的、简单的、精美的利用。
FBroadcast 在应用过程中,配合对立的播送类型注册表(也能够按模块分多张),开发者能够很轻易的借助 IDEA 的援用检索能力,随时查看所有播送的状况,对于一直迭代过程中的保护非常无益。
想要理解更多具体内容?请拜访 FBroadcast 官方主页 (PS:别忘了投出一个你认可的 Star 哦 ????)。
???? 如何应用?
在我的项目 pubspec.yaml
文件中增加依赖:
???? pub 依赖形式
dependencies: fbroadcast: ^<版本号>
⚠️ 留神,请到 pub 获取 FBroadcast 最新版本号
???? git 依赖形式
dependencies: fbroadcast: git: url: 'git@github.com:Fliggy-Mobile/fbroadcast.git' ref: '<分支号 或 tag>'
⚠️ 留神,分支号 或 tag 请以 FBroadcast 官网我的项目为准。
感觉还不错?请到 《FBroadcast》的 Github 主页投出您认可的一个 Star ???? 吧!
更多精彩组件
- 《FSuper》- 帮忙开发者疾速构建精美的简单视图
- 《FButton》- 为开发者筹备了诸多美好的配置项
- 《FSwitch》- 具备低劣交互和视效的精美开关元素
- 《FRadio》- 一个实用于简直任意单选场景的单选组件
- 《FFloat》- 满足你对浮动元素的所有设想
- 《FRefresh》- 轻松构建下拉刷新成果
- 《FDottedLine》- 辉煌的虚线成果
- 《FSearch》- 一应俱全的搜寻框组件
- 《FToast》- 粗劣灵便的 Flutter 原生 Toast 组件
- 《FLoading》- 帮忙开发者自在的应用 Loading