flutter : how to groupBy two or more fields? - flutter

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/

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.

How to link up web api call to the list view

So i have my dart call to my api get method. Btw the way am just learning flutter and dart and trying out basic crud operations I would use to be doing in .net and c#
import 'dart:convert';
import 'package:theapp/models/Players.dart';
import 'package:http/http.dart';
class ApiService {
final String apiUrl = "https://apiurlhidden.com/api";
final String getAllPlayersEndPoint = "/GetAllPlayers/";
Future<List<Player>> getAllPlayers() async {
final getallPlayersUrl = Uri.parse(apiUrl + getAllPlayersEndPoint);
Response res = await get(getallPlayersUrl);
if (res.statusCode == 200) {
List<dynamic> body = jsonDecode(res.body);
List<Player> players =
body.map((dynamic item) => Player.fromJson(item)).toList();
return players;
} else {
throw "Failed to load cases list";
}
}
}
And I have my listview here but it complaining saying key and players do not exist
import 'package:flutter/material.dart';
import 'package:theapp/models/Players.dart';
class PlayerList extends StatelessWidget {
List<Player> players = [];
PlayerList({Key key, this.players}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: players == null ? 0 : players.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: InkWell(
onTap: () {},
child: ListTile(
leading: Icon(Icons.person),
title: Text(players[index].firstName),
subtitle: Text(players[index].surname.toString()),
),
));
});
}
}
My Model
class Player {
final int id;
final int type;
final String playerLevel;
final String firstName;
final String surname;
Player(this.id, this.type, this.playerLevel, this.firstName, this.surname);
factory Player.fromJson(Map<String, dynamic> json) {
return Player(
json['id'],
json['type'],
json['playerlevel'],
json['firstname'],
json['surname'],
);
}
#override
String toString() =>
'Players{id: $id, firstName: $firstName, lastName: $surname}';
}
Is there any reason why it should not recognize players and key in my list view page also how do I get the items to appear in the listview.
Picture only added to show the context in the items I mentioned above. Also coming from a .net background I would normally use an observable collection so it gets any changes in data in real-time am I using the correct approach for that.
Use required keyword to make parameters mandatory.
PlayerList({required Key key, required this.players}) : super(key: key);
Named parameters are optional unless they’re explicitly marked as required.
See Parameters for details.

Flutter Riverpod: Filter rebuilds with StateNotifier and .select()

This is my current state management solution
class UserState {
final int id;
final String name;
}
class UserNotifier extends StateNotifier<UserState> {
UserNotifier() : super(User(1, 'Pero Peric'));
}
final userNotifierProvider = StateNotifierProvider((ref) => UserNotifier());
I want to rebuild my UI only when the name changes not the id!
Riverpod provides a way to do this link but I can't get it working with my StateNotifier.
I would write it like this but it isn't working like this.
// inside Consumer widget
final userName = watch(userNotifierProvider.select((value) => value.state.name));
Can you refactor my code to work or propose another solution?
Any help is appreciated!
According to the doc, "this method of listening to an object is currently only supported by useProvider from hooks_riverpod and ProviderContainer.listen".
Try to create another provider, which you can use in UI.
final nameProvider = StateProvider<String>((ref) => ref.watch(userNotifierProvider.select((user) => user.name)));
class UserState {
UserState({
required this.id,
required this.name,
});
final int id;
final String name;
}
class UserNotifier extends StateNotifier<UserState> {
UserNotifier() : super(UserState(id: 1, name: 'Pero Peric'));
}
final userNotifierProvider =
StateNotifierProvider<UserNotifier, UserState>((ref) => UserNotifier());
In consumer,
final userName =
watch(userNotifierProvider.select((value) => value.name)); // value is the state

when to implements an abstract class in freezed?

I need to understand this code, resoCoder did it on DDD Playlist. why does he implements IEntity inside freezed?
The code is:
#freezed
abstract class TodoItem with _$TodoItem implements IEntity {
const factory TodoItem({
#required UniqueId id,
#required TodoName name,
#required bool done,
}) = _TodoItem;
factory TodoItem.empty() => TodoItem(
id: UniqueId(),
name: TodoName(''),
done: false,
);
}
}
IEntity code is:
abstract class IEntity {
UniqueId get id;
}
UniqueId Code is:
class UniqueId extends ValueObject<String> {
#override
final Either<ValueFailure<String>, String> value;
// We cannot let a simple String be passed in. This would allow for possible non-unique IDs.
factory UniqueId() {
return UniqueId._(
right(Uuid().v1()),
);
}
/// Used with strings we trust are unique, such as database IDs.
factory UniqueId.fromUniqueString(String uniqueIdStr) {
assert(uniqueIdStr != null);
return UniqueId._(
right(uniqueIdStr),
);
}
const UniqueId._(this.value);
}
It ensures consistency; TodoItem must implement everything as per IEntity.
Let's imagine one day you want to add an attribute "createdAt" to IEntity: in this case, you will have to add "createdAt" to every class that implements IEntity across the project, otherwise the compiler will let you know you're missing something :D
Some code now.
The result would be
abstract class IEntity {
UniqueId get id;
int get createdAt; // let's keep it "int" for the example purpose
}
then you would have to update the freezed class too
#freezed
abstract class TodoItem with _$TodoItem implements IEntity {
const factory TodoItem({
#required UniqueId id,
#required int createdAt,
#required TodoName name,
#required bool done,
}) = _TodoItem;
factory TodoItem.empty() => TodoItem(
id: UniqueId(),
createdAt: 1234,
name: TodoName(''),
done: false,
);
}
}

Flutter - map two lists in one request

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
}