[英]How do I use a StreamProvider from a StateNotifierProvider?
[英]How to set multiple StateNotifierProvider (s) with dynamicaly loaded async data?
我完全堅持下面的任務。 所以,我們的想法是使用Riverpod
解決這些步驟
在暫停應用程序時使用某種Future async
從數據庫中獲取數據(顯示SomeLoadingPage()
等)
數據加載后:
2.1 初始化多個全局StateNotifierProvider
s,它們利用其構造函數中的數據,並且可以通過更新其狀態的方法在整個應用程序中進一步使用。
2.2 然后顯示MainScreen()
和UI的rest
到目前為止,我已經嘗試過這樣的事情:
class UserData extends StateNotifier<AsyncValue<Map>> { // just <Map> for now, for simplicity
UserData() : super(const AsyncValue.loading()) {
init();
}
Future<void> init() async {
state = const AsyncValue.loading();
try {
final HttpsCallableResult response =
await FirebaseFunctions.instance.httpsCallable('getUserData').call();
state = AsyncValue.data(response.data as Map<String, dynamic>);
} catch (e) {
state = AsyncValue.error(e);
}}}
final userDataProvider = StateNotifierProvider<UserData, AsyncValue<Map>>((ref) => UserData());
final loadingAppDataProvider = FutureProvider<bool>((ref) async {
final userData = await ref.watch(userDataProvider.future);
return userData.isNotEmpty;
});
class LoadingPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return FutureBuilder(
future: ref.watch(loadingAppDataProvider.future),
builder: (ctx, AsyncSnapshot snap) {
// everything here is simplified for the sake of a question
final Widget toReturn;
if (snap.connectionState == ConnectionState.waiting) {
toReturn = const SomeLoadingPage();
} else {
snap.error != null
? toReturn = Text(snap.error.toString())
: toReturn = const SafeArea(child: MainPage());
}
return toReturn;},);}}
我有意使用FutureBuilder
而不是.when()
因為將來我可能打算將Future.wait([])
與多個期貨一起使用
到目前為止這是有效的,但是當我想在UserData
中實現某種 update() 方法並通過整個應用程序監聽它的變量時,麻煩就來了。 就像是
late Map userData = state.value ?? {};
late Map<String, dynamic> settings = userData['settings'] as Map<String, dynamic>;
void changeLang(String lang) {
print('change');
for (final key in settings.keys) {
if (key == 'lang') settings[key] = lang;
state = state.whenData((data) => {...data});
}
}
SomeLoadingPage()
出現在每個changeLang()
方法調用中。
簡而言之:我真的希望有幾個StateNotifierProvider
能夠從內部修改它們的 state 並從外部收聽它。 但是從數據庫中獲取初始 state 並讓整個應用程序等待獲取此數據並初始化這些提供程序。
所以,我想我想出了如何解決這個問題:
final futureExampleProvider = FutureProvider<Map>((ref) async {
final HttpsCallableResult response =
await FirebaseFunctions.instance.httpsCallable('getUserData').call();
return response.data as Map;
});
final exampleProvider = StateNotifierProvider<Example, Map>((ref) {
// we get AsyncValue from FutureNotifier
final data = ref.read(futureExampleProvider);
// and wait for it to load
return data.when(
// in fact we never get loading state because of FutureBuilder in UI
loading: () => Example({'loading': 'yes'}),
error: (e, st) => Example({'error': 'yes'}),
data: (data) => Example(data),
);
});
class LoadingPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return FutureBuilder(
// future: ref.watch(userDataProvider.future),
future: ref.watch(futureExampleProvider.future),
builder: (ctx, AsyncSnapshot snap) {
final Widget toReturn;
if (snap.data != null) {
snap.error != null
? toReturn = Text(snap.error.toString())
: toReturn = const SafeArea(child: MainPage());
} else {
// this is the only 'Loading' UI the user see before everything get loaded
toReturn = const Text('loading');
}
return toReturn;
},
);
}
}
class Example extends StateNotifier<Map> {
Example(this.initData) : super({}) {
// here comes initial data loaded from FutureProvider
state = initData;
}
// it can be used further to refer to the initial data, kinda like cache
Map initData;
// this way we can extract any parts of initData
late Map aaa = state['bbb'] as Map
// this method can be called from UI
void ccc() {
// modify and update data
aaa = {'someKey':'someValue'};
// trigger update
state = {...state};
}
}
這對我有用,至少在這個復雜度上是這樣。 如果有更好的建議,我會留下未解決的問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.