rrousselgit / riverpod Goto Github PK
View Code? Open in Web Editor NEWA reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze.
Home Page: https://riverpod.dev
License: MIT License
A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze.
Home Page: https://riverpod.dev
License: MIT License
Since Riverpod does not depend on Flutter, we could use Riverpod in AngularDart
Is your feature request related to a problem? Please describe.
I'm refactoring a small app I have to use river_pods but one thing I keep wanting is a way to combine providers in a when
or maybeWhen
so they both get loaded at the same time in the case they're asynchronous.
Describe the solution you'd like
// Both userProvider and profileProvider are `StreamProvider`s
return useProvider2(userProvider, profileProvider).when(
data: (user, profile) {
// Logged in
},
loading: () {},
error: (_, __) {}
);
Or, another example, all possible badges (pulled from a json file) and the user's current badges.
// userBadgesProvider is a StreamProvider and badgesProvider is a FutureProvider
return useProvider2(userBadgesProvider, badgesProvider).when(
data: (userBadges, allBadges) {
// Show list of a user's current badges in list of all possible badges
},
loading: () {},
error: (_, __) {}
);
Describe alternatives you've considered
I can do what I want by checking both the .data
values on the providers to check if they're null but then I lose out on the error case of when
:
final user = useProvider(userProvider).data;
final profile = useProvider(profileProvider).data;
final isLoggedIn = user != null && profile != null;
Or for the other example, I currently do this:
useProvider(userBadgesProvider).when(
data: (userBadges) {
final badges = useProvider(badgesProvider).maybeMap<List<Badge>>(
data: (asyncData) => asyncData.data.value,
orElse: () => [],
);
},
loading: () {},
error: (_, __) {},
),
Additional context
hooks_riverpod: 0.6.0-dev
[✓] Flutter (Channel dev, 1.21.0-1.0.pre, on Mac OS X 10.15.5 19F101, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
[✓] Xcode - develop for iOS and macOS (Xcode 11.6)
[✓] Chrome - develop for the web
[✓] Android Studio (version 3.6)
[✓] Connected device (3 available)
Firstly, thanks a thousand times fot these great packages.
I wanted to move my entire project from provider to riverpod. But i’m stuck at this point.
class EditQuestionScreen extends StatelessWidget {
EditQuestionScreen({this.question, this.answers});
final QuestionModel question;
final List<AnswerModel> answers;
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => QuestionProvider(
question: this.question,
answers: this.answers),
child: Container());
}
}
This is my provider widget for subwidgets. Its initialize only once. How can I move this class to Hook Widget with riverpod?
Describe the bug
StateProvider.family.autoDispose
does not exist
Version: 0.6.0-dev+2
What should I do to pass events from provider to view? For example from the provider I want to pass an event that displays a dialog or a snackbar.
Is it possible to access an Computed
provider in initState
?
StateNotifier is not avaiable for use.
Future<void> _showDialogAddGhiChu(BuildContext context) async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (_) {
return Consumer((ct, read) {
final clickDate = read(mainStateNotifier).clickDate;
return AddNote(
date: clickDate,
context: ct,
); //magic ^_^
});
},
);
}
error
Another exception was thrown: Stack Overflow
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following StackOverflowError was thrown building Consumer(dirty, dependencies: [ProviderStateOwnerScope], state: _ConsumerState#34eeb):
Stack Overflow
The relevant error-causing widget was:
Consumer file:///Users/tbm98/dev/flutter_app/student-social/lib/presentation/screens/main/main_page.dart:132:16
When the exception was thrown, this was the stack:
#0 MainState.getClickDate (package:studentsocial/presentation/screens/main/main_state.dart:35:3)
#1 MainState.getClickDate (package:studentsocial/presentation/screens/main/main_state.dart:35:32)
#2 MainState.getClickDate (package:studentsocial/presentation/screens/main/main_state.dart:35:32)
#3 MainState.getClickDate (package:studentsocial/presentation/screens/main/main_state.dart:35:32)
#4 MainState.getClickDate (package:studentsocial/presentation/screens/main/main_state.dart:35:32)
...
════════════════════════════════════════════════════════════════════════════════════════════════════
Im using flutter_riverpod: ^0.6.0-dev+3 in my project, and i can't find the computed method !
in flutter_riverpod: ^0.5.1 i can find the computed method but in dev i could not find even if i use flutter LSP throws me an error
why is that? please help me if i'm wrong!
Hello and thanks for reading!
This issue is about slightly changing the syntax of how provider variants are created.
Feel free to comment and share your opinion on the matter!
Currently, we have many classes, which are all basically the same modulo a small name + prototype change.
The current syntax is a permutation of:
– | State | Provider | – |
AutoDispose | ChangeNotifier | Family | |
Future | |||
Stream | |||
StateNotifier |
For a total of 24 variants.
With a secondary table for Computed
.
But that's not very ideal:
AutoDisposeChangeNotifierProviderFamily
The last point is especially important, as ideally, I would like to add new variants:
Future
/Stream
in a separate column, such that we could have a FutureStateProvider
for exampleWhich means the table of variants would become:
– | – | – | – | Provider | _ |
AutoDispose | Retry | Future | StateNotifier | Family | |
Stream | State | ||||
ChangeNotifier |
That leads to a ridiculous 128 providers
The idea is to merge all the variants into two public classes: Provider
and Computed
.
Riverpod would still have 128 providers internally, but they would be code-generated so it doesn't matter for end-users.
The way this new syntax would work is using the Builder design pattern.
Basically, instead of:
final provider = AutoDisposeChangeNotifierProviderFamily<MyNotifier, int>((ref, id) {
return MyNotifier();
});
We would have:
final provider = Provider
.changeNotifier
.autoDispose
.family<MyNotifier, int>((ref, id) {
return MyNotifier();
});
Whereas:
final provider = AutoDisposeProvider<Object>((ref) => Object());
would become:
final provider = Provider.autoDispose<Object>((ref) => Object());
And Provider((ref) => Object())
would be untouched.
The difference:
package:riverpod/providers.dart
).stream
and .autoDispose
.Should some provider variants stay as is, such that we would still have StateProvider(...)
instead of Provider.state(...)
?
If so, what are the providers that we want to keep this way? I'm thinking of:
And then for fancier versions like FutureStateNotifierProvider
would become StateNotifierProvider.future
.
Using the read
method inside of a provider body is discouraged according to the docs, but the reason for this is unclear to me.
Is there a discernible difference in behavior between the following snippets?
final myServiceProvider = Provider((ref.read) => MyService(ref.read));
//initialization within constructor
MyService(Reader read)
: api=read(apiProvider);
final myServiceProvider = Provider(
(ref) => MyService(api: ref.read(apiProvider)));
I don't have as much of an issue with passing the reader given that riverpod is compile safe, but constructor injection does have the advantage of making dependencies more explicit. Is this indeed an anti-pattern and if so why?
The todos example shows adding and dismissing todos on a single screen. It is quite clear how the state of the todo list is manipulated in this example.
Suppose however that the main todo list screen only shows todos coming up in the next week. The state notifier TodoList only loads this subset of todos from the data source for performance reasons.
Suppose further that the user can push another screen on to the navigation stack to show a different list of todos where there might be some overlap with the first list – some of those todos might be in the main screen's TodoList, some might not. This presumably requires another TodoList state notifier instance which loads that second set of todos.
The question arises of how to update state on the first screen when a todo is changed on the second screen. The second screen's TodoList state notifier will update its state and thus reflect the state change on the second screen.
What would be the best practise to propagate this state change to the TodoList's of both screens? Or is this thinking about it the wrong way and that you would instead have a single TodoList that changes its internal list according to the screen navigation?
I appreciate that this question relates more to state notifiers than provider, but I thought to pose it here as StateNotifier seems an important part of Riverpod.
In todos example , to only update property completed we should defined another property again . It's possible to only updated only needed property ? something like this :
state = [
for (final todo in state)
if (todo.id == id)
Todo(
completed: !todo.completed,
)
else
todo,
];
already try this approach,but make property another completed null. Is this really the way it works?
Because i have alot of property and only need update 1 of them. But i should defined another property again.
state = [
for (final item in state)
if (item.idUtang == idUtang)
UtangModel(
idUtang: item.idUtang,
pembertang: item.pembertang,
pengutang: item.pengutang,
totalUtang: item.totalUtang,
sisaUtang: item.sisaUtang,
status: '1',
keterangan: item.keterangan,
tglKembali: item.tglKembali,
selfie: item.selfie,
createdDate: item.createdDate,
ttd: item.ttd,
)
else
item
];
Thank's.
Should I use only one StateNotifierProvider for per screen? Or is it a split state for multiple providers?
Describe the bug
As you described this, I tried to use 0.6.0-dev, but marvel example app is broken at ProviderScope(overrides:)
.
But the dartdoc and examples are up-to-date
#49 (comment)
The app doesn't work and error occurs.
Error log:
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following UnsupportedError was thrown building NotificationListener<KeepAliveNotification>:
Unsupported operation: Cannot override providers on a non-root ProviderContainer/ProviderScope
When the exception was thrown, this was the stack:
#0 new ProviderContainer (package:riverpod/src/framework/container.dart:51:7)
#1 ProviderScopeState.initState (package:flutter_riverpod/src/framework.dart:166:17)
#2 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4695:58)
#3 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4531:5)
... Normal element mounting (33 frames)
#36 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3501:14)
#37 Element.updateChild (package:flutter/src/widgets/framework.dart:3260:18)
#38 SliverMultiBoxAdaptorElement.updateChild (package:flutter/src/widgets/sliver.dart:1157:36)
#39 SliverMultiBoxAdaptorElement.createChild.<anonymous closure> (package:flutter/src/widgets/sliver.dart:1142:20)
#40 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2620:19)
#41 SliverMultiBoxAdaptorElement.createChild (package:flutter/src/widgets/sliver.dart:1135:11)
#42 RenderSliverMultiBoxAdaptor._createOrObtainChild.<anonymous closure> (package:flutter/src/rendering/sliver_multi_box_adaptor.dart:356:23)
#43 RenderObject.invokeLayoutCallback.<anonymous closure> (package:flutter/src/rendering/object.dart:1879:58)
#44 PipelineOwner._enableMutationsToDirtySubtrees (package:flutter/src/rendering/object.dart:927:15)
#45 RenderObject.invokeLayoutCallback (package:flutter/src/rendering/object.dart:1879:13)
#46 RenderSliverMultiBoxAdaptor._createOrObtainChild (package:flutter/src/rendering/sliver_multi_box_adaptor.dart:345:5)
#47 RenderSliverMultiBoxAdaptor.addInitialChild (package:flutter/src/rendering/sliver_multi_box_adaptor.dart:429:5)
#48 RenderSliverGrid.performLayout (package:flutter/src/rendering/sliver_grid.dart:550:12)
#49 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#50 RenderSliverEdgeInsetsPadding.performLayout (package:flutter/src/rendering/sliver_padding.dart:137:11)
#51 RenderSliverPadding.performLayout (package:flutter/src/rendering/sliver_padding.dart:377:11)
#52 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#53 RenderViewportBase.layoutChildSequence (package:flutter/src/rendering/viewport.dart:471:13)
#54 RenderViewport._attemptLayout (package:flutter/src/rendering/viewport.dart:1465:12)
#55 RenderViewport.performLayout (package:flutter/src/rendering/viewport.dart:1374:20)
#56 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#57 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:13)
#58 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#59 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:13)
#60 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#61 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:13)
#62 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#63 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:13)
#64 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#65 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:13)
#66 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#67 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:13)
#68 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#69 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:13)
#70 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#71 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:13)
#72 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#73 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:13)
#74 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#75 MultiChildLayoutDelegate.layoutChild (package:flutter/src/rendering/custom_layout.dart:173:11)
#76 _ScaffoldLayout.performLayout (package:flutter/src/material/scaffold.dart:497:7)
#77 MultiChildLayoutDelegate._callPerformLayout (package:flutter/src/rendering/custom_layout.dart:242:7)
#78 RenderCustomMultiChildLayoutBox.performLayout (package:flutter/src/rendering/custom_layout.dart:401:14)
#79 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#80 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:13)
#81 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#82 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:13)
#83 _RenderCustomClip.performLayout (package:flutter/src/rendering/proxy_box.dart:1269:11)
#84 RenderObject.layout (package:flutter/src/rendering/object.dart:1776:7)
#85 RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:115:13)
#86 RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:1639:7)
#87 PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:896:18)
#88 RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:404:19)
#89 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:881:13)
#90 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:286:5)
#91 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1117:15)
#92 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1056:9)
#93 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:972:5)
#97 _invoke (dart:ui/hooks.dart:253:10)
#98 _drawFrame (dart:ui/hooks.dart:211:3)
(elided 3 frames from dart:async)
To Reproduce
At https://github.com/rrousselGit/river_pod/tree/v0.6.0-dev, runexamples/marvel/lib/main.dart
.
Expected behavior
The app works without any errors.
I follow source code from documentation :
class Counter extends StateNotifier<int> {
Counter(): super(0);
void increment() => state++;
}
final counterProvider = StateNotifierProvider((ref) => Counter());
// ...
@override
Widget build(BuildContext context) {
// Obtains Counter without listening to its state.
// Will not cause the button to rebuild when the counter changes.
final Counter counter = useProvider(counterProvider);
return RaisedButton(
onPressed: () => counter.increment(),
child: Text('increment'),
);
}
Then i want custom it , when i click button i want show circularprogressindicator , after 3 second showing the text. So i already custom the example documentation to this :
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:state_notifier/state_notifier.dart';
class IsLoading extends StateNotifier<bool> {
IsLoading() : super(false);
void toggleLoading(bool value) => state = value;
}
final isLoadingProvider = StateNotifierProvider((ref) => IsLoading());
class TestingApp extends HookWidget {
@override
Widget build(BuildContext context) {
final IsLoading isLoading = useProvider(isLoadingProvider);
return Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: isLoading.state ? CircularProgressIndicator() : Text('This Is Your Data'),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: FlatButton(
onPressed: () => showLoading(isLoading), child: Text('Toggle Loading')),
),
],
),
),
);
}
void showLoading(IsLoading isLoading) async {
isLoading.toggleLoading(true);
print("Proses");
await Future.delayed(const Duration(seconds: 3));
print("Done");
isLoading.toggleLoading(false);
}
}
But i got this warning and although i click button , it not showing the circularprogressindicator:
The member 'state' can only be used within instance members of subclasses of 'package:state_notifier/state_notifier.dart'.dart(invalid_use_of_protected_member)
I missed something ?
I love how concise and safe the behavior of AsyncValue is for handling success, loading, and error states. However, when loading only takes a split second, it looks a bit jumpy when going from the loading state to the success state.
I was wondering if having an optional parameter to the loading function allowing for a minimum time spent loading would be a good idea just so things don't appear as jittery once the data is loaded.
Prior to adopting Riverpod, I was using a FutureBuilder on my splash screen to force it to wait 2 seconds before displaying the app content. That doesn't quite work when the parent context is responsible for where the user is routed.
In this example, the splash screen is shown for around 100ms before being redirected to the Login or Home page:
return authProvider.when(
data: (user) => user == null ? Login() : Nav(),
loading: () => Splash(),
error: (err, stack) => Center(child: Text('Error: $err')),
);
my code:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'events/event_base.dart';
import 'events/loading_event.dart';
import 'events/show_snackbar_event.dart';
final eventProvider = StateProvider<EventBase>((ref) => null);
void main() {
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulHookWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void onChange(StateController<EventBase> event) {
print(event.state);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ProviderListener(
provider: eventProvider,
onChange: onChange,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read(eventProvider).state = LoadingEvent();
context.read(eventProvider).state = ShowSnackbarEvent();
},
tooltip: 'Increment',
child: Icon(Icons.add),
));
}
}
Expected:
flutter: Instance of 'LoadingEvent'
flutter: Instance of 'ShowSnackbarEvent'
actually
flutter: Instance of 'ShowSnackbarEvent'
LoadingEvent is skiped.
Is your feature request related to a problem? Please describe.
Given data classes of Post
and Comment
, Riverpod works great wiring up their repositories' "boot" dependencies (a tree-like structure):
hiveProvider <- postsHiveBoxProvider <- postsRepositoryProvider
hiveProvider <- commentsHiveBoxProvider <- commentsRepositoryProvider
Post
s, Comment
s and other models, however, depend on each other. These relationships are better represented with a directed graph data structure, which can be navigated like:
final post = await ref.read(postsRepositoryProvider).getPost();
post.comments.first.post.value.comments.first...
Since postsRepository
needs to hold a reference to commentsRepository
, once ref.read(commentsRepositoryProvider)
is called I get a CircularDependencyError
.
Describe the solution you'd like
Allow disabling the circular reference assertion for legitimate cases like Providers in graph-like structures.
ref.read(commentsRepositoryProvider, circular: true)
Perhaps there are alternatives (using cached providers)? I not too familiar with Riverpod
A common use-case in modern applications is a "retry" feature – either refresh a feed or some request failed.
Thanks to providers being declarative, it is possible to have built-in support for such feature.
First, this would require #39 to make the syntax reasonable.
This would consist of the following things:
A MyProvider.retry((ref) {...})
modifier, to mark a state as retryable.
This would wrap the exposed object in a Retry<T>
.
For example, if we have:
final provider = FutureProvider((ref) async => 42);
Widget build(context) {
AsyncValue<int> value = useProvider(provider);
}
Then using the .retry
modifier, we would have:
final provider = FutureProvider.retry((ref) async => 42);
Widget build(context) {
Retry<AsyncValue<int>> value = useProvider(provider);
}
Where Retry
is:
abstract class Retry<T> {
T get value.
Future<T> retry();
}
The .retry
modifier would be specific to Future
& StreamProvider
only
The UI can then freely call Retry.retry()
, which would destroy the previous state and re-create a new state for the given provider.
The result of Retry.retry
is a Future<T>
that resolves when the state is re-created
The constructor of a .retry
provider would expose an parameter to define what state should be displayed during the AsyncValue.data
to AsyncValue.data
transition:
AsyncValue.loading
This parameter could be named gaplessPlayback
, which is the named used by the Image
widget for the same behavior.
We could also have an enum.
A provider marked with .rety
can hook-up on the retry event to perform some clean-up using ref
:
final provider = FutureProvider.retry((ref) async {
ref.onRetry(() {
// TODO: clear cache, so that the next request doesn't simply return the same result.
});
return 42;
});
When using ref.read(futureProvider)
, the value obtained is always a Stream<T>
, even for FutureProvider.retry
– as the value exposed by a retryable FutureProvider
can change over time.
All in all a typical usage would be a FutureProvider
that performs an HTTP request.
Then, the UI would expose a way for users to restart the request, such as with a pull-to-refresh or a "retry" button.
In code, this would look like this:
class Todos {
static final provider = FutureProvider.retry(
(ref) async {
final repository = ref.read(Repository.provider);
return repository.fetchTodos();
},
// during retry, keep showing the previous state
gaplessPlayback: true,
);
}
...
Widget build(context) {
final todos = useProvider(User.provider);
return todos.value.when(
loading: () => const CircularProgressIndicator(),
error: (err, stack) {
return Column(
children: [
Text('Error $err'),
RaisedButton(
onPressed: () => user.retry(),
child: Text('retry'),
),
],
);
},
data: (todos) {
return RefreshIndicator(
onRefresh: () => user.retry(),
child: ListView(
children: [
for (final todo in todos) TodoItem(todo: todo),
],
),
);
},
);
}
Given that async operations are quite common, I think it would be helpful to add a simple search functionality to the Marvel example that would debounce the input query.
How do you approach such functionality, if possible, in a reusable way?
Sorry if this would fit better in the state_notifier or hooks package. I felt like asking because of the Marvel project 😄
Congratulations on all the packages @rrousselGit !
I have simple example to search/ filter data in state , my type data state is List [UtangModel]. I can filtered list depending of what user type.
The problem is , although i success filtered the data but i lost data not what i type and it can't restore again. How best practice to implement search using state ?
void filterList(String query) {
var fullList = [...state];
var tempList = [];
if (query.isEmpty) {
tempList = [];
} else {
for (final item in fullList) {
if (item.pengutang.nameUser.toLowerCase().contains(query.toLowerCase())) {
tempList.add(item);
}
}
}
state = [...tempList];
print('Total Filtered list ${fullList.length}');
}
It's how i call the function into onChanged textfield
TextFormFieldCustom(
onSaved: (value) => '',
onChanged: (value) => utangProvider.read(context).filterList(value),
prefixIcon: Icon(Icons.search),
hintText: 'Cari...',
),
After reading carefully on docs , I think i can implement it using Computed.Family . So i changed previous code to this :
final showFilteredList = Computed.family<List<UtangModel>, String>((read, query) {
final utang = read(utangProvider.state);
// var tempList = [...utang];
return utang
.where((element) => element.pengutang.nameUser.toLowerCase().contains(query.toLowerCase()))
.toList();
});
this how i call in UI
Consumer((ctx, read) {
final utang = read(showFilteredList(_queryController.text));
return ListView.separated(
shrinkWrap: true,
itemCount: utang.length,
separatorBuilder: (BuildContext context, int index) {
return Divider();
},
itemBuilder: (BuildContext context, int index) {
final result = utang[index];
return Card(
child: Center(
child: Text(result.pengutang.nameUser),
),
);
},
);
}),
But i get same result from previous code, I missing something ?
Describe the bug
it is happened.
Running "flutter pub upgrade" in my_app...
Because hooks_riverpod >=0.1.0 depends on flutter_hooks ^0.10.0 and my_app depends on flutter_hooks ^0.11.0, hooks_riverpod >=0.1.0 is forbidden.
So, because my_app depends on hooks_riverpod ^0.3.0, version solving failed.
pub upgrade failed (1; So, because my_app depends on hooks_riverpod ^0.3.0, version solving failed.)
To Reproduce
Modify pubspec.yaml
below and excute flutter pub upgrade
.
environment:
sdk: ">=2.7.0 <3.0.0"
flutter: ">= 1.17.0"
dependencies:
flutter:
sdk: flutter
flutter_hooks: ^0.11.0
hooks_riverpod: ^0.3.0
<Please add a small sample to that can be executed to reproduce the problem. As a general rule, 100 lines is the maximum>
it is about yaml
. So, no code.
Expected behavior
downgrade flutter_hooks works, but it opponents for document.
# flutter_hooks: ^0.11.0
flutter_hooks: ^0.10.0
hooks_riverpod: ^0.3.0
The doc only has an example for flutter_hooks useProvider
. But I only want to use flutter_riverpod
's Consumer
only.
I tried this but the view is not updated when the data is changed:
class SomeState with ChangeNotifier {
String data = '';
void getData() async {
data = 'abc';
notifyListeners();
}
}
final stateProvider = ChangeNotifierProvider<SomeState>((_) => SomeState());
// ...
stateProvider.read(context).getData();
// ...
return Consumer((_, read) {
final state = read(stateProvider);
return Text(state.data);
})
I was reading the ChangeNotifierProvider documentation and I found out the following note:
Note: By using Riverpod, ChangeNotifier will no-longer be O(N^2) for dispatching notifications and O(N) for adding/removing listeners.
Could you maybe specify something more about that statement? Is it better by using Riverpod, worse, or?
I would like to see a more detailed example with StreamProvider in the docs. I think in the flutter / firebase context, a really nice example would be the equivalent of this from the provider way of things:
StreamProvider<FirebaseUser>.value(
value: FirebaseAuth.instance.onAuthStateChanged);
translated to River Pod, it might look like this, but i could be wrong (since i am trying to clean up here, its not 1:1 translation as is):
final firebaseAuthProvider = StreamProvider<FirebaseUser>((ref) {
ref.onDispose(() {
// Closes the StreamController when the state of this provider is destroyed.
FirebaseAuth.instance.signOut();
});
return FirebaseAuth.instance.onAuthStateChanged;
});
Please correct me if i am wrong on this.
I got Instance of 'CircularDependencyError'
but I don't know the reason for this error.
I have 2 providers that use each other
final calendarStateNotifier = StateNotifierProvider((ref) {
return CalendarStateNotifier(
ref.read(scheduleStateNotifier).value, ref.read(mainStateNotifier).value);
});
and
final scheduleStateNotifier = StateNotifierProvider((ref) {
return ScheduleStateNotifier(ref.read(calendarStateNotifier).value);
});
I was taking a look at the progress in the new branch merge_computed_and_provider
and I noticed that the commit 0431576 has removed the getter for the ProviderReference
in the ProviderContainer
class, breaking the Marvel example test in the process, which was doing the following:
final container = ProviderContainer();
final client = FakeDio();
final repository = MarvelRepository(container.ref, client: client);
I was wondering what would be the new way of doing this inside the tests now.
I'm sure you are planning to update/fix the examples, but I was just curious 😅
Describe the bug
When creating a StreamProvider with a value of Stream.value(null)
and then override it in another page then this should only be null until it is set. Right now it seems like it resets when navigating with an animation.
Have added a deugPrint(user.name) to easily see the behavior in the console.
To Reproduce
Click the ListTile and look at the log.
class User {
String id;
String name;
User.empty() {
id = "";
name = "";
}
User.other() {
id = "12345";
name = "Tester";
}
@override
bool operator ==(Object other) => identical(this, other) || other is User && runtimeType == other.runtimeType && name == other.name;
@override
int get hashCode => name.hashCode;
}
final userProvider = StreamProvider<User>((ref) {
return Stream.value(User.empty());
});
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ProviderScope(
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: PageOne(),
),
);
}
}
class PageOne extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ProviderScope(
// This override is causing the issue.
// If this override would be at the material app
// then the debugPrint will always print Tester
overrides: [
userProvider.overrideAs(
StreamProvider(
(ref) => Stream.value(User.other()),
),
),
],
child: Scaffold(
body: OpenContainer(
closedElevation: 4,
closedBuilder: (BuildContext context, VoidCallback openContainer) {
return Consumer((context, read) {
final user = read(userProvider).data == null ? User.empty() : read(userProvider).data.value;
debugPrint(user.name);
return Container(
height: 100,
child: ListTile(
title: Text(user.name),
),
);
});
},
openBuilder: (context, __) {
return Scaffold();
},
),
),
);
}
}
Do not forget to add dependencies in .yaml
animations: ^1.1.0
flutter_riverpod: ^0.1.0
Expected behavior
When running this the debugPrint(user.name) should not be empty during navigation.
Describe what scenario you think is uncovered by the existing examples/articles
There is a simple use case. I want to create a todoList stateNotifierProvider that is initialized with todos fetched from API. If I fetch the todos on a initialize function inside the stateNotifier class I can't get the states of an asyncValue from a futureProvider (loading, error and data).
Describe why existing examples/articles do not cover this case
There aren't examples covering this type of cases where stateNotifierProviders are needed, to create a state and some logic to change this state, but getting the benefits of an asyncValue states returned by a futureProvider.
Is your feature request related to a problem? Please describe.
I'm working on an app that uses riverpod and I'm finding that it's annoying and limiting that the only place where I can mark a Provider
as needing to be refreshed / triggering it to be refreshed is from within the widget tree. In other words, I have to be somewhere that has a BuildContext
in scope so that I can do context.refresh(someProvider)
.
For context, I'm building an app for storing recipes. One of the things you can do is delete a recipe. I am managing the lifecycle of deleting a recipe with a StateNotifier
called DeleteRecipeNotifier
. The notifier is pretty trivial. It roughly looks like this:
class DeleteRecipeNotifier extends StateNotifier<AsyncValue<bool>> {
final Api api;
DeleteRecipeNotifier({@required this.api}) : super(AsyncValue.data(false));
Future<void> deleteRecipe(String id) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
await api.deleteRecipe(id);
return true;
});
}
}
This is paired with some UI for showing a bottom sheet with a confirm button and a loading indicator while the delete is happening. When the delete is successful, the bottom sheet is closed and the UI pops back to a different screen.
(forgive me for blending hooks and non-hooks here 😄 i'm exploring some different approaches)
class _DeleteSheet extends HookWidget {
final Recipe recipe;
const _DeleteSheet({
Key key,
@required this.recipe,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ProviderListener(
onChange: (AsyncValue<bool> result) {
if (result.data?.value ?? false) {
context.refresh(recipesProvider);
// close the bottom sheet and also pop back out of the current "view recipe screen"
Nav.of(context)..pop()..pop();
}
},
provider: deleteRecipeProvider.state,
child: useProvider(deleteRecipeProvider.state).when(
data: (_) => _ConfirmDelete(recipe: recipe),
loading: () => Center(child: CircularProgressIndicator()),
error: (e, __) => Center(child: Text(e.toString())),
),
);
}
}
Describe the solution you'd like
What I'd love to be able to do is trigger a refresh of the recipesProvider
from within my DeleteRecipeNotifier
. Either by constructing it with a ProviderReference
or passing in a lambda like refreshRecipes: () => ref.refresh(recipesProvider)
to keep ProviderReference
out of the notifier.
Describe alternatives you've considered
Alternatively, I can keep doing what I'm doing in the example code above: listen to the notifier and trigger a refresh from the widget tree. If you've got a strong reason why I should prefer this approach, I'd love to hear more! I'm sure you have a philosophy around how this stuff should be done. I'm open to all sorts of ideas, and I totally accept that I might be thinking about this wrong.
Thanks!
Thank you very much for another great package. Is it possible to use Riverpod as a complete replacement for Provider to use in combination with MobX? Can it solve store-to-store communication problem? Any example or documentation?
Describe the bug
Riverpod Version -> 0.6.0-dev+3
Context
The task is to listen to two Providers namely FirebaseAuthProvider
and a PageControllerProvider
., use the data to determine: continue being in the IntroFlow or show the HomePage.
Snippet for the Provider:
https://github.com/preetjdp/YouOweMe/blob/5e51fb2f6132e637ad447273419f4360a27ad1d4/mobileApp/lib/ui/IntroFlow/providers.dart#L18-L45
The Problem
When the user sign's in, i.e., the FirebaseUserProvider
returns a new value the widget where the data is being used gets rebuild.,
The Snippet where the provider is used:
https://github.com/preetjdp/YouOweMe/blob/5e51fb2f6132e637ad447273419f4360a27ad1d4/mobileApp/lib/ui/IntroFlow/introFlow.dart#L22-L45
My Hypothesis
I believe the provider gets reset since I see the loading widget on the screen.
You probably already have this on your todo list, but I think documenting these, especially for how to use them for unit tests would be very good to have, as all of the tests in the examples so far don't seem to make use of this.
Is your feature request related to a problem? Please describe.
I cant find a way to add a provider based on the value of a stream (in this case, the value of a firebase auth).
Describe the solution you'd like
Take this from provider package. This widgets sits on top of MaterialApp and get a builder which in fact is the whole app. So its a way to "inject" providers (here a firestore db provider with a valid connection based on user credentials) after a successful login.
class AuthWidgetBuilder extends StatelessWidget {
const AuthWidgetBuilder({Key key, @required this.builder}) : super(key: key);
final Widget Function(BuildContext, AsyncSnapshot<User>) builder;
@override
Widget build(BuildContext context) {
final authService = Provider.of<AuthService>(context, listen: false);
return StreamBuilder<User>(
stream: authService.onAuthStateChanged,
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
final User user = snapshot.data;
if (user != null) {
return MultiProvider(
providers: <SingleChildCloneableWidget>[
Provider<User>.value(value: user),
ProxyProvider<User, FirestoreDatabase>(
update: (_, User user, __) => FirestoreDatabase(uid: user.uid),
)
],
child: builder(context, snapshot),
);
}
return builder(context, snapshot);
},
);
}
}
Additional context
I think "emulating" ProxyProvider in Riverpod should be quite easy with the "ref" variable but the whole concept of conditional providers is not clear. Perhaps i am missing something very basic here.
https://riverpod.dev currently does not mention context/container.refresh
at all.
It would be helpful to have a pull-to-refresh cookbook, showcasing context.refresh
in use.
Is your feature request related to a problem? Please describe.
I want to pass event from provider
to widget
issue related: #21
Describe the solution you'd like
Create a widget like BlocListener
.
It can pass events to widget then I can do something like navigation, showDialog ......
Current solution
Create a Stream in Provider and listen it in StatefulWidget or Hooks widget.
Is your feature request related to a problem? Please describe.
Not sure if it should be a bug or a feature request, but I can't read the original Provider
from Override
.
For example:
final nameProvider = Provider<String>((ref) => 'John Doe');
final emphasisProvider = Provider<String>((ref) {
final name = ref.read(nameProvider).value;
return '$name!';
});
// ...
ProviderScope(
child: Column(
children: <Widget>[
// Displays `John Doe`
Consumer((context, read) => Text(read(nameProvider))),
// Displays `John Doe!`
Consumer((context, read) => Text(read(emphasisProvider))),
ProviderScope(
overrides: [
nameProvider.overrideAs(emphasisProvider),
],
// Displays `null!` instead of `John Doe!`
child: Consumer((context, read) => Text(read(nameProvider))),
),
],
),
)
Describe the solution you'd like
Read original Provider
value instead of returning null.
Would you like to tell me how to dispose Provider when a page is popped, or any solution to get an initialized provider every time user open a page?
I knew Provider is disposed when its owner is disposed, and tried to prepare multi ProviderScope
s and wrapped a destination page with it. but it’s not working.
@hwidget
Widget secondPageWithScope() {
return ProviderScope(
key: ValueKey('2nd Scope'),
child: SecondPage(),
);
}
Full code is here:
https://gist.github.com/HeavenOSK/74cb5eb0a3cc01d4d897198e15193986
This is more of a question than an actual bug report. I'm trying to do the counter example using flutter_hooks, but I have noticed that when using hooks and consumer, when the state changes the entire widget tree is rebuilt.
class MyHomePage extends HookWidget {
@override
Widget build(BuildContext context) {
final count = useProvider(counterProvider).state;
debugPrint("Rebuild");
return Scaffold(
appBar: AppBar(
title: Text("Counter Example"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Consumer((context, read) => Text(
"$count",
style: Theme.of(context).textTheme.headline4,
)),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counterProvider.read(context).state++,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
When I used Consumer from the provider package, it used to rebuild only the consumer widget. Is this a bug or am I missing something to make this work?
marvel example missing assets/configurations.json
Describe the bug
I am not sure this is a Flutter bug or Riverpod bug.
When using StateNotifierProvider.family.autoDispose, it causes a grey screen on Flutter Web. Only happens on Release build, Debug is fine. If I don't use autoDispose it is also fine. I think I didn't experience this before the family.autoDispose style new syntax. My current work-around is not to use autoDispose.
I get this on the web console:
Another exception was thrown: Instance of 'minified:iu<void>'
Possibly related:
In my case, it was an static method uninitialized unexpectedly that triggered this issue. Seems like a webdev bug , only failed on web release build.
To Reproduce
StateNotifierProvider.family.autoDispose
Regards
Using:
Flutter 1.21.0-1.0.pre • channel dev • https://github.com/flutter/flutter.git
Framework • revision f25bd9c55c (3 weeks ago) • 2020-07-14 20:26:01 -0400
Engine • revision 99c2b3a245
Tools • Dart 2.9.0 (build 2.9.0-21.0.dev 20bf2fcf56)
In my case it is a StateNotifierProviderFamily
.
In a HookWidget, using the following line:
useProvider(mySNP(param).state);
I receive this error message AFTER changing the param
:
Unsupported operation: Used `useProvider(provider)` with a `provider` different than it was before
As far as I know, Computed
is like a filter and it only calculates and notifies Consumer
when there's a change. So in case I have a complex state, I need a lot of Computed
, should I set my Computed
as global variables or set it as static variables or something else?
If I set as global variables, I can't remember them all 😕
Is your feature request related to a problem? Please describe.
Yes.
The problem is that I want to be able to debug a mobile multiplayer game application by having two copies of it side by side in a flutter desktop application.
Describe the solution you'd like
I want to be able to override one provider, and any provider that depends on it is also overridden. Essentially I want to give each instance of the application a unique id, and then use the same set of providers that depend on that unique ID but have the reference to them result in a different set of providers per unique ID.
Describe alternatives you've considered
Currently I override the unique ID provider for each subtree and also have to override all providers that depend on the unique ID. The issue with this is that it creates a lot of code bloat in the widget tree (at the place I'm overriding). It also makes it not compile-safe, since I can't know for sure if I've overridden all of the providers that I need to.
The ideal solution would be to just need to override the unique ID provider, and all providers that depend on it are automatically resolved to a different instance of the provider. (I understand why this is not the case, since some providers are meant to be singletons, rather than factories).
I was using this library as soon as I watched your youtube video introducing this. I'm so glad to see the progress and continued work on this! Back then there was no Computed or Families, which both seem to sort of address this problem.
However, using families mean everywhere I obtain a provider, I also have to obtain the unique ID provider to reference the provider I actually want.
I could solve this by using a Computed that computes the provider I want based on the unique ID provider. However, this means that for every provider I want duplicated I have to create a computed to go along with it. As well as in the documentation of computed it mentions that it might get called multiple times, even if the inputs haven't changed.
Additional context
Here is my provider dependency diagram.
Multiplayer ID is the unique ID that I've been talking about.
Proposed Solution
I think I would propose an argument to the providers. uniqueForDependencies or keys, that would make them resolve to different providers if the set of uniqueForDependencies / keys are different. Similar to the keys argument of flutter_hooks.
i.e.
// Providers
final idProvider = Provider((_) => 1)
final storageProvider = Provider((ref) => StorageProvider(ref), uniqueForDependencies: [idProvider])
class StorageProvider {
ProviderReference ref;
Box box;
StorageProvider(this.ref){
init();
}
Future<void> init() async {
box = Hive.box('${ref.read(idProvider).value}');
}
String get clientId => box.get('clientId');
}
// Widget tree
void main() => runApp(
ProviderScope(
child: Row(
children: [
// App id == 1
MyApp(),
ProviderScope(
child: MyApp(),
overrides: [
// App id == 2
// Overriding id Provider should also override storage provider for this subtree
idProvider.overrideForSubtree(Provider((_) => 2))
],
),
],
),
));
class MyApp extends HookWidget {
// Inside my app
Widget build(BuildContext context){
// This resolves to a different instance of storage provider per MyApp instance.
final storage = useProvider(storageProvider);
print(storage.clientId);
// gets client id from one storage box within one MyApp instance
// and a different client id from a different storage box within the other MyApp instance.
}
}
Describe the bug
useProvider
triggers pointless rebuild when the value is collection type.
To Reproduce
At 0.6.0-dev+2
, executed this.
build
log will be printed each time PRESS ME
button is pressed.
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
void main() => runApp(const ProviderScope(child: App()));
class App extends StatelessWidget {
const App({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends HookWidget {
const HomePage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
print('build');
useProvider(_stateProvider.select((value) => value.state));
return Scaffold(
appBar: AppBar(),
body: RaisedButton(
child: const Text('PRESS ME'),
onPressed: () => context.read(_stateProvider).state = [42],
),
);
}
}
final _stateProvider = StateProvider((_) => [42]);
If [42]
(List<int>
) is changed to 42
(int
), the problem doesn't occurs, so collection comparison seems broken.
Expected behavior
build
should be printed at first time when the UI appeared, and shouldn't be printed when PRESS ME
button pressed.
At 0.5.1
, it works well without any problems.
Describe what scenario you think is uncovered by the existing examples/articles
Sometimes you want to nest providers inside of other providers for e.g. a listenable list where you want to only listen to item reordering, adding and removing, while all the internal items are individually listenable, so that you can avoid rebuilding everything when only a single item changes. What would be the most idiomatic way of achieving this in Riverpod?
Describe the bug
Trying to import StateNotifier from flutter_riverpod.dart
To Reproduce
import 'package:flutter_riverpod/flutter_riverpod.dart';
class Counter extends StateNotifier<int> {
Counter(): super(0);
}
seems like the riverpod has not included the StateNotifier, do we need to install this package on our own from state_notifier?
thanks
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.