How to select an item from a List in flutter - dart

I have list from a model like this
amount:"12000"
dateTime:"19/07/2018"
detail:"Soto"
hashCode:853818549
id:1
name:"Theodorus"
I want to just select amount and add it to another list of string, but I'm always getting this error A value of type 'String' can't be assigned to a variable of type 'List<String>'. , I thinks its because im not doing it right, here is my code below
void setupList() async {
DebtDatabase db = DebtDatabase();
listCache = await db.getMyDebt();
setState(() {
filtered = listCache;
});
List<String> amount = new List<String>();
listCache.map((value) {
amount = value.amount; } );
//print(amount);
}
can anyone help me, so I can get list of ammount from this model list and then sum all the ammount?

The map function returns an iterable and you can then transform it into a list.
You should try something like this:
void setupList() async {
DebtDatabase db = DebtDatabase();
listCache = await db.getMyDebt();
setState(() {
filtered = listCache;
});
List<String> amount = listCache.map((value) => value.amount).toList();
//print(amount);
}

Related

Stream of List with elements from another streams' current values

I want to create a List Stream based on another elements Stream. The List Stream should yield a new list every time an element from the list emits a new value.
Something like this:
Stream<List<Model>> getListStream(List<int> ids) async* {
final List<Model> models = [];
for (var id in ids) {
getModelStream(id).listen((event) {
models.add(event);
});
}
yield models;
}
but it always yields an empty array.
I think the problem is probably the fact the it does not react to the event listener.
How do you deal with this kind of problem?
You're yielding an empty list which will later be filled in.
If you want to wait for the list to be filled in before yielding it, you'll have to do that.
Either:
Stream<List<Model>> getListStream(List<int> ids) async* {
for (var id in ids) {
yield await getModelStream(id).toList();
}
}
or, if you want to start all the streams immediately, and compute them in parallel, and then emit the values when they are done (not necessarily in the original ID order), then:
Stream<List<Model>> getListStream(List<int> ids) {
var controller = StreamController<List<Model>>(sync: true);
controller.onListen = () {
var count = 0;
for (var id in ids) {
count++;
getModelStream(id).toList().then((models) {
controller.add(models);
if (--count == 0) controller.close();
});
}
};
return controller.stream;
}

Convert Stream<List<T>> to Stream<Map<K,T>>

I have a requirement of converting Stream<List<T>> to Stream<Map<K,T>>
I have a class
class Order
{
int id;
DateTime date;
}
I want to convert Stream<List<Order>> to Stream<Map<DateTime, List<Order>>
I want to display orders as below
12-Dec-2020
Order 1
Order 2
13-Dec-2020
Order 3
Order 4
14-Dec-2020
Order 5
Order 6
Suggestions for a better DS are welcome.
How do I do this?
Please suggest.
Thanks in advance.
Using groupListsBy from collection: ^1.15.0
K keySelector(T t) { ... }
final Stream<List<T>> source$ = ...;
final Stream<Map<K, List<T>>> result$ = source$.map((list) => list.groupListsBy(keySelector));
You can do this without any package.
First, you have to create an empty Map, then map a List<Order> to a map with a key as a date from the list item (single order) then add it to That Empty map.
Check if the date is available as a key in Map. If the date exists as a key in that map then simply add a new order to that value or else simply add a new key to the Map.
Code:
Stream<List<Order>> orders = ...;
Stream<Map<DateTime, List<Order>>> ordersByDate = {};
orders.forEach((Order order){
ordersByDate.keys.contains(order.date)
? ordersByDate.update(order.date, (oldValue) => oldValue + <Order>[order])
: ordersByDate[order.date] = [order];
});
print(ordersByDate);
//At here your ordersByDate
Let me know if it works for you.
Fold method is what you're looking for.
Example:
void main() async {
final today = DateTime.now();
final tomorrow = today.add(const Duration(days: 1));
final order1 = Order(1, today);
final order2 = Order(2, today);
final order3 = Order(3, tomorrow);
final x = await Stream
.fromIterable([order1, order2, order3])
.fold<Map<DateTime, List<Order>>>({}, (val, element) {
(val[element.date] ??= []).add(element);
// Same as:
// if (val[element.date] == null) {
// val[element.date] = [];
// }
// val[element.date]!.add(element);
return val;
});
print(x);
}
class Order
{
int id;
DateTime date;
Order(this.id, this.date);
#override
String toString() {
return 'Order(id: $id, date: $date)';
}
}

