How to pass null in a method? - dart

class Foo {
final int? i;
Foo({this.i});
Foo copyWith({int? x}) {
return Foo(i: x ?? i);
}
}
void main() {
final foo = Foo(i: 0);
foo.copyWith(x: null);
print(foo.i); // prints `0` but should print `null`.
}
How can I actually pass null value to the method? In earlier Dart version copyWith() and copyWith(x: null) were two different things.
Note: I'm not looking for workarounds like making a new variable, like isNull and then deciding whether to pass null or not based on its value.

With simple copyWithwhit Dart null-safety you can't override value by null because if id is null return this.id. You need to override the value by null but not return with another value. It can solve in a few ways but I will give you the best example.
void main() {
final user = User(name: 'Dave', id: 110);
User copy = user.copyWith(id: null);
print(copy.toString()); // prints User(name: Dave, id: null).
}
class User {
User({required this.name, this.id});
final String name;
final int? id;
UserCopyWith get copyWith => _UserCopyWith(this);
#override
String toString() => 'User(name: $name, id: $id)';
}
abstract class UserCopyWith {
User call({
String name,
int? id,
});
}
class _UserCopyWith implements UserCopyWith {
_UserCopyWith(this.value);
final User value;
static const _undefined = Object();
#override
User call({
Object name = _undefined,
Object? id = _undefined,
}) {
return User(
name: name == _undefined ? value.name : name as String,
id: id == _undefined ? value.id : id as int?,
);
}
}

Related

Why it returns Instance of instead of the value?

Why the getCurrencyFromAPI function returns Intance of currency instead of the value itself. Is there some thing wrong with my model class?
This is the function
import 'dart:convert';
import 'package:app_bloc/data/models/currency.dart';
import 'package:http/http.dart' as http;
import 'package:app_bloc/constants/api_urls.dart';
class Repository {
Future<dynamic> getCurrencyFromAPI() async {
final res = await http.get(Uri.parse(coinbaseURL));
if (res.statusCode == 200) {
final resData = jsonDecode(res.body);
final data = resData['data'] as List;
List<Currency> list = [];
for (var e in data) {
final a = Currency.fromJson(e);
list.add(a);
}
print(list);
} else {
throw Exception('Error fetching data from API');
}
}
}
void main(List<String> args) {
Repository repo = Repository();
repo.getCurrencyFromAPI();
}
this is the model class
class Currency {
String id;
String name;
String minSize;
Currency({required this.id, required this.name, required this.minSize});
factory Currency.fromJson(Map<String, dynamic> data) {
final id = data['id'] as String;
final name = data['name'] as String;
final minSize = data['min_size'] as String;
return Currency(id: id, name: name, minSize: minSize);
}
}
Your Currency class does not have a toString method. That means it inherits the default from Object which returns Instance of 'Currency'.
When you print the List<Currency> it calls toString on every element to get a string representation. So, that's what you see. It is a Currency object.
Try adding:
String toString() => "Currency(id: $id, name: $name, minSize: $minSize)";
to you Currency class and see if it makes a difference.
Currency currencyModelFromJson(String str) => Currency.fromJson(json.decode(str));
class Currency {
String id;
String name;
String minSize;
Currency({required this.id, required this.name, required this.minSize});
factory Currency.fromJson(Map<String, dynamic> data) {
final id = data['id'] as String;
final name = data['name'] as String;
final minSize = data['min_size'] as String;
return Currency(id: id, name: name, minSize: minSize);
}
}
Then do this :
class Repository {
Future<dynamic> getCurrencyFromAPI() async {
final res = await http.get(Uri.parse(coinbaseURL));
if (res.statusCode == 200) {
final resData = jsonDecode(res.body);
final data = resData['data'] as List;
List<Currency> list = [];
for (var e in data) {
final a = currencyModelFromJson(e); // change here
list.add(a);
}
print(list);
} else {
throw Exception('Error fetching data from API');
}
}
}

Dart null safety: the operand cannot be null, so the condition is always true

