Dartosphere Aggregate

13 Mar 2017 | Filip Hráček

From GWT to AngularDart: a case study with source code

Lots of interesting articles about Dart have been cropping up on Medium.com lately. We've decided to cross post them here so that followers of this blog won't miss out on them. We'll start by Istvan Soos's GWT-to-Dart case study, published on Friday. — Filip Hracek



Earlier this year I was asked if there’s a good way to compare developing web UIs in Google Web Toolkit (GWT) vs. Dart, specifically AngularDart. Having worked with both GWT and Dart, I had a good idea of the differences, but as I thought more, I started to wonder how hard it would be to migrate a GWT application to AngularDart. This article describes what I’ve found while doing just that.

The GWT Mail Sample was an ideal place to start: it’s much more than a trivial example, with diverse features and complex UI interactions, yet it’s still manageable in size.

If you’re in a hurry, take a look at the working demo and the source code, or scroll to the bottom for the conclusions.

Screenshot of the demo.

The method of migration

I’ve done the migration without any automated compiler. While it may be possible to write a tool to automatically convert the GWT templates to Angular, or the backing Java code to Dart, I wouldn’t suggest doing it that way. Chances are that the template, the styling, or the Java code uses patterns that are better served with new approaches. I’ll list a couple of these new approaches in the next section.

My process was very simple. I put the GWT and the AngularDart code in the same git repository and did the following cycle:

  1. Select a small or medium-sized chunk of the GWT codebase. Usually this was a single widget, or a smaller part of a complex widget.
  2. Carefully explore the features and edge cases that are in the code. Most of a proprietary codebase will be some kind of edge case that somebody stuffed in it. If there are more than 5 edge cases, it is a good indication that the task is too large and needs to be split into multiple chunks.
  3. Implement the selected part in AngularDart. This sounds simple, because it is simple. :-)
  4. Delete the selected part from the GWT codebase. The GWT application will no longer build, but you can easily track the parts that you haven’t migrated yet.

You can check my progress and individual steps either by observing the commit history, or by looking at the log.md file that I was writing along as I was doing the changes. One interesting observation: writing down the changes took much more time than implementing the code in AngularDart.

Old solutions vs. new ones

The bulk of the migration is straightforward: simple templates and styles can be mapped to their Angular counterparts easily. I won’t waste your time with minute details; instead I’ll just talk about the parts that differ.

The first and most obvious difference is the design: the Angular version uses material widgets from the angular2_components package. The package has a lot of useful components, if you are starting with AngularDart, you should try to go with that. While both stacks have a great selection of ready-to-use components, and most of them have 1:1 mappings with regard to features and settings, sometimes you need to use your judgement on which to use or select.

Watch the new version alongside the old one. AngularDart Components make it easy to have nice animations and consistent component views.

I missed a few things that aren’t available yet in AngularDart Components, like a table and tree component. However, as you can see in the log, it was really easy to implement them with Angular’s *ngFor templates. For example, the list of mail items is handled by the following template, which is much simpler than GWT’s table initialization in MailList.java:

<div *ngFor="let item of items"
    class="row"
    (click)="selectRow(item)"
    [class.selected]="isSelectedRow(item)">
 <div class="col sender"></div>
 <div class="col email"></div>
 <div class="col subject"></div>
 <material-ripple></material-ripple>
</div>

I was told that team is working on open sourcing more components; fingers crossed that a more complex table will be included soon.

Less intriguing, but an important difference is the lack of static variables and the handling of singleton instances. In the GWT version, the list of email messages is stored in a static field in MailItems.java. In the Dart version, the same (and much more) functionality is handled by MailService, which is injected and better for unit testing.

I’ve started with a mock implementation for easier development (also to match the GWT version), while later on it is easy to plug in a real API behind the web client. For example, configuring the injection is as simple as the following code. Switching to a real implementation would be just changing the useValue: part of it.

Change to real implementation when ready.

On a more subjective note, I find the separation of code, HTML template, and CSS styles much better in AngularDart than in GWT. The GWT codebase allowed sprinkling the Java code with both styling details and template construction, and those parts were the hardest to migrate to a cleaner version.

End result by the numbers