Future.then() is executed too early

I have a method which is supposed to return a Future, containing a list of groups.
This works fine as I can loop the list of groups in that method itself, but somehow list is returned before it could be filled. Surely this is an error on my part but I can't seem to grasp what I'm doing wrong.
Future< List<GroupData> > getGroups(String uniqueUserID) async
List<GroupData> groups = new List<GroupData>();
try {
var result = Firestore.instance
.collection("groups")
.where("members", arrayContains: uniqueUserID);
result.snapshots()
.listen (
(data) {
// Handle all documents one by one
for (DocumentSnapshot ds in data.documents)
{
List<String> members = new List<String>();
for (dynamic member in ds.data['members'])
{
members.add( member );
}
groups.add( new GroupData(ds.data['name'], ds.data['description'], ds.documentID.toString(), ds.data['admin'], members) );
}
}
);
} catch (exception)
{
print ('Something went wrong while fetching the groups of user ' + uniqueUserID + ' !');
}
return groups;
}
This method is being called using the method Future.then() but the list is empty while there should be several resuls (and there are, I can loop all items in the list in the above method and access/print their data). What am I missing?
The execution of your function is never locked. It doesn't wait until your listened stream finished.
There are a few solutions:
change stream.listen into await for (final item in stream)
add an await stream.done
Example:
before:
Stream<List<T>> stream;
stream.listen((list) {
for (final item in list) {
print(item);
}
});
after:
await for (final list in stream) {
for (final item in list) {
print(item);
}
}

Observing an object containing a list of observables?

