如何解决我应该关闭 BehaviorSubject
我就是因为这个晚上睡不着
我正在使用我的 DataObjectListOptions<T>
类,它包含许多帮助我听来自 firestore 的数据并对数据应用过滤和任何自定义转换
我使用 BehaviorSubject<T> objectsData
使用 StreamBuilder
侦听 UI 上的数据
所以我的问题是:我应该在 DataObjectListOptions<T>
上创建一个 dispose 方法来关闭 objectsData
BehaviorSubject 还是在所有 StreamBuilder
的订阅取消后自动发生?
数据对象列表选项:
abstract class BaseListOptions<L,U> {
BaseListOptions({
this.onLongPress,this.tap,this.empty,this.showNull = false,bool selectionMode = false,Stream<L>? itemsStream,L? items,Map<String,U>? selected,}) : assert(itemsStream != null || items != null),assert(showNull == false || (showNull == true && empty != null)),_selectionMode = BehaviorSubject<bool>.seeded(selectionMode),_selected = BehaviorSubject<Map<String,U>>.seeded(selected ?? {}),_objectsData = itemsStream != null
? (BehaviorSubject<L>()..addStream(itemsStream))
: BehaviorSubject<L>.seeded(items!);
final BehaviorSubject<L> _objectsData;
BehaviorSubject<L> get objectsData => _objectsData;
L? get items => _objectsData.value;
final BehaviorSubject<bool> _selectionMode;
BehaviorSubject<bool> get selectionMode => _selectionMode;
bool? get selectionModeLatest => _selectionMode.value;
final BehaviorSubject<Map<String,U>?> _selected;
BehaviorSubject<Map<String,U>?> get selected => _selected;
Map<String,U>? get selectedLatest => _selected.value;
final void Function(U)? tap;
final void Function(U)? onLongPress;
final U? empty;
final bool showNull;
void selectAll();
void selectNone() {
if (!_selectionMode.requireValue) _selectionMode.add(true);
_selected.add({});
}
void toggleSelected(U item);
void select(U item);
void deselect(U item);
}
class DataObjectListOptions<T extends DataObject>
implements BaseListOptions<List<T>,T> {
DataObjectListOptions({
Widget Function(T,void Function(T)? onLongPress,void Function(T)? onTap,Widget? trailing,Widget? subtitle)?
itemBuilder,this.onLongPress,Stream<List<T>>? itemsStream,List<T>? items,T>? selected,required Stream<String> searchQuery,List<T> Function(List<T>,String)? filter,_filter = (filter ??
((o,f) =>
o.where((e) => filterString(e.name).contains(f)).toList())),_searchQuery = BehaviorSubject<String>()..addStream(searchQuery),T>>.seeded(selected ?? {}),originalObjectsData = itemsStream != null
? (BehaviorSubject<Map<String,T>>()
..addStream(itemsStream.map((l) => {for (final o in l) o.id: o})))
: BehaviorSubject<Map<String,T>>.seeded(
{for (final o in items!) o.id: o}),itemBuilder = (itemBuilder ??
(i,Widget? subtitle) =>
DataObjectWidget<T>(i,subtitle: subtitle,onLongPress:
onLongPress != null ? () => onLongPress(i) : null,onTap: onTap != null ? () => onTap(i) : null,trailing: trailing)) {
buildItem = (i,{onLongPress,onTap,trailing,subtitle}) {
return this.itemBuilder(i,onLongPress,subtitle);
};
_objectsData = (showNull
? BehaviorSubject<List<T>>.seeded([empty!])
: BehaviorSubject<List<T>>())
..addStream(Rx.combineLatest2<String,T>,List<T>>(
_searchQuery,originalObjectsData,(search,items) => search.isNotEmpty
? _filter(items.values.toList(),search)
: items.values.toList()));
}
@override
late BehaviorSubject<List<T>> _objectsData;
@override
BehaviorSubject<List<T>> get objectsData => _objectsData;
@override
List<T>? get items => objectsData.value;
final BehaviorSubject<Map<String,T>> originalObjectsData;
@override
final BehaviorSubject<bool> _selectionMode;
@override
BehaviorSubject<bool> get selectionMode => _selectionMode;
@override
bool? get selectionModeLatest => _selectionMode.value;
@override
final BehaviorSubject<Map<String,T>> _selected;
@override
BehaviorSubject<Map<String,T>> get selected => _selected;
@override
Map<String,T>? get selectedLatest => _selected.value;
final BehaviorSubject<String> _searchQuery;
BehaviorSubject<String> get searchQuery => _searchQuery;
String? get searchQueryLatest => _searchQuery.value;
final List<T> Function(List<T>,String) _filter;
@override
final void Function(T)? tap;
@override
final void Function(T)? onLongPress;
@override
final T? empty;
@override
final bool showNull;
final Widget Function(T,Widget? subtitle) itemBuilder;
late final Widget Function(T,{void Function(T)? onLongPress,Widget? subtitle}) buildItem;
@override
void selectAll() {
if (!_selectionMode.requireValue) _selectionMode.add(true);
_selected.add({for (var item in _objectsData.requireValue) item.id: item});
}
@override
void selectNone() {
if (!_selectionMode.requireValue) _selectionMode.add(true);
_selected.add({});
}
@override
void toggleSelected(T item) {
if (_selected.requireValue.containsKey(item.id)) {
deselect(item);
} else {
select(item);
}
}
@override
void select(T item) {
assert(!_selected.requireValue.containsKey(item.id));
_selected.add({..._selected.requireValue,item.id: item});
}
@override
void deselect(T item) {
assert(_selected.requireValue.containsKey(item.id));
_selected.add(_selected.requireValue..remove(item.id));
}
}
String filterString(String s) => s.toLowerCase();
UI 中的示例用法:
class DataObjectList<T extends DataObject> extends StatefulWidget {
const DataObjectList({Key? key,required this.options}) : super(key: key);
final DataObjectListOptions<T> options;
@override
_ListState<T> createState() => _ListState<T>();
}
class _ListState<T extends DataObject> extends State<DataObjectList<T>>{
@override
Widget build(BuildContext context) {
return StreamBuilder<List<T>>(
stream: widget.options.objectsData,builder: (context,stream) {
if (stream.hasError) return Center(child: ErrorWidget(stream.error!));
if (!stream.hasData)
return const Center(child: CircularProgressIndicator());
final List<T> _data = stream.data!;
if (_data.isEmpty) return const Center(child: Text('No data to show'));
return ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 6),cacheExtent: 200,itemCount: _data.length,itemBuilder: (context,i) //item builder...);
},);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。