I am trying to double-check if the User object is successfully created, but Null saftey says
the operand cannot be null, so the condition is always true
What if in a scenario where the json data contains invalid type, in this case there might be some errors when creating the user object
class User {
String? name;
String? age;
User({name, age}) {
this.name = name;
this.age = age;
}
factory User.fromJson(dynamic json) {
return User(name: json['name'], age: json['age']);
}
}
void main() {
String data = '{name: "mike",age: "2"}';
User user = User.fromJson(data);
if (user != null) { // Warning: "The operand can't be null, so the condition is always true. Remove the condition."
}
}
Please advise, Thank you! :)
If something wrong is going on creating your User object from a JSON input, it will, in your case, throw an Exception which will crash the program if not catch.
So the variable user cannot be null in your case which is what the warning is telling you.
If you want to have some kind of User.tryFromJson which returns null in case of any problems, you could add something like this to you User class:
static User? tryFromJson(dynamic json) {
try {
return User.fromJson(json);
} catch (_) {
return null;
}
}
Also, some minor comments. Your User constructor does not make much sense since you could have written the following instead:
User({this.name, this.age});
Also, I would make both arguments required and prevent the nullable types. So something like this (also changed age to int):
class User {
String name;
int age;
User({
required this.name,
required this.age,
});
factory User.fromJson(dynamic json) => User(
name: json['name'] as String,
age: json['age'] as int,
);
static User? tryFromJson(dynamic json) {
try {
return User.fromJson(json);
} catch (_) {
return null;
}
}
}
void main() {
final data = '{name: "mike",age: 2}';
final user = User.fromJson(data);
}

How to create null safe, default value constructor with Syntactic sugar

How do I create a null safe constructor with Syntactic sugar that would set a default value if the provided value is null?
class Person {
Person({
required this.name, //Idealy, adding (?? "friend") instead of "required" should've worked but doesn't.
required this.age,
});
String name;
int age;
greet() {
print("Hello $name");
}
}
So, I actually want something like this,
class Person {
Person({
this.name ?? "friend",
this.age ?? 0,
});
String name;
int age;
greet() {
print("Hello $name");
}
}
But, as you know this is not valid in dart. So, how actually, should I achieve this?
class Person {
Person({
String? name,
int? age,
}) : this.name = name ?? "friend",
this.age = age ?? 0;
String name;
int age;
void greet() {
print("Hello $name");
}
}
Constructor Optional Params
for selecting my proposal
select this as an answer (converted from comment with permission)
You can also use default values for your optional parameters:
class Person {
Person({
this.name = "friend",
this.age = 0,
});
String name;
int age;
greet() {
print("Hello $name");
}
}
The parameter is not required, and if you don't pass it, it gets the default value. If you do pass an argument, it must be non-null.

how should I use assert in Dart?

