Dart: Use non-constant value as default constructor parameter - dart

I would like to use FirebaseAuth.instance (a non-constant value) as a default parameter in a constructor.
I have this class:
class MailAuth {
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
MailAuth();
// methods using firebaseAuth
}
Now I want to unit-test this class, so I want to mock firebaseAuth and inject the mock in the constructor. It should be a named parameter with the default value of FirebaseAuth.instance. I tried something like this:
class MailAuth {
final FirebaseAuth firebaseAuth;
MailAuth({this.firebaseAuth = FirebaseAuth.instance});
// methods using firebaseAuth
}
But this doesn't work because FirebaseAuth.instance is not a constant.
How can I achieve this behaviour?

Use a null value as sign to use your default value:
MailAuth({FirebaseAuth? firebaseAuth})
: firebaseAuth = firebaseAuth ?? FirebaseAuth.instance;
This initializes the variable with the parameter if the parameter is non-null, otherwise it uses the "default" singleton instance instead.
(Updated for null safety. This still works, and the instance variable isn't non-nullable. Only the constructor parameter is nullable.)

While the accepted answer works, you don't really have to "Use a null value as sign to use your default value".
With sound null safety, you might not want to make your instance variable of nullable type (final FirebaseAuth? firebaseAuth) for several reasons.
Instead of starting with null or having a nullable type(?), just declare your instance variable as late, instead of final, indicating that you do want to initialise it, ensuring you initialise it in the constructor with a default value if none is passed in invocation.
So now you will get:
class MailAuth {
late FirebaseAuth firebaseAuth;
MailAuth({firebaseAuth}) {
this.firebaseAuth = this.firebaseAuth ?? FirebaseAuth.instance;
}
// methods using firebaseAuth
}

Related

Check if a type implements an interface [duplicate]

I've recently found myself in a situation where I wanted to check if a Type is a subtype of another Type this is what I've tried
abstract class Record{}
class TimeRecord extends Record{}
void test(){
print(TimeRecord is Record); // return false but why ??
}
The only time it makes sense to check if one type is a subtype of another type is when at least one of the types is a type variable. (Otherwise, you can just look at the source and write a constant true or false into the code).
There is a way to check whether one type is a subtype of another, and it does use the is operator, but you need to have an instance as the first operand and a type as the second. You can't just create an instance of an unknown type, so we instead rely in Dart's covariant generics:
bool isSubtype<S, T>() => <S>[] is List<T>;
(You can use any generic class, or even create your own, instead of using List. All it needs is a way to create the object.)
Then you can write:
print(isSubtype<TimeRecord, Record>()); // true!
The is keyword is used to check if an object instance is an object of type T, and not if a type is another type:
abstract class Record{}
class TimeRecord extends Record{}
void test(){
print(TimeRecord() is Record); // returns true!
}
Just to add up to #lrn answer.
You could also do something like:
extension NullableObjectsExtensions<T> on T {
bool isSubtypeOf<S>() => <T>[] is List<S>;
bool isSupertypeOf<S>() => <S>[] is List<T>;
}
So this way you can test any variable anywhere.

What is the right order to initialize class fields?

What is the logic here when the programmer initializes _random at once but the _streamController is initialized in the constructor?
Can all the fields be initialized without a constructor then?
RandomStore {
RandomStore() {
_streamController = StreamController<int>();
_timer = Timer.periodic(const Duration(seconds: 1),
(_) => _streamController.add(_random.nextInt(100)));
randomStream = ObservableStream(_streamController.stream);
}
late final Timer _timer;
final _random = Random();
late final StreamController<int> _streamController;
late final ObservableStream<int?> randomStream;
...
Can all the fields be initialized without a constructor ?
Yes, you can initialize all fields without having to declare a constructor, but only if you don't need a reference to the current instance (this) or if they are 'late' fields.
The determining factor in choosing where to initialize fields is whether or not you need to have the reference (even implicit) to this.
In Dart this is only available from the construcor body; this means in particular that this is not usable in the initializer list and inside the inline initializers (except for the late fields).For terminology, see Glossary below.
this is the reference to the current instance, and is required in order to read the instance fields, even if you usually omit it (e.g., in your snippet, randomStream is equivalent to this.randomStream).
For example, in your snippet, to initialize randomStream you need to be able to read the streamController field, so you have to mark it with late; thanks to late you can initialize randomStream in the constructor body or in the inline initializer (in this second case it will actually be initialized only when you try to access it for the first time; which is an advantage if its initialization is expensive and you want to avoid it as long as possible).
As an alternative to late, you could mark the field as nullable and initialize it in the constructor body (in which case the field will first be implicitly initialized with a null value; in fact this approach is not usable if the field is final and therefore cannot be reassigned).
Instead, to initialize the streamController field, you don't use a reference to this, so you could avoid the overhead of marking the field with late and you can initialize it in the initializer list or in the inline initializer (it is the same).
Example:
class RandomStore {
final StreamController<int> _streamController;
RandomStore()
: _streamController = StreamController<int>() {
}
}
Is late always a good choice? (UPDATED)
While from the above it might seem that 'late' is a great solution for most situations, the doc suggests avoiding 'late' if it is not really necessary, because:
It is less secure:
a late field (also if it has a non-nullable type) entails a risk of errors at runtime similar to that which occurred before the introduction of sound null safety, because Dart does not force you to perform any checks before reading its value (unlike nullable fields, for which access to properties requires the use of not null operator or conditional operator). Note that Dart does not offer the possibility to check if a late field has already been initialized (See Issue #324 mentioned below)
It adds overhead:
under the cover will be created a field with the indicated type, a field -presumably boolean- for keep track of whether the initialization has occurred, and a getter that at each access checks if the initialization had occurred.
Useful sources about late:
Doc Guide about null safety and late variables https://dart.dev/null-safety/understanding-null-safety#late-variables
Dart Best practice about late
https://dart.dev/guides/language/effective-dart/usage#dont-use-late-when-a-constructor-initializer-list-will-do
https://dart.dev/guides/language/effective-dart/usage#avoid-late-variables-if-you-need-to-check-whether-they-are-initialized
Dart issue #324 Should we provide a way to query the status of late variables?
a very interesting insight into 'late' (the discussion in which the Dart Team decided not to allow the final developers to check if a late field has been initialized)
Note the differences with Java:
(which personally made it difficult for me to switch from Java to Dart at first)
this in Dart is not available in inline initializers (in Java it is available)
the final fields in Dart must be initialized before the constructor body (in Java they can also be initialized in the constructor body)
Gloassary:
class MyClass {
MyClass(String v1, String v2)
: myField1 = v1, myField2 = v2 //This is the "initializer list"
//Compared to inline initializers, it allows you to initialize fields using the constructor arguments
{
//This is the "constructor body"
myField4 = myField1; //This operation require an implicit reference to `this`; it is equivalent to `myField4 = this.myField1;`
}
String myField1;
String myField2;
String myField3 = '3'; //This is the "inline initialization"
late String myField4;
}

I want to use named parameters in Dart for clarity. How should I handle them?

TL;DR: Named parameters are optional as a result of a conscious design choice. Short of having official language support, is there any way to enforce (and inform) required named arguments?
I find it extremely useful to use named parameters when defining a class. Take, for instance, an Ability in an MMORPG:
class Ability {
final name;
final effectDuration;
final recast; // wait time until next use
// ...
}
effectDuration and recast both carry the same type of information (i.e. duration of time) and are likely represented by the same datatype. It is easy to mix up which number goes where. However, they are both information vital to the correctness of the object, so they can't be missing during instantiation.
I could just break the program via a try-catch to enforce the requirement of those parameters, but that doesn't sound like fun for someone who uses the class and has no idea (short of reading the docs and understanding intuitively what the class does) that they are required.
Is there any way to enforce the requirement of certain named parameters while managing to inform the caller of said requirement and/or help them use it correctly?
The meta package provides a #required annotation that is supported by the DartAnalyzer.
Flutter uses this a lot and provides #required directly from import 'package:flutter/foundation.dart'
foo({#required String name}) {...}
foo(); // results in static warning
#required doesn't check if the passed value is null or not, only that a value was actually passed on the call site.
To check for null you can also use assert() to check for passed values
class Ability {
Ability(this.name, this.effectDuration, this.recast) : assert(name != null), assert(effectDuration != null), assert(recast != null);
final name;
final effectDuration;
final recast; // wait time until next use
// ...
}
[Update] New as-of Dart 2.0
In dart 2.0 the required keyword has been added to the language as part of the null-safety update. This means that you get a compiler-enforced non-null value rather than one checked by the analyzer; this makes the null check completely redundant.
This means that this code does effectively the same as the old code below, except that you never have to worry about the assertion throwing as the values for name, effectDuration, and recast cannot be null.
class Ability {
final String name;
final Duration effectDuration;
final bool recast;
final String? description;
Ability({
required this.name,
this.effectDuration = Duration(seconds: 1),
this.recast = false,
this.description,
});
}
Before Dart 2.0
Yes, there is!
Here's an example:
class Ability {
final String name;
final Duration effectDuration;
final bool recast;
final String description;
Ability({
#required this.name,
this.effectDuration = new Duration(seconds: 1),
this.recast = false,
this.description,
}):
assert(name != null),
assert(effectDuration != null);
}
You don't have to assert that name is not equal to null, but it might be useful for you.
Although you could use the flutter foundation package as described in the accepted answer, when I am working with model classes that don't need to know about Flutter, I prefer to use the meta package directly. That way it doesn't create an unnecessary dependency on the framework. This allows you to share the Dart code even outside of Flutter.
Add meta to pubspec.yaml:
dependencies:
meta: ^1.1.7
Import it in your class file:
import 'package:meta/meta.dart';
Use the #required annotation in your code:
class Person {
String name;
int age;
Person({#required this.name, this.age,});
}
So name is a required parameter, but age isn't.
final person = Person(name: 'Bob');
Update:
In an upcoming version of Dart, the required keyword should be added by default, so no imports will be necessary at all.
As of 2.12 with null safety you can use required keyword (not #required). Also no need to import any additional packages.
In this example named parameter name is optional while effectDuration and recast are required.
class Ability {
final name;
final effectDuration;
final recast;
Ability({this.name, required this.effectDuration, required this.recast});
}
Update pubspec.yaml, for example:
environment:
sdk: ">=2.12.0-0 <3.0.0"
References:
Sound null safety
How does #required compare to the new required keyword?
With null safety:
Non-nullable named parameter:
You need to either mark the named parameter required or provide a default value or even mark it late. For example:
class Foo {
final int a;
final int b;
late final int c; // Mark late and provide value later.
Foo({
required this.a, // Mark required.
this.b = 0, // Provided a default value.
});
}
Nullable named parameter:
You don't need anything special to handle them.
class Foo {
final int? z;
Foo({
this.z,
});
}
If you want declare to a empty variable but that has methods inside , you can:
1)Use the late keyword
2)Declare the type like possible null returned example: int? number;
3)Initialize the variable empty, for example :
List listOfNumbers = [];
Map mapOfPerson1 = {};
And so you can use the methods of the variable to add them values

How to get subtypes using `DartType` class from the `analyzer`?

How can I get the subtypes of an element using the class DartType from the analyzer package?
For those wondering, the DartType class is a statically resolved type that is created by the analyzer package, Dart's static tooling package. The author is asking how they can get other types given a DartType - I think you mean super types, i.e. types that you inherit or implement.
(If you simply wanted to check if the DartType is a subtype of something, you could use isSubtypeOf)
We can get a hold of the Element that the DartType originates from, and then, if it is a ClassElement, simply return all of the super types, otherwise perhaps default to an empty list:
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
/// Returns all sub-types of [type].
Iterable<DartType> getSubTypes(DartType type) {
final element = type.element;
if (element is ClassElement) {
return element.allSupertypes;
}
return const [];
}
This is in analyzer version 0.29.3.

Struts2 annotation with parameter when using json plugin

Simply I want to evaluate a property of my action and use it's value within an annotation.
The following is exactly where I want to use it.
I want to define a excludeProperties parameter at run time.
Consider the following annotation which currently works on the action:
#Result(name = "success", type = "json", params = {"root", "model", "excludeProperties", "myCollection"})
There the actions model has a myCollection collection which I do not want serialized.
However I would like to create an exclusion String (a string will do for now).
If I create a getter setter for exclusion, I would expect the following annotation to work (which does not):
#Result(name = "success", type = "json", params = {"root", "model", "excludeProperties", "${exclusion}"})
Any ideas?
I have created actions similar to this answer which shows resolving a parameter within an annotation. I am using the named variable pattern matcher to extract values from the namespace... but I just can't seem to set this parameter no matter what I do.
Part of the issue was that I was working with entity objects and serializing collections was an issue. With your own custom JSON result type you can do what ever you want. Since created getter setter for jsonModel, I just constructed what I needed there. I don't need to worry about lazy initialization errors because you need to explicitly include collections with flexjson so if you just want the primitives (which I did) then flexjson is perfect.
This very simple result type using flexjson which worked for my needs:
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.util.ValueStack;
import flexjson.JSONSerializer;
import java.io.PrintWriter;
import org.apache.struts2.ServletActionContext;
public class Kjson implements Result {
#Override
public void execute(ActionInvocation invocation) throws Exception {
ServletActionContext.getResponse().setContentType("text/plain");
PrintWriter responseStream = ServletActionContext.getResponse().getWriter();
ValueStack valueStack = invocation.getStack();
Object jsonModel = valueStack.findValue("jsonModel");
//create json and put it into response stream
responseStream.println(new JSONSerializer().exclude("class").serialize(jsonModel));
}
}

Resources