针对flutter里的消息传递,这里做一个总结。
1.inheriteWidget
inheriteWidget是一个功能型的widget,可以高效地在widget树中将数据向下传递、共享。inheriteWidget可以跨组件传递数据,结构是inheriteWidget里有子节点和数据data,子节点里ui展示使用了该数据,当该数据变化并刷新时引用了该数据的子节点会刷新从而达到消息监听的效果
2.Notification
Notification是flutter的一个重要机制,在widget树中,每一个节点都可以分发通知,通知会沿着当前节点向上传递,所有父节点可以通过NotificationListener来监听通知。Flutter中将这种由子向父的传递通知的机制称为通知冒泡(Notification Bubbling)。通知冒泡和用户触摸事件冒泡是相似的,但有一点不同:通知冒泡可以中止,但用户触摸事件不行。
通知冒泡原理:
通知通过Notification的dispatch(context)发起,dispatch的源码:
void dispatch(BuildContext target) {
target?.visitAncestorElements(visitAncestor);
}
dispatch调用当前context的visitAncestorElements方法,该方法会向上遍历父元素。visitAncestorElements有一个遍历回调函数,在遍历过程中对遍历到的父元素执行visitAncestor回调。遍历的终止条件是visitAncestor回调返回false。visitAncestor源码:
//遍历回调,会对每一个父级Element执行此回调
bool visitAncestor(Element element) {
//判断当前element对应的Widget是否是NotificationListener。
//由于NotificationListener是继承自StatelessWidget,
//故先判断是否是StatelessElement
if (element is StatelessElement) {
//是StatelessElement,则获取element对应的Widget,判断
//是否是NotificationListener 。
final StatelessWidget widget = element.widget;
if (widget is NotificationListener<Notification>) {
//是NotificationListener,则调用该NotificationListener的_dispatch方法
if (widget._dispatch(this, element))
return false;
}
}
return true;
}
由上面的源码可以看出,visitAncestor有一个element参数为遍历到的父元素,首先判断该父元素是否是StatelessElement类型,如果是则判断该元素的widget是否是NotificationListener类型,如果是则调用NotificationListener的_dispatch方法,该方法的源码如下:
bool _dispatch(Notification notification, Element element) {
// 如果通知监听器不为空,并且当前通知类型是该NotificationListener
// 监听的通知类型,则调用当前NotificationListener的onNotification
if (onNotification != null && notification is T) {
final bool result = onNotification(notification);
// 返回值决定是否继续向上遍历
return result == true;
}
return false;
}
由以上代码可以看出,该函数先是要判断onNotification该回调是否为空并且该notifcation对象是否为监听的类型,如果是则执行onNotification回调。如果该回调执行的返回值决定了该通知是否继续向上遍历。
3.Stream
网上例子很多,针对stream可以单独写一个专题
4.EventBus
EventBus是flutter一个广播机制,核心是基于dart事件流,用以跨页面事件通知,eventBus实现了订阅者模式,包含订阅者发布者两种角色,可自定义监听事件。监听代码:
EventBus.on<Event>().listen((event) {
//监听操作
})
5.bloc
bloc是一种响应式编程方式构建应用的方法,是一种管理状态的设计方式。它是基于stream流的,它的设计模型大致如下:
class bloc{
blocbase ->Model Controller
widget ->UI
static of() => context.ancestorWidgetOfExactType
}
class blocbase{
data
stream
dipose()
}
blocbase封装Model层和Controller层,bloc封装了blocbase和View层,作为顶层节点,叶子节点通过ancestorWidgetOfExactType获取顶层bloc的流并监听,调用bloc提供的方法触发数据改变并发送通知,从而重新构建ui。示例代码如下:
bloc.dart:
import 'package:flutter/material.dart';
import 'dart:async';
//所有BLoC的通用接口
abstract class BlocBase {
void dispose();
}
//通用BLoC提供商 模型中的bloc
class BlocProvider<T extends BlocBase> extends StatefulWidget {
BlocProvider({
Key key,
@required this.child,//ui
@required this.bloc,//model and controller
}): super(key: key);
final T bloc;
final Widget child;
@override
_BlocProviderState<T> createState() => _BlocProviderState<T>();
static T of<T extends BlocBase>(BuildContext context){
final type = _typeOf<BlocProvider<T>>();
BlocProvider<T> provider = context.ancestorWidgetOfExactType(type);
return provider.bloc;
}
static Type _typeOf<T>() => T;
}
class _BlocProviderState<T> extends State<BlocProvider<BlocBase>>{
@override
/// 便于资源的释放
void dispose(){
print('_BlocProviderState dispose');
widget.bloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context){
return widget.child;
}
}
CounterPage.dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'Bloc.dart';
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final IncrementBloc bloc = BlocProvider.of<IncrementBloc>(context);
return Scaffold(
appBar: AppBar(title: Text('Stream version of the Counter App')),
body: Center(
child: StreamBuilder<int>(
stream: bloc.outCounter,
initialData: 0,
builder: (BuildContext context, AsyncSnapshot<int> snapshot){
return Text('You hit me: ${snapshot.data} times');
}
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: (){
bloc.incrementCounter.add(null);
},
),
);
}
}
//模型中的blocbase
class IncrementBloc implements BlocBase {
int _counter;
//
// Stream来处理计数器
//
StreamController<int> _counterController = StreamController<int>();
StreamSink<int> get _inAdd => _counterController.sink;
Stream<int> get outCounter => _counterController.stream;
//
// Stream来处理计数器上的操作
//
StreamController _actionController = StreamController();
StreamSink get incrementCounter => _actionController.sink;
//
// Constructor
//
IncrementBloc(){
_counter = 0;
_actionController.stream
.listen(_handleLogic);
}
void dispose(){
_actionController.close();
_counterController.close();
}
void _handleLogic(data){
_counter = _counter + 1;
_inAdd.add(_counter);
}
}
main.dart:
void main() {
runApp(
BlocProvider(//
child: MyApp(),
bloc: IncrementBloc(),
)
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home:CounterPage()
);
}
}
6. ValueNotifier
同样是订阅者模式,订阅者需要添加监听,通过addListener函数添加监听回调,并且在widget被unmount的时候使用removeListener移除掉该监听。它的原理是内部维护了一个回调列表,当valueNotifier的值改变时通过判断该值是否和旧值相同,如果相同则不作处理,如果不同则调用回调列表的所有回调一次。实际操作中可以在回调函数里调用刷新ui的操作,如setstate()等。关键代码如下:
class ValueNotifier<T> extends ChangeNotifier implements ValueListenable<T> {
set value(T newValue) {
if (_value == newValue)
return;
_value = newValue;
notifyListeners();
}
}
class ChangeNotifier implements Listenable {
@visibleForTesting
void notifyListeners() {
assert(_debugAssertNotDisposed());
if (_listeners != null) {
final List<VoidCallback> localListeners = List<VoidCallback>.from(_listeners);//_listeners是监听的回调函数列表
for (VoidCallback listener in localListeners) {
try {
if (_listeners.contains(listener))
listener();
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'foundation library',
context: ErrorDescription('while dispatching notifications for $runtimeType'),
informationCollector: () sync* {
yield DiagnosticsProperty<ChangeNotifier>(
'The $runtimeType sending notification was',
this,
style: DiagnosticsTreeStyle.errorProperty,
);
},
));
}
}
}
}
}
7.globalkey
globalkey是一个泛型类,泛型模板是state<statefulwidget>的子类,定义如下:
abstract class GlobalKey<T extends State<StatefulWidget>> extends Key
可以用globalkey获取当前state,从而调用对应函数,实现组件间通信。
示例代码如下:
main.dart
GlobalKey<HomePageState> globalKey = GlobalKey();
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home:Scaffold(
appBar: AppBar(
leading:FlatButton(
child: Text('调用'),
onPressed: (){
globalKey.currentState.call();
},
)
),
body: HomePage(key: globalKey),
)
);
}
}
homepage.dart
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
@override
HomePageState createState() => HomePageState();
}
class HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Container();
}
void call() {
print('我是call');
}
}
8.子组件里传入父组件的回调函数,需要的时候调用实现子组件向父组件通信的效果,示例:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
bt(_incrementCounter)//2.设置回调
],
),
),
);
}
}
class bt extends StatelessWidget{
VoidCallback call;//1.回调函数
bt(this.call);
@override
Widget build(BuildContext context){
return RaisedButton(
child: Text('按钮'),
onPressed:()=>call()//3.调用该回调
);
}
}
网友评论