While the AngularDart version has more features, and I’ve implemented custom components like the tree, overall it has less code than the GWT original. Depending on how you count, code size is down by 30%.

I was developing the application using Dartium, so I didn’t need to compile the Dart code to JavaScript; everything was done with a low-latency type-save-refresh cycle. However, the end result needed to be compiled to JS, and it may provide value to compare the numbers. On my Macbook Pro, the GWT applications’ compile times were:

  • 26 seconds for 1 permutations,
  • 11 seconds if the Java source code was already compiled, and
  • 40 seconds for all 5 permutations.

The Dart version took about 15–20 seconds to compile to cross-browser ES5. That is not blazing-fast, but it is faster than the GWT compiler.

Compile times (in seconds)

Last, a few notes about the output JavaScript’s size. The GWT compiler produces different JS version for each permutation (target browser), while the Dart compiler emits only a single compiled version. GWT’s output was roughly 207 KB / permutation (1M total for all 5, 84 KB/permutation when compressed), while the AngularDart version resulted in 689 KB (178 KB compressed).

Considering that the application logic is more complex (for example: diverse widgets, ripples, more detailed mail item handling), the larger JS size is not a surprise, although I hoped it would be smaller. Fortunately for us, a huge part of it is “fixed cost”, and additional components increase the size only a little (proportional to their complexity).

Conclusions

Both technologies originate from Google and both are capable of supporting larger team sizes, but they represent two very different eras. I have found the GWT code harder to read and reason about; the AngularDart version is more compact and easier for me. But don’t trust my point of view blindly. I strongly suggest that you try out the application and check the source codefor yourself.

I have also found the material components refreshing, and most of the time they were out of my way. Compared to that, styling and customizing GWT components is harder, requiring time that would be better spent on the application itself.

If you are looking to migrate your GWT app to a newer technology, AngularDart is a viable option. After all, Google is doing the same thing: they are migrating their critical apps from GWT to Dart (see their AdSenseand AdWords interviews).

— István Soós, March 10, 2017, canonical link
15 Feb 2017 | - Source

Dart SDK and Dartium 1.22.0 Chocolatey Packages for Windows

Chocolatey is a package manager for Windows similar to Linux's apt-get, based on Microsoft's Nuget tool and Powershell scripting language. It works from the command-line and from a WPF GUI interface.

You can install unmoderated versions by specifiying the install version during installation.
Chocolatey has also been updated with versions 1.22.0
Command line installation:
choco uninstall dart-sdk

choco install -y dart-sdk -version 1.22.0
To verify you have the right version on your path.
dart --version
Dart VM version: 1.22.0 (Mon Feb 13 17:39:28 2017) on "windows_x64"

Command line installation:
choco uninstall dartium

choco install -y dartium -version 1.22.0
14 Feb 2017 | Filip Hráček

Dart 1.22: Faster tools, assert messages, covariant overrides

Dart 1.22 is now available. It introduces a sync/async union type, assert messages, covariant parameter overrides, and much more. Tool startup is now much faster. Get it now!

Faster tool startup

We have switched to using application snapshots for running our SDK tools like dart2js, analyzer, and pub. This improves startup performance. See the AOT compiling talk at Dart Dev Summit 2016 for more information. Information about how to use application snapshots can be found in the SDK wiki.

Here are the improved performance numbers we see with the switch.


Assert messages

The fail-fast principle is crucial for building high-quality software, and assert is the simplest way to fail fast. But until now, it wasn’t possible to attach messages to asserts, so if you wanted to make your error useful, you were forced to throw a full exception. Like this:

num measureDistance(List waypoints) {
if (waypoints.any((point) => point.isInaccessible)) {
throw new ArgumentError('At least one waypoint is inaccessible.');
}
// ...
}

With messages in asserts, your code is not only shorter:

num measureDistance(List<Place> waypoints) {
assert(waypoints.any((point) => point.isInaccessible),
'At least one waypoint is inaccessible.');
// ...
}

But, more importantly, asserts are completely skipped in production, so your production code will be faster (it won’t have to iterate over waypoints at the start of every measureDistance() call).

Covariant parameter override

