关于flutter中的状态管理(三)(Stream版Provid
上一篇有个想法就是以ChangeNotifier为主, InheritedWidget来管理ChangeNotifier的思路,最终夭折,这篇主要搞一下使用StreamController和InheritedWidget组合方式来实现状态管理,依然是InheritedWidget管理StreamController, StreamController来最终完成状态的管理.
(原创想法,如有雷同纯属巧合)
先思考我们要的是什么效果,我需要在全局套一个InheritedWidget来传递StreamController以及数据,而且是数据与StreamController是一一对应的,所以我们可以将数据与StreamController放到一起,而且为了我的数据扩展,我可以这样来定义
class CountModel<T> extends StreamObject {
int count = 0;
add() {
count++;
update();
}
}
class StreamObject {
final StreamController? _streamController = StreamController();
StreamSink? get sink => _streamController?.sink;
Stream? get stream => _streamController?.stream;
void update() {
sink?.add("");
}
}
将数据类型继承自基类,基类中管理StreamController
当然这里我们在StreamBuilder中使用的是count本身,所以sink.add只是用来触发重构的,所以这里随便传什么都可以,前提是你不再使用snapshot,而是使用数据本身
创建InheritedWidget
class PutStreamController<T extends StreamObject> extends InheritedWidget {
T model;
PutStreamController(
{required this.model, Key? key, required Widget child })
: super(key: key, child: child);
static T? of<T extends StreamObject>(BuildContext context, {bool listen = true}) {
PutStreamController<T>? inheritedWidget;
inheritedWidget = ((context.getElementForInheritedWidgetOfExactType<PutStreamController<T>>())
?.widget as PutStreamController<T>?);
return inheritedWidget?.model;
}
@override
bool updateShouldNotify(covariant PutStreamController oldWidget) {
return false;
}
}
这里只是使用到了InheritedWidget传递数据的能力,所以updateShouldNotify可以返回false,以及我只需要通过getElementForInheritedWidgetOfExactType
查询到我的数据即可
使用起来是这样的
void main() {
Get.put(TestGetController());
runApp(
PutStreamController(
model: CountModel(),
child: const MyApp(),
)
);
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: StreamBuilder(
stream: PutStreamController.of<CountModel>(context)?.stream,
builder: (context, AsyncSnapshot snapshot) {
return Text("${PutStreamController.of<CountModel>(context)?.count}");
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
PutStreamController.of<CountModel>(context)?.add();
},
child: const Icon(Icons.add),
),
);
}
}
现在整体来看是可以了,但是我一点都不想把StreamController暴露出来,所以可以对StreamBuilder进行封装
typedef AsyncModelBuilder<T> = Widget Function(BuildContext context, T model);
class ModelBuilder<T extends StreamObject> extends StatelessWidget {
ModelBuilder({required this.builder,Key? key}) : super(key: key);
AsyncModelBuilder<T?> builder;
@override
Widget build(BuildContext context) {
T? model = PutStreamController.of<T>(context);
return StreamBuilder(
stream: model?.stream,
builder: (context, _) => builder(context,model),
);
}
}
这样调用就变得简单了
ModelBuilder<CountModel>(
builder: (context,model)=>Text("${model?.count}"),
)
而之前是这样的...
StreamBuilder(
stream: PutStreamController.of<CountModel>(context)?.stream,
builder: (context, AsyncSnapshot snapshot) {
return Text("${PutStreamController.of<CountModel>(context)?.count}");
},
),
到这里一个基于StreamController和InheritedWidget的状态管理就完成了
最终的代码
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(PutStreamController(
model: CountModel(),
child: const MyApp(),
));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: ModelBuilder<CountModel>(
builder: (context,model)=>Text("${model?.count}"),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
PutStreamController.of<CountModel>(context)?.add();
},
child: const Icon(Icons.add),
),
);
}
}
class PutStreamController<T extends StreamObject> extends InheritedWidget {
final T model;
const PutStreamController({required this.model, Key? key, required Widget child})
: super(key: key, child: child);
static T? of<T extends StreamObject>(BuildContext context,
{bool listen = true}) {
PutStreamController<T>? inheritedWidget;
inheritedWidget = ((context
.getElementForInheritedWidgetOfExactType<PutStreamController<T>>())
?.widget as PutStreamController<T>?);
return inheritedWidget?.model;
}
@override
bool updateShouldNotify(covariant PutStreamController oldWidget) {
return false;
}
}
class CountModel extends StreamObject {
int count = 0;
add() {
count++;
update();
}
}
class StreamObject {
final StreamController? _streamController = StreamController();
StreamSink? get sink => _streamController?.sink;
Stream? get stream => _streamController?.stream;
void update() {
sink?.add("");
}
}
typedef AsyncModelBuilder<T> = Widget Function(BuildContext context, T? model);
class ModelBuilder<T extends StreamObject> extends StatelessWidget {
const ModelBuilder({required this.builder,Key? key}) : super(key: key);
final AsyncModelBuilder<T> builder;
@override
Widget build(BuildContext context) {
T? model = PutStreamController.of<T>(context);
return StreamBuilder(
stream: model?.stream,
builder: (context, _) => builder(context,model),
);
}
}
这里你只需要关注于你的CountModel就可以了
Provider是基于ChangeNotifier和InheritedWidget, ChangeNotifier来刷新InheritedWidget从而实现对应数据的刷新,主角是InheritedWidget,而今天的Stream与InheritedWidget的组合却是,以Stream为主的,所以我称之为Stream版本Provider