I am using the observe package.
Consider this example:
class Product extends Object with ChangeNotifier {
double _price = 0.0;
#reflectable double get price => _price;
#reflectable void set price(double value) {
if (value == null) throw new ArgumentError();
_price = notifyPropertyChange(#price, price, value);
}
}
class Order extends Object with ChangeNotifier {
final ObservableList<Product> products = new ObservableList<Product>();
double get total {
double sum = 0.0;
for (var item in products) {
sum += item.price;
}
return sum;
}
}
// Synchronizes the view total with the order total.
// Or rather, I'd like it to do that.
var order = new Order();
order.changes.listen((records) {
view.total = order.total;
});
How would I rewrite this example to make it work?
I would like to be notified of any changes to the object's state, even if they happen to the list or the items of the list.
Do I have to manage change subscriptions to all items and the list itself? Inside or outside of the Order class? Through which property would I notify the change? It seems messy either way.
The elements in the ObservableList do not propagate the notification to the list that contains them. They can't because they have no reference to the list.
Also the list does not forward the notifications to the class it is referenced by.
Not really satisfying but the best I could come up with.
import 'dart:async' as async;
import 'package:observe/observe.dart';
class Product extends Object with ChangeNotifier {
double _price = 0.0;
#reflectable double get price => _price;
#reflectable void set price(double value) {
if (value == null) throw new ArgumentError();
_price = notifyPropertyChange(#price, price, value);
}
#override
String toString() => 'Product - price: $price';
}
class Order extends Object with ChangeNotifier {
final ObservableList<Product> products = new ObservableList<Product>();
// keep listeners to be able to cancel them
final List<async.StreamSubscription> subscriptions = [];
Order() {
products.changes.listen((cr) {
// only react to length changes (isEmpty, isNotempty changes are redundant)
var lengthChanges = cr.where((c) => c.name == #length);
if(lengthChanges.isNotEmpty) {
lengthChanges.forEach((lc) =>
notifyChange(lc));
// we can't know if only additions/removals were done therefore we
// cancel all existing listeners and set up new ones for all items
// after each length change
_updateProductsListeners();
}
});
// initial setup
_updateProductsListeners();
}
// cancel all product change listeners and create new ones
void _updateProductsListeners() {
print('updateListeners');
subscriptions.forEach((s) => s.cancel());
subscriptions.clear();
products.forEach((p)
=> subscriptions.add(p.changes.listen((crs) =>
crs.forEach((cr) =>
notifyPropertyChange(cr.name, cr.oldValue, cr.newValue)))));
}
double get total {
double sum = 0.0;
for (var item in products) {
sum += item.price;
}
return sum;
}
}
void main() {
// Synchronizes the view total with the order total.
// Or rather, I'd like it to do that.
var order = new Order();
order.changes.listen((records) {
//view.total = order.total;
records.forEach(print);
});
// a PathObserver example but it doesn't seem to be more convenient
var op = new PathObserver(order, 'products[3].price')..open((c) =>
print(c));
var prods = [new Product()..price = 1.0, new Product()..price = 2.0, new Product()..price = 3.0, new Product()..price= 4.0];
var prods2 = [new Product()..price = 5.0, new Product()..price = 6.0];
order.products.addAll(prods);
// use Future to allow change notification propagate between changes
new async.Future(() =>
order.products..addAll(prods2)..removeWhere((p) => p.price < 3.0))
.then((_) => new async.Future(() => order.products[3].price = 7.0));
new async.Future.delayed(new Duration(seconds: 1), () => print('done'));
}
I suggest to use something like an event bus for this where the objects that want/should to notify about something just send and event and objects that are interested in something listen for that without any knowledge of where the other object exists.
For example https://pub.dartlang.org/packages/event_bus
Another solution is to use the ListPathObserver. The class is deprecated but you can copy his code and reuse it. With that class you can listen for specific changes in the contained items. The field to watch is specified by path.

Monotouch.Dialog Generate from db and retain values

I'm have a settings view where I'm using MT.D to build out my UI. I just got it to read elements from a database to populate the elements in a section.
What I don't know how to do is access each elements properties or values. I want to style the element with a different background color for each item based on it's value in the database. I also want to be able to get the selected value so that I can update it in the db. Here's the rendering of the code that does the UI stuff with MT.D. I can get the values to show up and slide out like their supposed to... but, styling or adding delegates to them to handle clicks I'm lost.
List<StyledStringElement> clientTypes = SettingsController.GetClientTypes ();
public SettingsiPhoneView () : base (new RootElement("Home"), true)
{
Root = new RootElement("Settings") {
new Section ("Types") {
new RootElement ("Types") {
new Section ("Client Types") {
from ct in clientTypes
select (Element) ct
}
},
new StringElement ("Other Types")
}
Here's how I handled it below. Basically you have to create the element in a foreach loop and then populate the delegate with whatever you want to do there. Like so:
public static List<StyledStringElement> GetClientTypesAsElement ()
{
List<ClientType> clientTypes = new List<ClientType> ();
List<StyledStringElement> ctStringElements = new List<StyledStringElement> ();
using (var db = new SQLite.SQLiteConnection(Database.db)) {
var query = db.Table<ClientType> ().Where (ct => ct.IsActive == true && ct.Description != "Default");
foreach (ClientType ct in query)
clientTypes.Add (ct);
}
foreach (ClientType ct in clientTypes) {
// Build RGB values from the hex stored in the db (Hex example : #0E40BF)
UIColor bgColor = UIColor.Clear.FromHexString(ct.Color, 1.0f);
var localRef = ct;
StyledStringElement element = new StyledStringElement(ct.Type, delegate {
ClientTypeView.EditClientTypeView(localRef.Type, localRef.ClientTypeId);
});
element.BackgroundColor = bgColor;
ctStringElements.Add (element);
}
return ctStringElements;
}

Resources