In object-oriented class hierarchies, especially in user interface frameworks, it's fairly common to run into code like this:

class Widget {
void addChild(Widget widget) {...}
}

class RadioButton extends Widget {
void select() {...}
}

class RadioGroup extends Widget {
void addChild(RadioButton button) {
button.select();
super.addChild(button);
}
}

Here, a RadioGroup is a kind of widget. It refines the base Widget interface by stating that its children must be RadioButtons and cannot be any arbitrary widget. Note that the parameter type in RadioGroup.addChild() is RadioButton, which is a subclass of Widget.

This might seem innocuous at first, but it's actually statically unsound. Consider:

Widget widget = new RadioGroup(); // Upcast to Widget.
widget.addChild(new Widget()); // Add the wrong kind of child.

Tightening a parameter type, that is, using a proper subtype of the existing one in an overriding definition, breaks the Liskov substitution principle. A RadioGroup doesn't support everything that its superclass Widget does. Widget claims you can add any kind of widget to it as a child, but RadioGroup requires it to be a RadioButton.

Breaking substitutability is a little dubious, but in practice it works out fine. Developers can be careful and ensure that they only add the right kinds of children to their RadioGroups. However, because this isn't statically safe, many languages disallow it, including Dart strong mode. (Classic Dart permits it.)

Instead, users must currently manually tighten the type in the body of the method:

class RadioGroup extends Widget {
void addChild(Widget widget) {
var button = widget as RadioButton;
button.select();
super.addChild(button);
}
}

The declaration is now statically safe, since it takes the same type as the superclass method. The call to select() is safe because it's guarded by an explicit as cast. That cast is checked and will fail at runtime if the passed widget isn't actually a RadioButton.

In most languages, this pattern is what you have to do. It has (at least) two problems. First, it's verbose. Many users intuitively expect to be able to define subclasses that refine the contracts of their superclasses, even though it's not strictly safe to do so. When they instead have to apply the above pattern, they are surprised, and find the resulting code ugly.

The other problem is that this pattern leads to a worse static typing user experience. Because the cast is now hidden inside the body of the method, a user of RadioGroup can no longer see the tightened type requirement at the API level.

Both problems are solved by covariant parameter overrides. To enable them on a method parameter, you mark it with the contextual keyword covariant:

class Widget {
void addChild(covariant Widget widget) {...}
}

Doing so says "A subclass may override this parameter with a tighter desired type". A subclass can then override it like so:

class RadioGroup extends Widget {
void addChild(RadioButton button) {
// ...
}
}

No special marker is needed in the overriding definition. The presence of covariant in the superclass is enough. The parameter type in the base class method becomes the original type of the overridden parameter. The parameter type in the derived method is the desired type.

This approach fits well when a developer provides a library or framework where some parameter types were designed for getting tightened. For instance, the Widget hierarchy was designed like that.

In cases where the supertype authors did not foresee this need, it is still possible to tighten a parameter type by putting the covariant modifier on the overriding parameter declaration.

The covariant modifier can also be used on mutable fields. Doing so corresponds to marking the parameter in the implicitly generated setter for that field as covariant:

class Widget {
covariant Widget child;
}

This is syntactic sugar for:

class Widget {
Widget _child;
Widget get child => _child;
set child(covariant Widget value) { _child = value; }
}

Learn more about this feature in the changelog or in the informal spec.

The Null type is now a subtype of every other type

In other words, the Null type has been moved to the bottom of the type hierarchy. The null literal was always treated as a bottom type. Now the named class Null is too:

final empty = <Null>[];

String concatenate(List<String> parts) => parts.join();
int sum(List<int> numbers) => numbers.fold(0, (sum, n) => sum + n);

concatenate(empty); // OK.
sum(empty); // OK.

FutureOr<T>

A lot of asynchronous code in Dart allows the use of a T or a Future<T>. For example, the callback to Future.then is declared to take a T but it doesn't specify the return type, since it could be an S or a Future<S>:

Future<S> then<S>(onValue(T value), { Function onError });

We are adding FutureOr<T> to support this use case. FutureOr<T> represents the union of Future<T> and T. So the signature of then now looks like this.

