Closed zgramming closed 4 years ago
Computed.family as you've described should work.
I'm not sure why it doesn't in your case. Try a flutter clean?
@rrousselGit strangely , after flutter clean and try uninstall application. it doesn't work what as expected.
Result data when typing will be seen immediately.
It will show data when keyboard closed, and i think data show when keyboard close because when keyboard close it will rebuild entire widget. You can see my Gif implementation. I missed something ?
Have you tried printing the unfiltered list, to see if it changes?
I believe the issue is that you are watching the family provider, but you aren't rebuilding when the queryController.text changes so it isn't getting the new computed. So you probably want to listen to the controller and rebuild based on that as well? I'm not sure if there is a better solution though.
Oh that's possible.
A solution is to save the filter in a StateProvider
, then instead of a Computed.family, have a simple Computed that reads the filter
Another testing for make it sure.
class TestingFilterModel {
String id;
String name;
int price;
TestingFilterModel({this.id, this.name, this.price});
}
class TestingFilterProvider extends StateNotifier<List<TestingFilterModel>> {
TestingFilterProvider([List<TestingFilterModel> state]) : super(state ?? []);
}
final testingFilterProvider = StateNotifierProvider<TestingFilterProvider>((ref) {
return TestingFilterProvider([
TestingFilterModel(id: '1', name: 'chair', price: 20000),
TestingFilterModel(id: '2', name: 'book', price: 50000),
TestingFilterModel(id: '3', name: 'handphone', price: 1000),
TestingFilterModel(id: '4', name: 'money', price: 60000),
TestingFilterModel(id: '5', name: 'bycle', price: 88000),
]);
});
final filterTesting = Computed.family<List<TestingFilterModel>, String>((read, query) {
final testing = read(testingFilterProvider.state);
return testing.where((element) => element.name.toLowerCase().contains(query)).toList();
});
Consumer((ctx, read) {
// final utang = read(showFilteredList(_queryController.text));
final filter = read(filterTesting(_queryController.text));
return Expanded(
child: ListView.separated(
shrinkWrap: true,
itemCount: filter.length,
separatorBuilder: (BuildContext context, int index) {
return Divider();
},
itemBuilder: (BuildContext context, int index) {
final result = filter[index];
return Card(
child: Center(
// child: Text(result.pengutang.nameUser),
child: Text(result.name),
),
);
},
),
);
}),
@rrousselGit can you give me example to implement that ? Because i don't get what you think. Because in documentation , Computed don't have parameter to send right ? the option my understanding is Computed.Family / Provider.Family
@TimWhiting problem is in my code because it not rebuild the state ?
Yes, the problem as it currently stands is in your code. However, you can use Remy's suggestion to make it work without a manual rebuild on a listener on your controller.
class TestingFilterModel {
String id;
String name;
int price;
TestingFilterModel({this.id, this.name, this.price});
}
class TestingFilterProvider extends StateNotifier<List<TestingFilterModel>> {
TestingFilterProvider([List<TestingFilterModel> state]) : super(state ?? []);
}
final testingFilterProvider = StateNotifierProvider<TestingFilterProvider>((ref) {
return TestingFilterProvider([
TestingFilterModel(id: '1', name: 'chair', price: 20000),
TestingFilterModel(id: '2', name: 'book', price: 50000),
TestingFilterModel(id: '3', name: 'handphone', price: 1000),
TestingFilterModel(id: '4', name: 'money', price: 60000),
TestingFilterModel(id: '5', name: 'bycle', price: 88000),
]);
});
/// No longer a family computed
final filterTesting = Computed<List<TestingFilterModel>>((read) {
final testing = read(testingFilterProvider.state);
/// Now using filter from a filterProvider
final filter = read(filterProvider);
return testing.where((element) => element.name.toLowerCase().contains(filter)).toList();
});
/// filter provider initialized with empty string
final filterProvider = StateProvider((ref) => '');
/// In your widget
// Somewhere you need to put something like this. (Probably in initState if you aren't using hook widgets).
// filterprovider state set to the new filter based on your filter text controller
// DON'T put this in your build method, since you don't want to create a new listener every build. Memory leak
_controller.listen((value) => context.read(filterProvider).state = value);
// If using hook widgets you can probably use useEffect
Consumer((ctx, read) {
final filter = read(filterTesting);
return Expanded(
child: ListView.separated(
shrinkWrap: true,
itemCount: filter.length,
separatorBuilder: (BuildContext context, int index) {
return Divider();
},
itemBuilder: (BuildContext context, int index) {
final result = filter[index];
return Card(
child: Center(
child: Text(result.name),
),
);
},
),
);
}),
I haven't tested this so it might have some issues. But this is the general idea.
Tim is correct, modulo the fact that you don't have to listen to the texteditingcontroller
You can use Textfield.onChange:
final filterProvider = StateProvider((_) => '');
...
Textfield(
onChange: (value) => filterProvider.read(context).state = value,
)
Both the Todos and the Marvel example implement a filter. The former uses an enum as filter, while the latter has a search field.
Ahhh finally i get it. Thank's @rrousselGit and @TimWhiting for both of you to help my case.
@rrousselGit I wonder for some common design patterns like filtering etc, there could be some helper functions (Maybe in another package). Or another provider for filtering that wraps a StateProvider for the filter and a Computed for the filtered results. I know you are planning on creating a retry variant for that common use case, so maybe a filter variant of a provider would be another good common use case to look at?
I'd be willing to help with creating some convenient providers/functions for common use cases and make a few PRs. But I'll probably wait for your reworked version to come out before looking into that.
What would helpers for filter looks like? In my opinion, Computed is the helper (especially combined with StateProvider)
What do you think is missing?
The problem isn't necessarily a feature issue, more of a discoverability issue I think. When you are thinking of a filter feature for your app you aren't necessarily thinking in terms of providers and computed. You have to switch mentalities to try to think how to implement it. So it wouldn't necessarily be a different provider, just some convenience functions or hooks that are in the terminology you would use when describing a filter. i.e.
final myFilter = createFilterProvider(initialQuery: () => '', filter: (ref, query) => //do filter stuff here
);
/// In hook widget
final filter = useFilter(myFilter);
// Changing query
TextField(onChange: (value) => filter.query = value),
// Using filter state
Column(children: filter.state.map(...).toList()),
I'm pretty busy this week, but I can try to prototype such a function / provider. Essentially what gets returned from the function is not a provider but something like this:
class FilterProviderHelper<Query extends StateNotifier, Filter extends Computed> {
final Query queryProvider;
final Filter filterProvider;
FilterProviderHelper(this.queryProvider, this.filterProvider);
}
And the hook functions access the relevant portions of this class.
As a side note. Note that I say discoverability issue, not example/documentaion issue, I know you have examples for it, but I think people don't necessarily look at examples on GitHub. I used to use Bloc before starting to migrate my stuff over to StateNotifier + Freezed + Hooks + Riverpod. I liked the tutorial section of the Bloc documentation: for example: https://bloclibrary.dev/#/fluttertodostutorial. Tutorials are longer than simple examples, but they showcase some design patterns & how to think when working with the API. I'd be willing to turn your todo example into a tutorial for the riverpod website. But it might take a little bit of time.
In general I'd love to see more content, such as a Medium article or YouTube series showing how to use the combination of (StateNotifier + Freezed + Hooks + Riverpod). I really love how they all work together. Again, I'd be willing to do this, but need to find / set aside time for it & this week is going to be pretty busy for me already.
Feel free to make a proposal about such helpers, although I'm not entirely convinced. It sounds like this is more about people not knowing how to use the tools available than a need for a new tool. And adding new APIs is difficult as they require a lot of effort to maintain.
Overall: Tutorials, articles, a search bar, and more examples are planned. They should come right after the big PR gets merged, so August I'd say
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 ?
Problem
Filter code
It's how i call the function into onChanged textfield
Update
After reading carefully on docs , I think i can implement it using Computed.Family . So i changed previous code to this :
this how i call in UI
But i get same result from previous code, I missing something ?