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.
Operator overloading
In Part 1 I already introduced you briefly to operator overloading in Dart. And by now you can probably imagine some pretty neat and useful stuff you can do with it. Stuff like vector math, custom maps, … but let's not get distracted. Let's have some fun with it.
Here are all operators available to overload:
+ - * / ^ % ~/ ~ < <= == >= > & | << >> >>> [] []=
Almost all of them must receive a single parameter of any type and return a value of any type. []=
must declare two parameters and return void
. ==
must receive an Object
and return a bool
.
Actual percentage
Let's start simple. Say you are just learning to code and you come across the %
operator. When trying it out, it returns weird results that aren't really what you expected. Now, instead of learning about modulo and becoming a better programmer, let's just fix this obvious mistake:
class RealPercent {
RealPercent(this.percent);
final double percent;
double operator %(double n) {
return n * this.percent / 100;
}
}
With this simple class, you can do your percentage calculations as you intend to:
// get 20 percent of 110
var myPercentage = RealPercent(20) % 110;
// myPercentage == 22
DartX
Now, here is a fun one that I absolutely did not spend waaaaay too much time on getting to work. I was asking my flatmate if he had any ideas for use(less)-cases and he was like:
"You can overload < and > right? Why not make HTML in Dart?"
Misusing one programming language to mimic the syntax of another progr a markup language? That sounds reasonable and also very much in the spirit of this article.
Before I besiege you with my struggles, how I got there and how it works, let the following sink in a little:
void main() {
html <<#p>>'Hello World!'<<#p>>>'' | 'body';
}
This valid dart program will render a paragraph saying "Hello World!" to the body of an empty html page. Can it get any better?
Let's start at the beginning. Before we look at the implementation, we first have to lay out our syntax and define the grammar we use. The first thing you might notice that I am using <<
and >>
instead of <
and >
. The issue is that Dart does not allow chaining comparison operators in a single expression. Even if we use custom classes and override those operators, something like a < b > c
will always make the compiler complain with "A comparison expression can't be an operand of another comparison expression". We could solve this by using parentheses like (a < b) > c
, but that's not viable for us since this would break the illusion of writing html. So the next closest thing to use were the bit-shift operators. A nice side-effect is that dart also has the triple >>>
operator, which we will use to indicate a closing tag.
So with that our html <p>Hello World!</p>
becomes <<p>>Hello World!<<p>>>
in Dart.
Next, we have to look at the tags (p
) and the content (Hello World!
). We obviously cannot just write them out since we still need valid Dart syntax. For the content we can just use a string and wrap this in quotes. For the tag we could do the same, but I think it is nicer and more readable to use the symbol literal (#p
). Applied to our example this becomes: <<#p>>'Hello World!'<<#p>>>
.
Now, remember that we are still dealing with operators, more specifically binary operators. So we always have to have expressions on either side of an operator. In our case, that is currently not true for the first <<
and last >>>
. In the end we can just append an empty string to make this valid. For the beginning we could do the same, but considering the future implementation, we need an instance of a class that overloads the <<
operator for anything to actually work. So I decided on using a global html
variable, which has the nice side-effect of being explicit about wanting to start some html statement.
Again with all that we look at our current html snippet: html <<#p>>'Hello World!'<<#p>>>''
. For clarity let's also realize again that we are actually dealing with normal operators and value literals, we just moved some whitespaces around: html << #p >> 'Hello World!' << #p >>> ''
.
Now let's dive into the implementation. I will surely skip some parts to keep it concise, but you can always look up the full implementation on the DartPad I linked below. To figure out our classes and what operators to overload, let's go through our final snipped step by step.
Say the html variable is of class A
. A
needs then to overload the <<
operator and accept a Symbol
(html << #p
). We also need to return a new instance of a new class, since we are in another state of our grammar (inside an open tag); let's call it class B
. Class B
then needs to overload >>
to close the tag, and looking ahead it also needs to overload >>>
. Both operators must accept a String
for now (b >> 'Hello …'
). We can also return an instance of A
here, since the next step would again be to open a tag with <<
.
What this system also needs to support is nested html tags. Luckily (or by clever design…) our current system already supports that with one slight catch. Because again we are dealing with binary operators, we cannot simply do <<#div>> <<#p>> ...
because that would leave >>
and <<
without any value in between. In those cases, we need to insert an empty string between the operators, which works because >>
expects a string anyways.
There is a lot more that affected the final implementation, like operator precedence and associativity. But I will spare you with any more dry implementation details. Go look it up if you want, or stay for some more amazing features of this new language in a language.
First since we are still in normal Dart, instead of using string literals we can just put in a variable for the contents of a tag:
void main() {
var title = 'Hello, World!';
html <<#p>> title <<#p>>>'' | 'body';
}
Second, I took the time to also include attributes in the implementation. You can add attributes to a tag using the []
operator and providing a Map<Symbol, String>
. This can again be either a literal or a variable and looks like this:
void main() {
html <<#p[{ #id:'my_id' }]>> 'Hello World!' <<#p>>>'' | 'body';
}
Third, instead of only accepting String
s as the content of a tag, you can also provide an instance of a previous html statement. This makes this grammar able to handle components much like React:
void main() {
var component = html <<#p>>'Hello World!<<#p>>>'';
html <<#div>> component <<#div>>>'' | 'body';
}
Fourth, you can already provide multiple child elements like this:
void main() {
html <<#div>>''
<<#p>>'First'<<#p>>>''
<<#p>>'Second'<<#p>>>''
<<#div>>>'' | 'body';
}
To step it up even more, additionally to String
s and single components, the final implementation also accepts a List
of components. This enables you to dynamically generate child elements:
void main() {
var items = ['First', 'Second', 'Third'];
html <<#ul>>[
for (var i in items) html <<#li>> i <<#li>>>''
]<<#ul>>>'' | 'body';
}
Fifth, the resulting element has a render(String target)
function, but of course there is also an operator for that: |
inspired by the unix pipe operator.
Bringing it all together, let's pause for a second and admire the full beauty and abomination of our creation:
void main() {
// some inputs
var title = 'Hello, World!';
var items = ['First', 'Second', 'Third'];
var component = html <<#p>>'Let\'s html'<<#p>>>'';
// constructs and renders the html
html <<#div>>''
<<#h1[{#id:'title'}]>> title <<#h1>>>
component
<<#ul>>[
for (var i in items) html <<#li>>i<<#li>>>''
]<<#ul>>>''
<<#div>>>'' | 'body';
/* produced html (formatted):
<div>
<h1 id="title">Hello, World!</h1>
<p>Let's html</p>
<ul>
<li>First</li>
<li>Second</li>
<li>Third</li>
</ul>
</div>
*/
}
Useless Tips for Dart you probably never need. (Part 3)
Kilian Schulte ・ ・ 4 min read
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)
That's wonderful if you are creating a static website generator in Dart.