Flutter - map two lists in one request - flutter

I'm passing the mapped list (firstList) to Widget (as a model) and I'd like to pass a second one as a regionModel. So here's my question. Is it possible to map two lists in one request (need to map secondList and pass it as regionModel: value2). I found something like IterableZip but it's not supported anymore.
Widget _buildView(
List<FirstListModels> firstList,
List<SecondListModels> secondList,
...
children: firstList.map((value) {
return Widget(
model: value, regionModel: value2);
}).toList()
then when i pass them I have:
class SomeClass extends StatefulWidget {
final FirstListModel model;
final List<SecondListModel> regionModel;
So, i need to get rid of list to have only SecondListModel and have output like this:
class SomeClass extends StatefulWidget {
final FirstListModel model;
final SecondListModel regionModel;

If both lists have the same length, you can use for loop and spread operator
void main() {
final firstList = ['firstList - item #1', 'firstList - item #2'];
final secondList = ['secondList - item #1', 'secondList - item #2'];
final listOfModels = [
for (var i = 0; i < firstList.length; i++) ...{
SomeModel(model: firstList[i], region: secondList[i]),
}
];
print(listOfModels);
}
class SomeModel {
final String model;
final String region;
SomeModel({this.model, this.region});
toString() => 'SomeModel(model: $model, region: $region)';
}

Sovled
I left it like it was and then i compare both model and list with firstWhere and return string from it (part of url)
String _compareLists(){
var result = widget.secondList.firstWhere(
(element) => element?.property == widget.firstList.property, orElse: () => null)
.?propertyContainsUrl ?? "";
return result
}

Related

Dart Inherit class and Use it in functions of the parent class

I want to make a parent class which use ChangeNotifier. And from this class, I want to create two separate inherited classes that will provide list data to some parts of the app, and each will have its own separate list. But I could not figure out how each class could create its own list and only make operations on that list via using superclass. Can someone explain to me how can I manage this?
import 'package:flutter/material.dart';
class ObjectListProvider<T extends num, K extends Object> with ChangeNotifier {
final Map<T, K> _map = {};
Map<T, K> get map {
return {..._map};
}
K? getSingleObjectWithId(id) {
return _map[id];
}
void add(T id, K obj) {
_map[id] = obj;
notifyListeners();
}
void remove(T id) {
_map.remove(id);
notifyListeners();
}
}
import 'object_list_provider.dart';
import '../person.dart';
class PersonList extends ObjectListProvider {
final Map<dynamic, Person> _people = {};
}
import './object_list_provider.dart';
import '../group.dart';
import '../person.dart';
class GroupList extends ObjectListProvider {
final Map<dynamic, Group> _groups = {};
void addPersonToGroup<T extends num>(Person person, T id) {
super.add(id, person);
notifyListeners();
}
void removePersonFromGroup<T extends num>(Person person, T id) {
_groups[id]?.removePerson(id);
notifyListeners();
}
}
import './person.dart';
import './transaction.dart';
class Group {
final int _id;
String _name;
List<Person> _people = [];
List<Transaction> _transactions = [];
int _totalSpending;
Group({required int id, required String name, int totalSpending = 0})
: _id = id,
_name = name,
_totalSpending = totalSpending;
int get id {
return _id;
}
String get name {
return _name;
}
int get totalSpending {
return _totalSpending;
}
set name(String newName) {
_name = newName;
}
void addPerson(Person person) {
_people.add(person);
}
void removePerson<T extends num>(T id) {
_people = _people.where((Person person) => person.id != id).toList();
}
void addTransaction(Transaction transaction) {
_transactions.add(transaction);
}
}
class Person {
final int _id;
final String _name;
int _balance;
List<int> involvedGroups = [];
Person({required int id, required String name, int balance = 0})
: _id = id,
_name = name,
_balance = balance;
int get id {
return _id;
}
}
For example, I will use this provider in some other dart file as
final groupList = Provider.of<GroupList>(context);
groupList.add(....)
I refactored my code and came up with a solution that worked for me. Let me try to explain future reads as much as I can.
changed map from private to public. I am not sure it is the best way but it worked for this case. I was also able to work it with getter and setters but by doing that provider object did end up having two variables as _map and map.
import 'package:flutter/material.dart';
class ObjectListProvider<T extends num, K extends Object> with ChangeNotifier {
Map<T, K> map = {};
K? getSingleObjectWithId(id) {
return map[id];
}
void add(T id, K obj) {
map[id] = obj;
notifyListeners();
}
void remove(T id) {
map.remove(id);
notifyListeners();
}
}
Add generics after extending. This way I was able to access the map variable which previously I made publicly accessible. did the same thing for the PersonList as well.
import './object_list_provider.dart';
import '../group.dart';
import '../person.dart';
class GroupList extends ObjectListProvider<num, Group> {
void addPersonToGroup<T extends num>(Person person, T id) {
super.map[id]?.addPerson(person);
notifyListeners();
}
void removePersonFromGroup<T extends num>(Person person, T id) {
super.map[id]?.addPerson(person);
notifyListeners();
}
}
Other than these I did not changed anything related. Now I can call and use provider in some other file as
...
#override
Widget build(BuildContext context) {
final groupList = Provider.of<GroupList>(context);
final groups = groupList.map;
return ListView.builder(
itemCount: groups.length,
itemBuilder: (context, int index) {
return ListTile(
onTap: () => index,
title: Text(groups[groups.keys.elementAt(index)]!.name),
trailing: Text(
groups[groups.keys.elementAt(index)]!.totalSpending.toString(),
),
);
},
);
}
...
I'm working on something simmilar(not current version) at the moment. I would like to try and help if and where I can - though with the caveat that I'm still figuring out a lot of the basics myself.
Could you narrow-down or re-phrase the problem?
What I've done in the app I linked to above, as far as I think it might be relevant to you after a quick skim through your code, what I've done is:
To 'hold' the list and as much as possible of the functionality in the parent class.
In my case, each child class extends that parent - I'm calling it a 'listicle', and the type of object is specific to that childTypeListicle (for now).
The child classes hold specification of the types they list - in my case each it type shares an abstract parent Item class - as well as some config details for e.g. remote access and factory constructors. These' fields communicate up to the parent interfacing and its generic-ized functionality around the list through abstract method declarations enforced by the shared parent class. So that crteates a kind of the list as axel while it its reasonably item-type agnostic. Make sense?
Seems to work well so far, basically holds the logic this side of the plane-of-presentation implementation.
I also have tertiary connected interface elements like bottom-alert-bar connecting into fields of the parent 'listicle', and creating a kind of navigation ui that manipulates the list out onto the listview builder. Would like to also build in some partial local repository-ing but that doesn't seem a priority at the moment for this project.
I hope some of that helps somehow.

apply multiple conditional filters on a ListView

I'm a new flutter developer and would like to use multiple filters on my List.
I'm using a ListView :
ListView.builder(
itemBuilder: (ctx, index) =>
MealCard(_customList[index]),
itemCount: _customList.length,
),
and my Meal class have the following properties:
Meal {
final String id,
final String title,
final String imgUrl,
final bool isLowCalorie,
final bool isVegan,
final bool isBreakfat,
final bool isDinner,
final bool isLunch}
I'd like to apply different filters based on the booleans included within my class; I have some methods that look something like this:
List<Meal> get breakfastFilter {
return _meals.where((element) => element.isBreakfast).toList();
}
List<Meal> get lunchFilter {
return _meals.where((element) => element.isLunch).toList();
}
List<Meal> get dinnerFilter {
return _meals.where((element) => element.isDinner).toList();
However these methods allow me to create a single list, what if I wish to add additional filters together and create a combination of two lists.
I know I'm late to the game, so let me throw my name in the hat. I'd refactor it a bit so as to support more than one filter, otherwise you won't know upfront how many filters will be applied.
I'd create an enum called MealType:
enum MealType {
none,
isLowCalorie,
isVegan,
isBreakfast,
isDinner,
isLunch
}
Refactor the class to take a list of MealType values:
class Meal {
final String? id;
final String? title;
final String? imgUrl;
final List<MealType>? mealTypeFilter;
Meal({ this.id, this.title, this.imgUrl, this.mealTypeFilter });
}
The data would look like this:
List<Meal> meals = [
Meal(
id: '1001',
title: 'Pancakes',
imgUrl: '',
mealTypeFilter: [
MealType.isBreakfast
]
),
Meal(
id: '1002',
title: 'Chicken Wings',
imgUrl: '',
mealTypeFilter: [
MealType.isLunch
]
),
Meal(
id: '1003',
title: 'Yogurt',
imgUrl: '',
mealTypeFilter: [
MealType.isLowCalorie,
MealType.isBreakfast,
MealType.isVegan
]
)
];
I'd collect the applied filters in a Set to avoid duplicates, and filter the meals whether the mealTypeFilter collection contains any of the applied filters:
Set<MealType> appliedFilters = {};
List<Meal> filteredList = [];
List<Meal> getFilteredList() {
if (appliedFilters.isEmpty) {
return meals;
}
return meals.where((m) => m.mealTypeFilter!.any((f) => appliedFilters.contains(f))).toList();
}
I'd call the getFilteredList() method every time in the build method when I trigger a button that contains the filter to be applied and populate the filteredList with which I render a ListView with the results:
#override
Widget build(BuildContext context) {
filteredList = getFilteredList();
// rest of the code here
}
See Gist for the full code, and run it through DartPad so you can see it in action, like this:
Hope this works.
You can do it something like this:
bool filterItem(element){
return element.isVegan&&element.isBreakfat;
}
_meals.where((element) => filterItem(element)).toList();
There's many ways to do it but you can try to use boooleans combined with optional parameters like this:
getMealFilter(_meals,
{bool lookForBreakfast = false,
bool lookForVegan = false,
bool lookForLunch = false}) {
return _meals
.where((element) =>
(lookForBreakfast && element.isBreakfast) ||
(lookForVegan && element.isBreakfast) ||
(lookForLunch && element.isLunch))
.toList();
}
I just put 3 filters, but you can easily add the rest. How this works is that if, for example, lookForBreakfast is false, element.isBreakfast is ignored. (false and something is always false), and so on for the others parameters.

flutter : how to groupBy two or more fields?

how can I use groupBy function which is in collection.dart to group items by two fields and return a model class with them not one field like int?
If I group by one field, It works fine and there is no problem:
Map<int, List<FooterItem>> _getGroups(List<FooterItem> items) {
return groupBy(items, (FooterItem i) {
return i.groupId;
});
}
But when I wan to return a model class from result ,groupBy is not grouping values .
here I have a list of FooterItem which has It's group data and how can I use groupBy to group a List<FooterItem> by groupId and titleGroup and return FooterGroup not int :
class FooterItem {
final int id;//item id
final int groupId;
final String title;//item title
final String titleGroup;
...
}
Map<FooterGroup, List<FooterItem>> _getGroups(List<FooterItem> items) {
return groupBy(items, (FooterItem i) {
return FooterGroup(id: i.groupId, title: i.titleGroup);
});
}
I could solve problem by extending Equatable in the model class which I wanted to use as grouped values and overriding props :
import 'package:equatable/equatable.dart';
class FooterGroup extends Equatable{
final int id;
final String title;
FooterGroup({
#required this.id,
#required this.title,
});
#override
List<Object> get props => [id,title];
}
so duplicate values of Groups where not seen any more. so
Map<FooterGroup, List<FooterItem>> _getGroups(List<FooterItem> items) {
return groupBy(items, (FooterItem i) {
return FooterGroup(id: i.groupId, title: i.titleGroup);
});
}
works fine now.
A quick way to achieve this:
groupBy(footers, (FooterItem f) {
return '${f.groupId}+${f.titleGroup}';
});
Source: https://coflutter.com/dart-how-to-group-items-in-a-list/

Flutter: Convert String to class field name during runtime

Is it possible to "convert" a String which is dynamically generated in my code, into a class field/variable name?
The issue I am facing is that I have a database which returns a unique name (as String) for each of its rows. I want to use this name in order to find the corresponding field (with the same spelling as the database entry) of a generated class that holds all my translations within its various fields. As this class is generated and subject to constant change there is no way to convert into a Map and thereby access its fields as values as explained here.
My code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
// Suppose this is a dynamic list returned from a database query
final List<String> listFromDB = ['one', 'three', 'two'];
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: listFromDB.length,
itemBuilder: (context, index) =>
_listItemBuilder(context, index, listFromDB)),
);
}
}
Widget _listItemBuilder(BuildContext context, int index, List<String> listFromDB) {
// suppose this is some map with translations of different languages.
final labels = Labels.translations['languageCode'];
final String dbUniqueName = listFromDB[index];
// QUESTION: How can I use the above String dbUniqueName to access a field of the
// AnyClass class in order to return the corresponding value, e.g. "eins" for
// its name "one"?
print(labels.dbUniqueName) // ERROR: Not compiling because dbUniqueName is a String
return null;
}
// GENERATED: The below code can not be changed as it is generated
class Labels {
static final Map<String, AnyClass> translations = {
'languageCode' : AnyClass(one: 'eins', two: 'zwei', three: 'drei')
};
}
class AnyClass {
AnyClass({this.one, this.two, this.three});
final String one;
final String two;
final String three;
}
I have read this old thread on GitHub on the same issue. But it seems there was no solution at that time. I also came across reflections in Dart but I am not sure whether they provide a solution in the above described case.
Thanks for any help and advice!
Reflection is provided in the mirrors package, but that package requires the VM and is therefore not supported in Flutter. Have you looked at the reflectable package?
If that doesn't work, a simple Map could do the trick of translating a String in to an object's field, e.g.:
var object = AnyClass();
var field = {'one': object.one, 'two' object.two, 'three': object.three};
print(field['two']); // will print object.two;
You usually use methods like the one below in Dart language to reference a class variable using a String.
Widget _listItemBuilder(
BuildContext context, int index, List<String> listFromDB) {
final labels = Labels.translations['languageCode'];
final String dbUniqueName = listFromDB[index];
print(getByFieldName(dbUniqueName, labels));
return null;
}
String getByFieldName(String name, AnyClass anyClass) {
switch (name) {
case 'one':
return anyClass.one;
case 'one':
return anyClass.one;
case 'one':
return anyClass.one;
default:
throw ('Unsupported');
}
}

