[英]Flutter - How does MultiProvider work with providers of the same type?
For example, I am trying to obtain data emitted for multiple streams at once, but 2 or more of these streams emit data of the same type, lets say a string.例如,我试图一次获取为多个流发出的数据,但是这些流中的 2 个或更多发出相同类型的数据,比如说一个字符串。
My question is, is it possible to use MultiProvider
and use multiple StreamProvider
(or any provider, but I am interested in this case) of the same type while still being able to access the data emitted by each one of them?我的问题是,是否可以使用MultiProvider
并使用相同类型的多个StreamProvider
(或任何提供者,但我对这种情况感兴趣),同时仍然能够访问每个提供者发出的数据?
A solution for this is using a StreamBuilder
when using common data types but I really like what the MultiProvider
offers in terms of cleaner code.一个解决方案是在使用常见数据类型时使用StreamBuilder
,但我真的很喜欢MultiProvider
在更清晰的代码方面提供的功能。
Example:例子:
class MyScreen extends StatelessWidget {
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<String>(stream: Observable.just("stream1")),
StreamProvider<String>(stream: Observable.just("stream2")),
StreamProvider<String>(stream: Observable.just("stream3"))
],
child: Builder(
builder: (BuildContext context) {
AsyncSnapshot<String> snapshot =
Provider.of<AsyncSnapshot<String>>(context);
String data = snapshot.data;
return Text(data);
},
),
);
}
}
MultiProvider
or not doesn't change anything. MultiProvider
与否不会改变任何东西。 If two providers share the same type, the deepest one overrides the value.如果两个提供程序共享相同的类型,则最深的一个会覆盖该值。
It's not possible to obtain the value from a provider that is not the closest ancestor for a given type.不可能从不是给定类型最接近祖先的提供者那里获取值。
If you need to access all of these values independently, each should have a unique type.如果您需要独立访问所有这些值,则每个值都应具有唯一的类型。
For example, instead of:例如,而不是:
Provider<int>(
value: 42,
child: Provider<int>(
value: 84,
child: <something>
),
)
You can do:你可以做:
class Root {
Root(this.value);
final int value;
}
class Leaf {
Leaf(this.value);
final int value;
}
Provider<Root>(
value: Root(42),
child: Provider<Leaf>(
value: Leaf(84),
child: <something>
),
)
This allows to obtain each value independently using:这允许使用以下方法独立获取每个值:
Provider.of<Root>(context)
Provider.of<Leaf>(context);
UPDATE: This solution I recently found seems to be cleaner and working better.更新:我最近发现的这个解决方案似乎更干净,工作得更好。 The solution below is another way but requires more coding.下面的解决方案是另一种方式,但需要更多的编码。
I was looking for a similar solution and couldn't find anything so I implemented my own with the MultiProvider, StreamGroup, and a ChangeNotifier.我一直在寻找类似的解决方案,但找不到任何解决方案,因此我使用 MultiProvider、StreamGroup 和 ChangeNotifier 实现了自己的解决方案。 I use the StreamGroup
to hold all the streams I need to keep track of by adding and removing streams.我使用StreamGroup
来保存我需要通过添加和删除流来跟踪的所有流。 I didn't want to use a bunch of extra libraries and/or plugins.我不想使用一堆额外的库和/或插件。
In the ChangeNotifierProxyProvider
, it runs the update
function whenever the Family
stream gets an update from StreamProvider<Family>
above it.在ChangeNotifierProxyProvider
,只要Family
流从其上方的StreamProvider<Family>
获得更新,它就会运行update
函数。
// main.dart
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<Family>(
initialData: Family(),
create: (context) => FirebaseFireStoreService().streamFamilyInfo(),
),
ChangeNotifierProxyProvider<Family, FamilyStore>(
create: (context) => FamilyStore(),
update: (context, family, previousFamilyStore) {
// Manually calling the function to update the
// FamilyStore store with the new Family
previousFamilyStore!.updateFamily(family);
return previousFamilyStore;
},
)
],
builder: (context, child) => MaterialApp(),
);
}
The Family
just holds an array of AdultProfile
uid
s so I can keep track of the adults in a family. Family
只保存了一个AdultProfile
uid
数组,所以我可以跟踪一个家庭中的成年人。 It's basically just a stream receiver.它基本上只是一个流接收器。
// family.dart
class Family {
List<String> adults;
Family({
this.adults = const [],
});
factory Family.fromMap(Map<String, dynamic>? data) {
if (data == null) {
return Family(adults: []);
}
return Family(
adults: [...data['adults']],
);
}
}
In my firestore class
, I have the necessary functions that return a Stream
for the class
I need.在我的 firestore class
,我有必要的函数可以为我需要的class
返回一个Stream
。 I only pasted the function for the Family
, but same code for AdultProfile
with minor path changes.我只粘贴了Family
的函数,但为AdultProfile
粘贴了相同的代码,但路径变化很小。
// firebase_firestore.dart
Stream<Family> streamFamilyInfo() {
try {
return familyInfo(FirebaseAuthService().currentUser!.uid).snapshots().map(
(snapshot) {
return Family.fromMap(snapshot.data() as Map<String, dynamic>);
},
);
} catch (e) {
FirebaseAuthService().signOut();
rethrow;
}
}
This is where most of the work happens:这是大部分工作发生的地方:
// family_store.dart
class FamilyStore extends ChangeNotifier {
List<AdultProfile>? adults = [];
StreamGroup? streamGroup = StreamGroup();
FamilyStore() {
// Handle the stream as they come in
streamGroup!.stream.listen((event) {
_handleStream(event);
});
}
void handleStream(streamEvent) {
// Deal with the stream as they come in
// for the different instances of the class
// you may have in the data structure
if (streamEvent is AdultProfile) {
int index = adults!.indexWhere((element) => element.uid == streamEvent.uid);
if (index >= 0) {
adults![index] = streamEvent;
} else {
adults!.add(streamEvent);
}
notifyListeners();
}
// This is the function called from the
// ChangeNotifierProxyProvider in main.dart
void updateFamily(Family newFamily) {
_updateAdults(newFamily.adults);
}
void _updateAdults(List<String> newAdults) async {
if (newAdults.isEmpty) return;
// Generate list of comparisons so you can add/remove
// streams from StreamGroup and the array of classes
Map<String, List<String>> updateLists =
_createAddRemoveLists(adults!.map((profile) => profile.uid).toList(), newAdults);
for (String uid in updateLists['add']!) {
// Add the stream for the instance of the
// AdultProfile to the StreamGroup
(streamGroup!.add(
FirebaseFireStoreService().streamAdultProfile(uid),
));
}
for (String uid in updateLists['remove']!) {
// Remove the stream for the instance of the
// AdultProfile from the StreamGroup
streamGroup!.remove(
FirebaseFireStoreService().streamAdultProfile(uid),
);
// Also remove it from the array
adults!.removeWhere((element) => element.uid == uid);
notifyListeners();
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.