Future<S> then<S>(FutureOr<S> onValue(T value), { Function onError });

This tightens types in places where dynamic was used before (like with `then` above), and allows strong-mode tools to do better type inference. In non-strong-mode, FutureOr just means dynamic. In other places, where types were already tight but the implementation had to force asynchronous code, it will allow to loosen that requirement.

Generalized tear-offs are going away

Generalized tear-offs are no longer supported, and will cause errors. We updated the language spec and added warnings in 1.21, and are now taking the last step to fully de-support them. They were previously only supported in the VM, and there are almost no known uses of them in the wild.

Use of Function as a class is now deprecated

You can still use Function as a type name, but don’t use it as a class (don’t extend it, implement it, etc.). For example:

// This is deprecated.
class MyFunction extends Function {
// …
}

But this is still okay

// This is okay.
void myAwesomeMethod(Function callback) {
// …
}

Read the changelog for more information and additional changes.

This release took one additional week to finish (our regular schedule is a release each 6 weeks) but we think it was worth the wait.
11 Feb 2017 | - Source

Cellular Automata - Forest Fire

Cellular Automata has featured on this blog before - Conway's Game of Life. Whilst on the Rosetta Code site I stumble upon the Forest Fire model. Of course, a Dart version was soon underway - specifically designed to fit nicely on my phone's screen.

The model's rules are straightforward:


"A burning cell turns into an empty cell
A tree will burn if at least one neighbor is burning
A tree ignites with probability f even if no neighbor is burning
An empty space fills with a tree with probability p"

Watch the video below, try it out here(link fixed) or view the source code.

11 Jan 2017 | - Source

Sound Dart and strong mode

As of the 1.19 release, Dart supports an optional mode, called strong mode, that supports stronger static typing. Strong mode helps you find bugs sooner and contributes to making Dart a sound language.

To learn more about using strong mode to enable soundness, including the how, the why, and fixes for common problems you might encounter, see:
  • Sound Dart
    How and why to write sound Dart code, and how to use strong mode to enable soundness.
  • Sound Dart: FAQ
    A list of common questions for those who are interested in stronger static typing.
  • Sound Dart: Fixing Common Problems
    How to fix errors and warnings you may encounter when writing sound Dart code.
11 Jan 2017 | - Source

Set The Title Of A Web Page in Angular2 / AngularDart

This is just a tiny snippet for Angular2 / AngularDart that I decided was worth a blog post because it is A) a FAQ and B) has lots of weird Angular 1 workarounds on the web that may be time wasting rabbit trails!

How to set the web page Title from Angular?

import 'package:angular2/src/platform/browser/title.dart';
....
Title _title = new Title();
....
_title.setTitle(text);
10 Jan 2017 | Filip Hráček

Dart in 2016: The fastest growing programming language at Google, 2nd fastest growing in TIOBE Index

