There exist a lot of great articles highlighting the most useful tips and tricks for Flutter and Dart. They provide great value for both beginners and advanced developers.
This article is not one of them.
This is about the useless tips, the weird quirks, the boring facts and not needed tricks. They are either so absurd you won't ever need them, or so abstruse you don't wish to need them. But they deserve some love, too.
NoSuchMethod
On our list of things we can use how they are definitely not intended, there is one more we haven't covered yet. In Dart there exists the special noSuchMethod(Invocation i)
method that we can override in any custom class. As the name suggests, this method will be called when we try to access an instance member that does not exists.
For example take the class Person
with only one method greet()
and the overridden noSuchMethod(Invocation i)
. When we try to do person.hello()
instead of throwing an error, our noSuchMethod(Invocation i)
gets called with an Invocation
object containing all information about the failed invocation, like name of the method or any parameters. It also supports getters and setters. Just note that the person
variable has to have the type dynamic
otherwise the compiler would complain.
JsObject
Here's my pitch: In Javascript you can access properties of any object either using object['myProp']
or directly via object.myProp
. In Dart you can only do one at a time, either with a Map
or a custom class that defines myProp
as a field or getter/setter. You cannot however access an arbitrary property dynamically and extend an object at runtime.
Except with noSuchMethod
we can. We would have a class that internally just wraps a Map
, but externally uses noSuchMethod
to catch any property access using getters and setters.
You probably know that there are several reasons why this is a very bad idea to do. For instance it is a poor way to completely disregard any type safety that comes with Dart. So let's do it anyways!
First we define our JsObject
class. It just wraps a Map<String, dynamic>
and overrides both noSuchMethod
and toString
.
class JsObject {
final Map<String, dynamic> map = {};
@override
dynamic noSuchMethod(Invocation i) {
// TODO
}
@override
String toString() {
return map.toString();
}
}
We then fill in the implementation for noSuchMethod()
which is actually pretty straight forward:
@override
dynamic noSuchMethod(Invocation i) {
if (i.isGetter) {
return map[i.memberName.name];
} else if (i.isSetter) {
map[i.memberName.name] = i.positionalArguments.first;
}
}
The .name
is just an extension on Symbol
to get the actual name. It also strips the tailing =
that each setter invocation has. Lastly we need a helper function that creates a new instance of JsObject
but returns dynamic
to trick the compiler:
dynamic jsObject() => JsObject();
With that here is how to use it:
void main() {
var o = jsObject();
o.myProp = "Hello World";
o.year = 2022;
print(o.myProp); // prints "Hello World"
print(o); // prints "{myProp: Hello World, year: 2022}"
}
Void Variables
Now on to the finale. All the above tips and tricks were language features designed with a specific purpose which we have crudely disregarded and defaced. But did you know there is actually a feature that is designed to be useless?
The following quote was taken directly from the depths of the Dart Language Specification:
It could be said that the type void is used to help developers maintain a certain self-imposed discipline about the fact that certain objects are not intended to be used.
What a nice way of saying something is useless, isn't it?
So what does that mean? You most certainly know void
as the return type of functions that do not return anything. But you can also use void
as a variable type. You can get such a variable by assigning any other variable to a new void
variable and thereby making it completely unusable.
void main() {
var myString = "Hello World";
void myVoidVr = myString;
// what to do with myVoidVar?
}
Here is what you cannot do with a void variable: You cannot print
it. You cannot pass it as a dynamic
parameter. You cannot do .toString()
or .runtimeType
on it. You cannot compare it with ==
or !=
.
Here is what you can do: You can cast it back to its original type. That's effectively it. You can also return it from a void
function or pass it as a void
parameter, but that still leaves you with an unusable void
variable.
So the only thing you can use a void variable for is to throw it away. As per the Language Specification:
The special type void is used to indicate that the value of an expression is meaningless and intended to be discarded.
So how can we this time completely ignore any good practices or misuse a certain language feature? A feature that is particularly designed to be meaningless and not be used? Well, we use it of course:
First let's define a normal void Function()
. However instead of returning nothing, we return some value which we assign to void :
void getText() {
String str = "Hello World";
void result = str;
return result;
}
We also define a function that accepts a value of type void
and casts it back to String
. This will of course throw if the value is not actually a String
.
void printText(void text) {
var str = text as String;
print(str);
}
Finally we combine these two methods:
void main() {
var text = getText();
// text is void and cannot be used for anything
printText(text);
}
Why would you do it? Maybe you want to torture your coworkers. Maybe you want to torture yourself. For everybody else:
Please never use it!
Do you know any more useless or abstruse tricks? Please leave a comment.
Also I wanted to experiment with something you won't read everyday in contrast to the thousands of 'Tips & Tricks' articles out there. So please show some love in case you liked it or give some feedback.
Top comments (1)
When you are code reviewing some bullshit function from your coworker you could edit the code and assign the result from that function to a void variable. Just to make it clear how wrong your coworker is.