Why the List list = new List() execute before the constructor HomeContent?
class HomeContent extends StatelessWidget{
List list = new List(); // no matter I write this line code before or after `HomeContent()`
HomeContent() {
for(var i = 0; i < 21; i ++) {
this.list.add("I am item $i .");
}
}
Widget build(BuildContext context) {
return ListView.builder(
itemCount: this.list.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(this.list[index]),
);
}
);
}
}
The order in which you add class members has no effect on the order of execution. When constructing a new object, the class's properties are initialised before the constructor is executed.
Dart object initialization is defined to work the way you observe.
When you write new Foo(...) then the language allocates space for a Foo object and then goes through the initialization process for the Foo class and all its superclasses.
The initialization of an instance of Foo happens as follow:
First evaluate instance field initializers declared in Foo in source order.
Then evaluate the initializer list of the Foo class.
Then recursively initialize the instance as Foo's superclass, as specified by the super invocation of the initializer list (defaults to super() if you write nothing).
When done with that, execute the Foo constructor body.
When this process hits the Object class, the object is considered properly initialized, and it's made available to the constructor bodies that are executed on the way back.
As an example:
Object log(Object value) {
print(value);
return value;
}
class Super /* extends Object */ {
var superField6 = log(6);
var superField9;
Super(this.superField5) : superField8 = log(8), superField9 = log(9) {
log(10);
}
var superField7 = log(7);
var superField8;
var superField5;
}
class Sub extends Super {
var subField1 = log(1);
var subField4;
Sub() : subField3 = log(3), subField4 = log(4), super(log(5)) {
log(11);
}
var subField2 = log(2);
var subField3;
}
main() {
new Sub();
}
If you run this code, you will see the log calls happening in the order of their arguments.
Related
I am trying to make a source generator that would mimic C# anonymous objects, because they are great for when you are manipulating with collections (Select, GroupBy, etc.).
Imagine this code:
class Person {
final String firstName;
final String lastName;
final int age;
Person(this.firstName, this.age, this.lastName);
}
class TestClass {
final _data = [
Person('John', 'Doe', 51),
Person('Jane', 'Doe', 50),
Person('John', 'Smith', 40),
];
void testMethod() {
final map1 = _data.map((p) => _$$1(name: p.firstName, age: p.age));
final map2 = _data.map((p) => _$$2(fullName: '${p.firstName} ${p.lastName}', age: p.age));
}
}
Those _$$x objects are what I want to generate now. I need to somehow find them and find what is being passed into them, so my code generator would generate this:
class _$$1 {
final String name;
final int age;
const _$$1({required this.name, required this.age});
}
class _$$2 {
final String fullName;
final int age;
const _$$1({required this.fullName, required this.age});
}
but I cannot seem to even find method content:
FutureOr<String?> generate(LibraryReader library, BuildStep buildStep) {
for (final clazz in library.classes) {
final method = clazz.methods.first;
method.visitChildren(RecursiveElementVisitor<dynamic>());
}
}
it looks like the MethodElement doesn't have any children? so this doesn't look like the right way.
Is there any other way to find what I need?
A visitor can be used at the lower-level Abstract Syntax Tree to find the _$$x constructor invocations.
The visitor should also visit the whole library rather than just classes as is done in your example, in order to locate top-level usages as well.
The AST does not distinguish between constructor and method invocations, but we can use a series of checks to make sure that the invocation in question is an appropriate target for code generation nonetheless. In a similar fashion, checks can also be put in place to ensure that the invocation is done in a closure.
The following example implements this approach, and leaves you with a map of '_$$x' to MethodInvocations to work with:
FutureOr<String?> generate(LibraryReader library, BuildStep buildStep) {
final libraryElement = libraryReader.element;
final parsedLibraryResult = libraryElement.session
.getParsedLibraryByElement(libraryElement) as ParsedLibraryResult;
final libraryCompilationUnit = parsedLibraryResult.units[0].unit;
final selectorInstantiationLocator = SelectorInstantiationLocator();
libraryCompilationUnit.visitChildren(selectorInstantiationLocator);
final selectorInstantiations =
selectorInstantiationLocator.selectorInstantiations;
// ...
}
class SelectorInstantiationLocator extends RecursiveAstVisitor<void> {
final selectorInstantiations = <String, MethodInvocation>{};
#override
void visitMethodInvocation(MethodInvocation node) {
// Ensure that the invocation is an appropriate target for code generation.
// &= is not used in favour of the short-circuit && operator (https://github.com/dart-lang/language/issues/23).
// Stop if the invocation doesn't match the required prefix.
final className = node.methodName.name;
var isSelectorInstantiation = className.startsWith(r'_$$');
final classIndex = int.tryParse(className.substring(3));
isSelectorInstantiation =
isSelectorInstantiation && (classIndex != null && classIndex >= 0);
// No target will exist for a constructor invocation.
isSelectorInstantiation =
isSelectorInstantiation && node.realTarget == null;
// The selector instantiation should be done in an expression function body (=>).
isSelectorInstantiation =
isSelectorInstantiation && node.parent is ExpressionFunctionBody;
// The function body should be part of a function expression (rather than a method declaration)
isSelectorInstantiation =
isSelectorInstantiation && node.parent!.parent is FunctionExpression;
// The function expression should be inside an argument list.
isSelectorInstantiation =
isSelectorInstantiation && node.parent!.parent!.parent is ArgumentList;
if (isSelectorInstantiation) selectorInstantiations[className] = node;
return super.visitMethodInvocation(node);
}
}
I have this class:
class Entry {
final String id;
final List<ListEntry> listEntries;
Entry({this.listEntries}):
id = Uuid().v4();
Entry.withId({this.id, this.listEntries});
}
// create new class instance
var e = Entry();
Now when I call any method on e.listEntries I will get a NPE because it is not initialized. Is there a way to have it default to an empty list in case the constructor argument is not provided?
You can use a Factory constructor:
class Entry {
final String id;
final List<String> listEntries;
factory Entry({List<String> listEntries}) {
return Entry._(listEntries ?? []);
}
Entry._(this.listEntries):
id = Uuid().v4();
Entry.withId({this.id, this.listEntries});
}
You can initialize the field in the initializer list instead of using this.ListEntries (an initializing formal).
class Entry {
final String id;
final List<ListEntry> listEntries;
Entry({List<ListEntry> listEntries})
: this.withId(id: Uuid().v4(), listEntries: listEntries);
Entry.withId({this.id, this.listEntries})
: listEntries = listEntries ?? [];
}
// create new class instance
var e = Entry();
Here I reuse the Entry.withId constructor so I only have to write things once.
Admittedly, this is a little bit of an odd test case, but it's a problem I've ran in to. I have a class that takes a function as a parameter in it's constructor. I'd like to know if the function that was passed was called. Here's an example:
class TestClassMock extends Mock implements RealClass {
RealClass _real;
TestClassMock() {
_real = new RealClass();
when(callsTo("myNamedFunction")).alwaysCall(_real.myNamedFunction);
}
}
class RealClass {
String _name = "RealClass";
Function myNamedFunction;
RealClass() {
myNamedFunction = _theNamedFunction;
}
String _theNamedFunction() {
return _name;
}
}
class ClassThatCallsRealClass {
ClassThatCallsRealClass(Function func) {
func();
}
}
//The test
TestClassMock testClassMock = new TestClassMock();
ClassThatCallsRealClass caller = new ClassThatCallsRealClass(testClassMock.myNamedFunction);
testClassMock.getLogs(callsTo("myNamedFunction")).verify(happenedOnce);
So to explain a bit, ClassThatCallsRealClass takes a function as a parameter and calls it. If you were to pass in (Instance Of RealClass).myNamedFunction, this would in turn call the private function _theNamedFunction on RealClass. However, if you try to mock RealClass and redirect all calls from myNamedFunction to the RealClass myNamedFunction, this seems to fail. I don't see any clear way to get this to work, but I would think it'd be possible.
Any ideas?
In Dart, all functions are instances of class Function as you know since you pass an instance of Function to the ClassThatCallsRealClass constructor. Instances of Function have a method call() as shown here.
Meanwhile, Dart has a very good mocking capability described here (with thanks to #KWalrath for the update).
So all you need to do is test with mocks like with any other object. Just as described in the reference, create a spy for ClassThatCallsRealClass and a mock for your Function instance. Then use a verify(happenedOnce) on the call() method of the function.
To mock your function do this:
class MockFunction extends Mock {
call(int a, int b) => a + b;
}
var mock = new MockFunction();
mock(1,2); //returns 3
Of course the parameter list to call will match that of the real function. Pass mock to your spy on ClassThatCallsRealClass.
That worked for me:
library x;
import "package:unittest/unittest.dart";
import "package:unittest/mock.dart";
class TestClassMock extends Mock implements RealClass {
RealClass _real;
TestClassMock() {
_real = new RealClass();
when(callsTo("myNamedFunction")).alwaysCall(_real.myNamedFunction);
}
}
class RealClass {
String _name = "RealClass";
Function myNamedFunction;
RealClass() {
myNamedFunction = _theNamedFunction;
}
String _theNamedFunction() {
return _name;
}
}
class ClassThatCallsRealClass {
ClassThatCallsRealClass(Function func) {
func();
}
}
class MyFunc implements Function {
Function func;
String functionName;
MyFunc(this.func, this.functionName);
call() {
var inv = new MyInvocation(functionName);
func(inv);
}
}
main(List<String> args) {
test('xx', () {
//The test
TestClassMock testClassMock = new TestClassMock();
ClassThatCallsRealClass caller = new ClassThatCallsRealClass(new MyFunc(testClassMock.noSuchMethod, "myNamedFunction"));
testClassMock.getLogs(callsTo("myNamedFunction")).verify(happenedOnce);
});
}
class MyInvocation extends Invocation {
final String f;
MyInvocation(this.f);
bool get isGetter => false;
bool get isMethod => true;
bool get isSetter => false;
Symbol get memberName => new Symbol(f);
Map<Symbol, dynamic> get namedArguments => {};
List get positionalArguments => [];
}
testClassMock.myNamedFunction returns null so I call noSuchMethod directly instead which needs an Invocation.
Invocation is abstract so I created an implemented.
MyFunc is a class that wraps the function. MyFunc can be called as a function because it implements the call method.
Basically, that's what I'm trying to do:
ClassName
{
final OtherClass field;
ClassName()
{
field = new OtherClass(this);
}
}
It's not possible to assign a final field in a constructor body. The final field needs to be assigned before the constructor body, in the initializer list or on declaration:
class ClassName
{
final OtherClass field = new OtherClass(); // Here
ClassName()
: field = new OtherClass() // or here
{
}
}
As you can't use this in the initializer list or on the declaration, you can't do what you plan to do.
With null safety, you can initialize a final field in different ways:
At declaration:
class Foo{
final int bar = 1;
}
In constructor parameter (initializing formal).
class Foo {
final int bar;
// Initializing in constructor parameter.
Foo(this.bar);
}
In the initializer list.
class Foo {
final int bar;
// Initializer list
Foo() : bar = 1;
}
Combination of above two.
class Foo {
final int bar;
Foo(int value) : bar = value;
}
Use late keyword for lazy initialization.
class Foo {
late final int bar; // Initialize it later, maybe in a method
}
Since Dart 2.12 it is possible by using late keyword.
The code below prints 5:
class ClassName
{
final int var1 = 5;
late final OtherClass field;
ClassName()
{
field = new OtherClass(this);
}
}
class OtherClass {
OtherClass(ClassName object) {
print(object.var1);
}
}
void main() {
final object = ClassName();
}
Please see this and the following sections
Like the question at Dynamic class method invocation in PHP I want to do this in Dart.
var = "name";
page.${var} = value;
page.save();
Is that possible?
There are several things you can achieve with Mirrors.
Here's an example how to set values of classes and how to call methods dynamically:
import 'dart:mirrors';
class Page {
var name;
method() {
print('called!');
}
}
void main() {
var page = new Page();
var im = reflect(page);
// Set values.
im.setField("name", "some value").then((temp) => print(page.name));
// Call methods.
im.invoke("method", []);
}
In case you wonder, im is an InstanceMirror, which basically reflects the page instance.
There is also another question: Is there a way to dynamically call a method or set an instance variable in a class in Dart?
You can use Dart Mirror API to do such thing. Mirror API is not fully implemented now but here's how it could work :
import 'dart:mirrors';
class Page {
String name;
}
main() {
final page = new Page();
var value = "value";
InstanceMirror im = reflect(page);
im.setField("name", value).then((_){
print(page.name); // display "value"
});
}
You can use Serializable
For example:
import 'package:serializable/serializable.dart';
#serializable
class Page extends _$PageSerializable {
String name;
}
main() {
final page = new Page();
var attribute = "name";
var value = "value";
page["name"] = value;
page[attribute] = value;
print("page.name: ${page['name']}");
}