How to design Filters in Flutter? - flutter

I am new to flutter and I wonder how to develop filters in flutter something like this (screenshot taken from google images), so I just want to know how to do filtering in a flutter, is there anything like plugins or special widgets? If you provide any reference or code or any tutorials will be helpful for me to learn.ThankYou in Advance.

You need to break it down in few pieces.
First is your UI: these are just standard Flutter Widgets. You want a user to slide it up? Figure out how to show a Widget by sliding up. You want it to be in the alert popup? Figure out how to do alert popup. Filter UI is not different from any other UI - so you can look for and ask generic UI questions.
Second is how you implement the model. It can be something simple like a Provider that holds the list of Items that you fetched; and then each filter adding more where conditions to your list.
Something like:
var items=<Item>[]; // somehow you would fetch the initial list of items
var filtered;
void addColorFilter(Color color) {
filtered=filtered??items;
filtered=filtered.where( (element) => element.color==color);
notifyListeners();
}
void addSizeFilter(String size) {
filtered=filtered??items;
filtered=filtered.where( (element) => element.size==size);
notifyListeners();
}
void removeFilters() => filtered=null;
void getFiltered() => filtered??items;
And then you can use filtered iterator in your ListView.builder() to show only filtered items.
To answer your follow-up question here:
You have mix of 'AND' & 'OR' conditions. If you just keep adding iterators like the above, you won't be able to show 2 sizes (M and S) - because no items is both M and S. In this case, where there is a multiple choice filter, you will need to add additional list for each filter type that can have multiple choice. And you will have to rebuild your entire filter.
This might be a good starting point - for your price and size example:
var items=<Item>[]; // somehow you would fetch the initial list of items
Iterator? filtered;
double? lowPrice;
void addLowPrice(double price) {
lowPrice=price;
rebuildFilter();
}
double? highPrice;
void addHighPrice(double price) {
highPrice=price;
rebuildFilter();
}
var sizeOptions=<String>[];
void addSizeFilter(String size) {
sizeOptions.add(size);
reubuildFilter();
}
void rebuildFilter() {
filtered=items.where((e) => e.price >= lowPrice??0 && e.price <= highPrice&&double.infinity).where((e) => sizeOptions.isEmpty || sizeOptions.contains(e));
notifyListeners();
}
void removeFilters() {
lowPrice=null;
highPrice=null;
sizeOptions.clear();
filtered=null;
notifyListeners();
}
void getFiltered() => filtered??items;

Related

How to create a method inside a provider class