I saw exmaple code something like:
class ModelBinding extends StatefulWidget {
ModelBinding({
Key key,
this.initialModel = const GalleryOptions(),
this.child,
}) : assert(initialModel != null),
super(key: key);
...
so I wrote something:
class Person {
String firstName;
Person({name}){
print(name);
}
}
class Employee extends Person {
Employee(String name) : assert(false), super(name: name);
}
main() {
var emp = new Employee('Jason');
}
No matter if it is assert(false) or assert(true), the result is same.
So what is the meaning of assert?
assert is used for debugging and it simply means the condition should be true to proceed. Let me explain:
class MyClass {
final int age;
MyClass({this.age});
void someMethod() {
// using `age` here
}
}
You might face issues in someMethod if age passed is null, so to make sure it isn't null, you use assert like:
class MyClass {
final int age;
MyClass({this.age}) : assert(age != null, "Make sure age isn't null");
void someMethod() {
// using `age` here
}
}

How to use SpEL to select and return root objects in a collection by evaluating a list of other sub-objects maintained in a field in the root objects

The service I'm working with is running in Java 8. I am using SpEL to filter a generic collection of objects based on an input expression.
I am successfully filtering the collection when the expression evaluates top-level, primitive fields in the RootObject.
The SpEL Collection Selection feature returns a filtered collection of the RootObjects based on an expression for the key, label, and/or type in the RootObject. This case is working fine.
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("collection", collection);
String selectionExpression = "#collection.?[key matches 'foo|foo2|foo3']";
My problem is how to filter the original collection to return a collection of RootObjects based on evaluating fields in the OtherObject which are in the values list in the RootObject?
i.e., return all RootObjects that have a list item in RootObject.values where OtherObject.name == foo or OtherObject.count > 10 or OtherObject.isSelected == true.
The collection objects look something like these:
public class RootObject {
String key;
String label;
String type;
List<OtherObject> values;
public RootObject() {}
public RootObject(String key, String label, String type, List<OtherObject> values) {
this.key = key;
this.label = label;
this.type = type;
this.values = values;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<OtherObject> getValues() {
return values;
}
public void setValues(List<OtherObject> values) {
this.values = values;
}
}
public class OtherObject {
private String name;
private String label;
private Integer count;
private Integer totalCount;
private Boolean isSelected;
public OtherObject() {}
public OtherObject(String name, String label, int count, int totalCount, boolean isSelected) {
this.name = name;
this.label = label;
this.setCount(count);
this.isSelected = isSelected;
this.totalCount = totalCount;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getLabel() {
return this.label;
}
public void setLabel(String label) {
this.label = label;
}
public Integer getCount() {
return this.count;
}
public void setCount(Integer count) {
this.count = count;
}
public Integer getTotalCount() {
return this.totalCount;
}
public void setTotalCount(Integer totalCount) {
this.totalCount = totalCount;
}
public Boolean getIsSelected() {
return this.isSelected;
}
public void setIsSelected(Boolean isSelected) {
this.isSelected = isSelected;
}
}
I don't think you can do it in pure SpEL; you can implement it in Java and register a custom SpEL function.
You're just looking to do two projections. Within the collection of root objects, you want to project to find those root objects where within its set of values, the projection of criteria you want aren't empty.
#collection.?[!(values.?[name == 'foo' or count > 10 or isSelected == true].isEmpty())]
A projection just makes another list, and you can project based on any boolean expression. And inside a projection, you can continue to use SpEL to figure out that boolean expression, including another projection. You just have to keep in mind that the projection returns a list, whereas the projection criteria needs to be a boolean, so you need to actually check the condition that you're looking for (in this case, that the list is not empty).
Here's the test I did to demonstrate to myself that in fact that it was the right syntax and worked correctly:
#Test
public void test() {
final OtherObject otherFooOneNotSelected = new OtherObject("foo", "", 1, 1, false);
final OtherObject otherBarOneNotSelected = new OtherObject("bar", "", 1, 1, false);
final OtherObject otherFooTwelveNotSelected = new OtherObject("foo", "", 12, 12, false);
final OtherObject otherBarTwelveNotSelected = new OtherObject("bar", "", 12, 12, false);
final OtherObject otherFooOneSelected = new OtherObject("foo", "", 1, 1, true);
final OtherObject otherBarOneSelected = new OtherObject("bar", "", 1, 1, true);
final OtherObject otherFooTwelveSelected = new OtherObject("foo", "", 12, 12, true);
final OtherObject otherBarTwelveSelected = new OtherObject("bar", "", 12, 12, true);
final RootObject rootNoValues = new RootObject("noValues", "", "", Collections.<OtherObject>emptyList());
final RootObject rootFooOneNotSelected = new RootObject("rootFooOneNotSelected", "", "", Collections.singletonList(otherFooOneNotSelected));
final RootObject rootBarOneNotSelected = new RootObject("rootBarOneNotSelected", "", "", Collections.singletonList(otherBarOneNotSelected));
final RootObject rootBarTwelveNotSelected = new RootObject("rootBarTwelveNotSelected", "", "", Collections.singletonList(otherBarTwelveNotSelected));
final RootObject rootAllValues = new RootObject("allValues", "", "", Arrays.asList(
otherFooOneNotSelected,
otherBarOneNotSelected,
otherFooTwelveNotSelected,
otherBarTwelveNotSelected,
otherFooOneSelected,
otherBarOneSelected,
otherFooTwelveSelected,
otherBarTwelveSelected));
final Collection<RootObject> collection = Arrays.asList(rootNoValues, rootFooOneNotSelected, rootBarOneNotSelected, rootBarTwelveNotSelected, rootAllValues);
final ExpressionParser parser = new SpelExpressionParser();
final StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("collection", collection);
final String selectionExpression = "#collection.?[!(values.?[name == 'foo' or count > 10 or isSelected == true].isEmpty())]";
final List<?> result = (List<?>) parser.parseExpression(selectionExpression).getValue(context);
assertEquals("Result", Arrays.asList(rootFooOneNotSelected, rootBarTwelveNotSelected, rootAllValues), result);
}

Resources