Why when i use a class in dart with equatable and just a list as property the copyWith method return the same object, same hascode

Im using bloc and it was working as expected but today i notice a strage behaviour when i was sending the same state (RefreshState) using copyWith, the state wasnt trigger after second call. then i did a test creating two objects and compared them but the result was they are the same object, very odd.
So why is this happen?, this is my class:
class Model extends Equatable {
final List<Product> mostBuyProducts;
const Model({
this.mostBuyProducts,
});
Model copyWith({
List<Product> mostBuyProducts,
}) =>
Model(
mostBuyProducts: mostBuyProducts ?? this.mostBuyProducts,
);
#override
List<Object> get props => [
mostBuyProducts,
];
}
and then i use the CopyWith method like (inside the bloc):
Stream<State> _onDeleteProduct(OnDeleteProduct event) async* {
state.model.mostBuyProducts.removeWhere((p) => p.id == event.id);
var newMostBuyProducts = List<Product>.from(state.model.mostBuyProducts);
final model1 = state.model;
final model2 = state.model.copyWith(mostBuyProducts: newMostBuyProducts);
final isEqual = (model1 == model2);
yield RefreshState(
state.model.copyWith(mostBuyProducts: newMostBuyProducts));
}
isEqual return true :/
BTW this is my state class
#immutable
abstract class State extends Equatable {
final Model model;
State(this.model);
#override
List<Object> get props => [model];
}
Yes because lists are mutable. In order to detect a change in the list you need to make a deep copy of the list. Some methods to make a deep copy are available here : https://www.kindacode.com/article/how-to-clone-a-list-or-map-in-dart-and-flutter/
Using one such method in the solution below! Just change the copyWith method with the one below.
Model copyWith({
List<Product> mostBuyProducts,
}) =>
Model(
mostBuyProducts: mostBuyProducts ?? [...this.mostBuyProducts],
);