dart:
As you can see in this .dart code I am trying to print the list of id, there is a Dummy Data class which have a list of details of some product. There is a main function which call a instance (productIdList) of a class name Present.
class DummyData{
List<Product> dummyProduct = [
Product(
id: 'p1',
title: 'Red Shirt',
description: 'A red shirt - it is pretty red!',
price: 29.99,
imageUrl:
'https://cdn.pixabay.com/photo/2016/10/02/22/17/red-t-shirt-1710578_1280.jpg',
),
Product(
id: 'p2',
title: 'Trousers',
description: 'A nice pair of trousers.',
price: 59.99,
imageUrl:
'https://upload.wikimedia.org/wikipedia/commons/thumb/e/e8/Trousers%2C_dress_%28AM_1960.022-8%29.jpg/512px-Trousers%2C_dress_%28AM_1960.022-8%29.jpg',
),
Product(
id: 'p3',
title: 'Yellow Scarf',
description: 'Warm and cozy - exactly what you need for the winter.',
price: 19.99,
imageUrl: 'https://live.staticflickr.com/4043/4438260868_cc79b3369d_z.jpg',
),
Product(
id: 'p4',
title: 'A Pan',
description: 'Prepare any meal you want.',
price: 49.99,
imageUrl:
'https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Cast-Iron-Pan.jpg/1024px-Cast-Iron-Pan.jpg',
),
];
}
class Product{
final String id;
final String title;
final String description;
final double price;
final String imageUrl;
Product({this.id, this.description, this.imageUrl, this.price, this.title});
}
class Present extends DummyData{
void productIdList (){
print(dummyProduct.map((product) =>product.id).toList());
}
}
void main(){
Present obj = Present();
obj.productIdList;
}
The code above will not compile in the latest version of dart due to changes made to support null safety. Specifically the product class. You can fix in a few different ways.
The first approach would be to mark all of the constructor arguments in the Product class as required. This approach makes it so you have to pass in values for every constructor argument.
class Product {
final String id;
final String title;
final String description;
final double price;
final String imageUrl;
Product(
{required this.id,
required this.description,
required this.imageUrl,
required this.price,
required this.title});
}
The other approach would be to make the fields within the product class nullable. In this approach, you are not required to pass in a value for every constructor argument, but when you do not pass in a value the field will be assigned a null value.
class Product {
final String? id;
final String? title;
final String? description;
final double? price;
final String? imageUrl;
Product(
{this.id,
this.description,
this.imageUrl,
this.price,
this.title});
}
Related
I am working with some Dart code for a Flutter/Dart class I'm taking. I expected the following code to compile, but it did not:
class Person {
String? name;
int? age;
Person(this.name, this.age);
#override
String toString() {
return "name: $name\nage: $age";
}
}
void main() {
final person = Person(name: 'Joe', age: 30);
print(person);
}
When I made the constructor parameters optional, as below, it does compile:
class Person {
String? name;
int? age;
Person({this.name, this.age});
#override
String toString() {
return "name: $name\nage: $age";
}
}
void main() {
final person = Person(name: 'Joe', age: 30);
print(person);
}
I tried searching the Flutter dev docs for a reason why this is so, or a way to have required parameters with their names in a constructor, but I didn't find anything. I can certainly imagine cases where I would want required constructor parameters to have names.
My pubspec.yaml specifies the following:
environment: sdk: ">=2.12.0 <3.0.0"
Your first example uses what are called "positional parameters" in dart. You cannot call a positional parameter with a name label, which is why the first example does not compile.
The second example uses "named parameters". Any parameter defined within {} is considered a named parameter and must be called using a name label. As explained in the dart language tour, named parameters are optional unless they’re explicitly marked as required.
So simply add the required keyword before any named parameter you want to require.
class Person {
String? name;
int? age;
Person({required this.name, required this.age});
#override
String toString() {
return "name: $name\nage: $age";
}
}
void main() {
final person = Person(name: 'Joe', age: 30);
print(person);
}
class Destination {
String imageUrl;
String city;
String country;
String description;
List<Activity> activities;
Destination({
this.imageUrl,
this.city,
this.country,
this.description,
this.activities,
});
List<Activity> activities = [
Activity(
imageUrl: 'assets/images/stmarksbasilica.jpg',
name: 'St. Mark\'s Basilica',
type: 'Sightseeing Tour',
startTimes: ['9:00 am', '11:00 am'],
rating: 5,
price: 30,
),
Your problem is that named parameters is optional by default in Dart. And if a parameter does not specify a default value, the default value is going to be null.
This is a problem when using null-safety since your variables are declared to never be allowed to have the value null. But by not requiring values for the parameters, there are a risk that some or all of these variables are going to have null as value.
The fix is to use the keyword required to specify that you want a named parameter to be required to get a value when calling the constructor/method. So change your code to:
class Destination {
String imageUrl;
String city;
String country;
String description;
List<Activity> activities;
Destination({
required this.imageUrl,
required this.city,
required this.country,
required this.description,
required this.activities,
});
}
Change this:
Destination({
this.imageUrl ?? "",
this.city ?? "",
this.country ?? "",
this.description ?? "",
this.activities ?? "",
});
now it will work
I have a dart object that includes a field of type Money, which itself is composed of amount and currency:
#JsonSerializable()
class Account {
final String id;
final String type;
final String subtype;
final String origin;
final String name;
final String status;
final String currency;
final Money balance; <== value object
...
}
Money looks something like this:
class Money {
final int amount;
final String currency;
const Money(this.amount, this.currency);
...
}
The above is to be mapped for use by sqflite, therefore the target JSON must be a flat JSON, like:
{
"id": String,
"type": String,
"subtype": String,
"origin": String,
"name": String,
"status": String,
"currency": String,
"balanceAmount": int; <== value object
"balanceCurrency": String; <== value object
}
I understand I can use JsonKey.readValue to extract a composite object from the entire JSON object prior to decoding.
But how can I do the opposite? Grab the two values from the Money instance and map them to balanceAmount and balanceCurrency?
Nothing in the search I did on json_serializable api docs, GitHub issues, or StackOverflow appears to respond to this in particular: how to map one field to two (or more) target keys?
this solution would be better if the generated code used full json in "balance"
like this:
Money.fromJson(json)
But I don't know how to do that :(
import 'package:project/test/money.dart';
import 'package:json_annotation/json_annotation.dart';
part 'account.g.dart';
#JsonSerializable()
class Account {
final String id;
final String type;
final String subtype;
final String origin;
final String name;
final String status;
final String currency;
#JsonKey(fromJson: _dataFromJson)
final Money balance; // <== value object
Account({
required this.id,
required this.type,
required this.subtype,
required this.origin,
required this.name,
required this.status,
required this.currency,
required this.balance,
});
static Money _dataFromJson(Object json) {
if (json is Map<String, dynamic>) {
return Money(amount: json["balanceAmount"], currency: json["balanceCurrency"]);
}
throw ArgumentError.value(
json,
'json',
'Cannot convert the provided data.',
);
// return Money(amount: 0, currency:"");
}
factory Account.fromJson(Map<String, dynamic> json) => Account(
id: json['id'] as String,
type: json['type'] as String,
subtype: json['subtype'] as String,
origin: json['origin'] as String,
name: json['name'] as String,
status: json['status'] as String,
currency: json['currency'] as String,
balance: Money.fromJson(json),
);
Map<String, dynamic> toJson() => _$AccountToJson(this);
}
// {
// "id": String,
// "type": String,
// "subtype": String,
// "origin": String,
// "name": String,
// "status": String,
// "currency": String,
// "balanceAmount": int; <== value object
// "balanceCurrency": String; <== value object
// }
I am experimenting with Dart mixins and noticed that
You can use private fields in mixins
Even abstract methods like abstract classes.
Imagine you have few abstract methods no need to create an abstract ad extend it, you can collect them inside a mixin and re-use it.
The discussion goes whether you like to have inheritance within your design
Here is a code snippet:
import 'package:flutter/cupertino.dart';
mixin Favourite {
bool _isFavourite = false;
bool get isFavourite;
}
class ProductProvider
with Favourite, ChangeNotifier
implements Comparable<ProductProvider> {
final String id;
final String title;
final String description;
final String imageUrl;
final double price;
ProductProvider({
required this.id,
required this.title,
required this.description,
required this.imageUrl,
required this.price,
});
ProductProvider.noPrice({
required this.id,
required this.title,
required this.description,
required this.imageUrl,
bool isFavourite = false,
}) : price = 0.0;
toggleFavourite() {
this._isFavourite = !this._isFavourite;
notifyListeners();
}
#override
bool get isFavourite => _isFavourite;
factory ProductProvider.fromJSON(Map<String, dynamic> json) {
return json['price'] != null
? ProductProvider(
id: json['id'],
title: json['title'],
description: json['description'],
imageUrl: json['imageUrl'],
price:
json['price'] is int ? json['price'].toDouble() : json['price'])
: ProductProvider.noPrice(
id: json['id'],
title: json['title'],
description: json['description'],
imageUrl: json['imageUrl'],
isFavourite: json['isFavourite'] ?? false,
);
}
#override
int compareTo(ProductProvider other) {
return (price - other.price).toInt();
}
}
class Product {
final int id;
final String name;
String url;
Product(
this.id,
this.name,
);
Product.fromMap(Map data)
: url = data['url'], // error
this(data['id'], data['name']);
}
How can I assign value to url in named constructor, I don't want to use method to get this done.