Dart was the fastest growing programming language at Google in 2016 with millions of lines of code written. It also made it to TIOBE Index Top 20 this month (see TIOBE's methodology).

It takes time to build something as ambitious as Dart and, in some ways, Dart is still in its infancy. But we're glad the hard work is starting to pay off.

Many thanks to our amazing community!

We're going to celebrate by ... releasing 1.22 next week (as per our usual 6 week release schedule).
17 Dec 2016 | - Source

Notepad 8080 and AngularDart Update

As detailed in previous posts, I have been working on a notepad application using AngularDart. There's not been a blog post for while but it has slowly and steadily progressed.

AngularDart has had a couple of big updates since I started this and not caused any issues. So I am finding it very stable and easy to relate to Angular1. There's a lot of Angular features that I haven't used in there yet so I will continue to scour articles and samples. That said, the focus for np8080 will probably be on the end user functionality rather than the implementation technology. Adding markdown support was very easy thanks to the package on pub - the real battle was with the CSS.

I am going to continue to develop the application - I use it almost daily in the day job for notes and as a scratchpad. Feel free to raise a feature request! I am pondering making an Electron version at least for Windows.

Looks quite good on my Chromebook! Try it out at np8080.win

Finally, on an unrelated note, for a limited time my Dart book is just $5 for the ebook version!

08 Dec 2016 | - Source

Dart SDK and Dartium 1.21.0 Chocolatey Packages for Windows

Chocolatey is a package manager for Windows similar to Linux's apt-get, based on Microsoft's Nuget tool and Powershell scripting language. It works from the command-line and from a WPF GUI interface.

You can install unmoderated versions by specifiying the install version during installation.
Chocolatey has also been updated with versions 1.21.0
Command line installation:
choco uninstall dart-sdk

choco install -y dart-sdk -version 1.21.0
To verify you have the right version on your path.
dart --version
Dart VM version: 1.21.0 (Thu Dec 07 06:52:21 2016) on "windows_x64"

Command line installation:
choco uninstall dartium

choco install -y dartium -version 1.21.0
07 Dec 2016 | - Source

Enhancing the Flutter developer experience

At the Dart Developer Summit we introduced our fast and powerful Flutter developer experience. But our ambitions don’t stop here, so we have been hard at work developing several updates that further improve the experience.

Faster startup during development


Hot reload means you only have to launch your app once; after that changes are simply reloaded into the running app. But even that initial launch should be really fast. Previously we used a loader application to bootstrap the device with your application sources. Thanks to recent improvements made to the reload engine inside the VM this is no longer necessary and your application will be booted immediately, and you will see the real launch experience of your app.

IntelliJ improvements


We have published an update to our IntelliJ plugin, version 0.1.6 that has several exciting changes:
  • Launching the app with hot reload support (see details below)
  • A new flutter action pane has been added on flutter.yaml files (see details below)
  • Keyboard shortcuts were added for hot reload and restart:
    • Hot reload: ctrl \ or ctrl alt ;
    • Restart: ctrl shift \ or ctrl shift alt ;
  • Flutter doctor now prints out the IntelliJ plugin version numbers
  • Improved the robustness of starting and stopping program runs

Launching the app with hot reload support


We have updated the behaviour of the IntelliJ Run and Debug buttons to both launch the app with Hot Reload support. If you want to run without hitting breakpoints, click the ‘Run’ button. If you want to trigger breakpoints, click the ‘Debug button’. 


Flutter specific action bar on flutter.yaml files


A custom action bar has been added for flutter.yaml files. This allows you to upgrade your flutter installation, and to run the doctor command for troubleshooting. 


Updates to reload semantics


In Todd and John’s Dart Developer Summit talk they discussed the semantics of hot reload. Since giving the talk there have been some important updates that refine the semantics of reload to improve the developer experience.

Const fields are always reloaded


Changes to fields that are marked const can now be reloaded. For example:

   const animationDuration = const Duration(seconds: 1);

*reload*

   const animationDuration = const Duration(seconds: 2);

After the reload animationDuration will have the duration of two seconds. You can keep making changes and each time you reload, your program will use the updated value.

New instance fields have their initializing expressions run

When you add a new instance field with an initializing expression, the VM will run that expression and assign the result to the instance field for all instances of the class. For example:

   class Foo {
     ...
   }

*reload*

   class Foo {
     ...
     String myTitle = ‘My Title String’;
   }

After the reload all instances of Foo will have the new field myTitle and it will contain ‘My Title String’. This is different than John and Todd’s talk which said myTitle will contain null.

When are initializing expressions run?

Initializing expressions are run as the last stage of a hot reload. Note that the call stack may not be empty. After we have safely performed the reload we find all instances of the class and run the initializing expression and store the result in the field. The VM will do this for all classes that received new fields but in no particular order.

Initializing expressions with side effects

Your initializing expressions can have side effects but the VM does not run the initializing expression against instances in a particular order. In other words, you cannot guarantee that a certain instance will have the initializing expression run before any other instance.

Initializing expressions that throw

If an initializing expression throws we log that it threw and leave the field as null.

Updating and feedback


To try out these enhancements, please update your flutter IntelliJ plugin (you should be prompted in IntelliJ; if not, select the menu item IntelliJ > Check for updates... and upgrade from there.). Next update your flutter installation by running flutter upgrade in a terminal, or by using the flutter.yaml action bar discussed above. 

Should you see any issues, or have suggestions or enhancement, please let us know in the issue tracker!