I want to be clear and precise. I have a database with 260 variables, I have a data model class and a SQLiteHelper class. I'm using a provider and I have all the CRUD inside.
The problem comes because I have scrolleable page in which I want to be able to change all variables. Around 240 variables are int? and each one will have a button which do the same, convert it to zero if it is null or add 1 if it is an integer. I'm not using a normal callback because in that scrolleable page I use different reusable buttons and I want to know the value of the variable in child and parent widget. In the reusable buttons for change color and text, and in the parent widget(scrolleable page) to save them in SQlite at the end of the page with a save button.
This is my provider class
class DBProvider with ChangeNotifier {
final SQLiteHelper0 _db = SQLiteHelper0();
List<DB> _items = [];
Future<void> loadDB() async {
List<Map<String, dynamic>> data = await _db.charDB;
_items = data.map((DB) {
return DB(
id: charDB["id"],
name: charDB["name"],...// the rest of the CRUD
I'm trying something like that
dynamic increment(index){
if(_items[index] != int?){
return _items[index];
} else if (_items[index]! == null){
return _items[index]== 0;
}else { return _items[index] + 1;}
}
an then in the scrolleable page make another function like that
late DBProvider _dBProvier;
void _increment(index){setState(() {
_dBProvider.increment(index);
});}
I am having different problems, at times I think that nothing makes sense. But if it is a totally dumb way, please give me some direction to keep trying. This question is related with other question where I focused the problem in a different and wrong way Why this function is null?
Thanks.

Flutter Bloc Rx dart combineLatest2 combine function not running

I'm writing a flutter app and and using the bloc library. I have a bloc and a cubit, within the state of each is a list of ids of some other documents I need to fetch from firestore. There can be some overlap and some docs are already fetched so I want to get the list of ids from both states, compare them, and then only go to firestore for ones that exist in one but no the other.
I set a new cubit for this:
class CircleRecipesCubit extends Cubit<CircleRecipesState> {
CircleRecipesCubit({
#required RecipesBloc recipesBloc,
#required CirclesCubit circlesCubit,
}) : assert(
recipesBloc != null,
circlesCubit != null,
),
_recipesBloc = recipesBloc,
_circlesCubit = circlesCubit,
super(CircleRecipesInitial());
final RecipesBloc _recipesBloc;
final CirclesCubit _circlesCubit;
StreamSubscription _recipesSubscription;
StreamSubscription _circlesSubscription;
Future<void> getCircleRecipes() async {
// get a list of recipes the user already has loaded
List<String> userRecipesIds;
_recipesSubscription = _recipesBloc.stream.listen((RecipesState event) {
if (event is RecipesLoaded) {
userRecipesIds = event.recipes.map((e) => e.id).toList();
print('*');
print(userRecipesIds);
print('*');
}
});
// get a list of recipes in the circles
List<String> circleRecipeIds;
_circlesSubscription = _circlesCubit.stream.listen((CirclesState event) {
if (event is CirclesLoaded) {
circleRecipeIds = event.circles.fold([],
(previousValue, element) => [...previousValue, ...element.recipes]);
print('|');
print(circleRecipeIds);
print('|');
// List<String> circleOnlyRecipeIds = circleRecipeIds;
// circleRecipeIds.removeWhere((e) => userRecipesIds.contains(e));
// print(circleOnlyRecipeIds);
}
});
// reduce the list of recipes to a set of only circle recipes
//TODO
//------- Try with RX dart
Stream<RecipesState> recipesStream = _recipesBloc.stream;
Stream<CirclesState> circlesStream = _circlesCubit.stream;
Rx.combineLatest2(recipesStream, circlesStream, (
RecipesState recipesState,
CirclesState circlesState,
) {
print("This doesn't print!");
print(recipesState);
print(circlesState);
if (recipesState is RecipesLoaded) {
userRecipesIds = recipesState.recipes.map((e) => e.id).toList();
print('*');
print(userRecipesIds);
print('*');
}
if (circlesState is CirclesLoaded) {
circleRecipeIds = circlesState.circles.fold([],
(previousValue, element) => [...previousValue, ...element.recipes]);
print('|');
print(circleRecipeIds);
print('|');
// List<String> circleOnlyRecipeIds = circleRecipeIds;
// circleRecipeIds.removeWhere((e) => userRecipesIds.contains(e));
// print(circleOnlyRecipeIds);
}
// fetch the set of recipes
});
}
#override
Future<void> close() {
_recipesSubscription.cancel();
_circlesSubscription.cancel();
return super.close();
}
}
So above is my cubit - it listens to the recipesBloc and the circlesCubit. The first two expressions in the getCiricleRecipes() function are only there to prove that its hooked up correctly - when it runs those print statement print the ids I want it to from both the other bloc and the other cubit.
I need the latest values from both though at the same time to compare them - so I thought rx.combinelatest2 would be good. I give it the stream from the bloc and the cubit. But the combiner function doesn't even run even though things seem 'wired up' correctly.
Any help greatly appreciated.
Make sure both streams have already emitted at least one item.
combineLatest documentation states:
The Stream will not emit until all streams have emitted at least one item.
Since the first block (where you subscribe to _circlesCubit) prints, then most likely _recipesBloc is the culprit here.

in Flutter, make a list of api call inside one api call

In one of my flutter app, at first I want to call an api, which will return a list of item, and the item will be shown in a ListView. I also need to call another api for each item of the ListView to fetch description of that item and show the description to each item according to their id. How can I resolve this scenario. In RxJava, there is an operator called flatmap which did the same things without any hassle. But in flutter, How can I implement this. Here is my 2 function
class HomeRepositoryImpl extends HomeRepository {
HomeGraphQLService homeGraphQLService;
HomeMapper homeMapper;
HomeRepositoryImpl(HomeGraphQLService homeGraphQLService, HomeMapper homeMapper) {
this.homeGraphQLService = homeGraphQLService;
this.homeMapper = homeMapper;
}
#override
Future<List<Course>> getAllCourseOf(String className, String groupName) async {
final response = await homeGraphQLService.getAllCourseOf(className, groupName);
return homeMapper.toCourses(response).where((course) => course.isAvailable);
}
#override
Future<CourseProgressAndPerformance> getProgressAndPerformanceAnalysisOf(String subjectCode) async {
final response = await homeGraphQLService.getProgressAndPerformanceAnalysisOf(subjectCode);
return homeMapper.toProgressAndPerformance(response);
}
}
In the above class, first I call getAllCourseOf() function to get a list of course and show them in list view. I need to call getProgressAndPerformanceAnalysisOf(courseId) to fetch description of each item and show the description in each item of that list.
So what is recommended way to do so.
thanks in advance
I'm not sure on how the listing would be presented, my guess is you're looking for Stream and asyncMap()
Here's an example implementation that would give you a list of CourseProgressAndPerformance, this is the direction I'd investigate.
var perfList = Stream
.fromIterable(listOfCourses)
.asyncMap((course) => getProgressAndPerformanceAnalysisOf(courseId))
.toList();

Alternative for ChangeNotifier that is optimized for large number of listeners?

Flutter documentation for ChangeNotifier says
ChangeNotifier is optimized for small numbers (one or two) of listeners. It is O(N) for adding and removing listeners and O(N²) for dispatching notifications (where N is the number of listeners).
Is there an alternative class available for use in Flutter if I want to design a model where there will be many number of listeners (e.g. dozens of listeners)?
Ideally, I am looking for something with less than O(N^2) for dispatching notifications where N is number of listeners.
Interestingly, when I look at the latest code/doc, it is optimized now!
It says (2021.01):
It is O(1) for adding listeners and O(N) for removing listeners and dispatching notifications (where N is the number of listeners).
Thus we can happily use it. Yeah!
For why this happens: Looking at the source code
void notifyListeners() {
assert(_debugAssertNotDisposed());
if (_listeners!.isEmpty)
return;
final List<_ListenerEntry> localListeners = List<_ListenerEntry>.from(_listeners!);
for (final _ListenerEntry entry in localListeners) {
try {
if (entry.list != null)
entry.listener();
} catch (exception, stack) {
...
}
}
}
we see it iterate through the listeners and call them.
In the old days, say even flutter 1.21, the source code looks like:
void notifyListeners() {
assert(_debugAssertNotDisposed());
if (_listeners != null) {
final List<VoidCallback> localListeners = List<VoidCallback>.from(_listeners!);
for (final VoidCallback listener in localListeners) {
try {
if (_listeners!.contains(listener))
listener();
} catch (exception, stack) {
...
}
}
}
}
Thus you see, in the old days there is double loop (a for loop + a contains check), and in the new days there is not.

How to create an accordion in wicket

I am using wicket 1.4.17.I went through quite a few posts on this but couldn't understand it clearly. How can I make an accordion in wicket?
Basically what I am looking for is kind of a table with 1 column and multiple rows where each row can be expanded or collapsed, and each row on expansion shows another table of data.
The following code example should help you get started.
Feel free to ask questions if something seems unclear. Of course you could go even deeper in your DetailPanel (that's why I would suggest that approach)
AbstractRepeater exampleView = new ListView<Object>("exampleView", myList) {
#Override
protected void populateItem(ListItem<Object> item) {
//you can use a own panel, fragment, etc to illustrate your detail view here
//you could also use one WebMarkupContainer for visibility - but I'd assume this will get very messy, very soon
final DetailPanel detailPanel = new DetailPanel("detailPanel", item.getModel());
detailPanel.setVisible(false);
detailPanel.setOutputMarkupPlaceholderTag(true);
item.add(detailPanel);
//add AjaxLink to switch between the visibilty of the detailView
AjaxLink<Void> detailLink = new AjaxLink<Void>("detailLink") {
#Override
public void onClick(AjaxRequestTarget target) {
detailPanel.setVisible(!detailPanel.isVisible());
target.addComponent(detailPanel);
}
};
item.add(detailLink);
}
};
add(exampleView);