Flutter introduction from a Xamarin developer

Maybe you've never heard of me, but I have been a Xamarin developer for four years now, always obsessed with code-sharing strategies. You can't imagine how excited I was when I first discovered tools that allowed me to share a large amount of logic in the C# language, and today I feel the same way with Flutter. In this long post, I will try to share various reflections after several weeks of experimentation.

The tools are continuously evolving, so you can read things here that are no longer valid as the language and framework are continuously developed.

 

The language : Dart

Seriously ?

For many years I have seen very little information about Dart, and my first thought when I knew that the Flutter team chose this language for the core tools was :

> Why this language? There are plenty of already well-known languages, including Kotlin that is currently being pushed for Android development. This will slow down user adoption for sure...Anyway, after a few lines of code, I understood: this language is developed internally at Google, and they have total control over how it should adapt on every platform, and you can feel it. It doesn't have a long history behind it, with various compatibility constraints (like Java/.NET environment). So everything has been designed for what you are using today, and it's really pleasant to use.

A simple but powerful language

It is very easy to learn Dart syntax if you are familiar with OOP languages like C#, Java, Swift, Kotlin or Javascript. I made a quick selection of language specifics to make you more confident with it.

Modules

The strange thing when coming from Java or C# world, is the module oriented architecture (much like Javascript). It is common to have multiple classes in the same file, and also to declare elements at the module level since module imports can be explicitely named.

Statically typed

The language is statically typed, even if it may not be obvious at first glance, because of type inference and dynamics.

var myVar = this.calculateVar();
final myFinal = const EdgeInsets.all(10.0);

Again, type inference is also present in C#, but in Dart you can often omit it from declarations. To be honest, I am not a big fan of it and it can make code readability worse. But the Dart team could change it in the coming versions to make it more strict. The dynamic keyword looks a lot like the C# one too.

Declaring objects

There are several differences between Java or C#. First in Dart, interfaces are implicit: every class could be used as an interface with the implements keyword. Though, you can declare a class as abstract to make it not instantiable. Moreover, unlike typical languages, there isn't any keyword for defining a method as private or public. In Dart, you simply prefix your member by _ declaring it as private, otherwise, it is public. The interesting part is that a private member is visible from the library (aka module), so it's more an internal equivalent than private and it is clear that you have to think about your software architecture differently around modules. You can have static and read-only fields with final keywords. Syntactic sugar is also available to make constructor declarations simpler.

class User {  
    final String firstname, lastname;  
    String get fullname => "$firstname $lastname";  
    User(this.firstname, this.lastname);  
    String greet(String who) => 'Hello $who. I am real person.';
}

class Impostor implements User {  
    @override  
    String greet(String who) => "Hi $who. Ah ah, I'm an impostor!";  
    @override  
    String get firstname => null;  
    @override  
    String get fullname => null;  
    @override  
    String get lastname => null;
}

Type matching

It is pretty common to test the type of an instance, and Dart makes it easy by simply casting the object for you.

final result = getResult();
if(result is String) {    
    final length = result.length; // result is cast as String
} else if(result is int) {    
    final addition = result + 5; // result is cast as Int
}

Again, .NET developers have similar operations with C# 7 (and even more with pattern matching).

Named constructors

It is common to have static factory functions to instanciate objects in other languages to make clear of how they were constructed. Dart provides a way to name constructor variants for this purpose.

class Customer {    
    final String name;        
    Customer(this.name);        
    Customer.fromMap(Map map) : this.name = map["name"];
}

Asynchronicity

You also have all the helper functionalities you could expect from a modern language like async/await keywords and the Future type that greatly simplifies the state management of asynchronous operations. Here is a short example of an HTTP call to a REST API (it should look really familiar to any .NET developer that used HttpClient/Task/async/await/JsonConvert) :

/// Gets a list of customers from a distant 
server.Future<List<String>> getCustomers() async {  
    var httpClient = new HttpClient();  
    var uri = Uri.parse('http://company.com/api/customers');  
    var request = await httpClient.getUrl(uri);  
    var response = await request.close();  
    if (response.statusCode == HttpStatus.OK) {      
        var responseBody = await response.transform(UTF8.decoder).join(); // [ "...", "...", ... ]      
        return JSON.decode(responseBody).toList();      
    }  
    throw("failed to load customers");
}

Reactive programming

The Dart language has built-in Stream APIs that are well suited for reactive-like programming.

Future<int> sum() async {    
    final Stream<int> stream = this.openStream();    
    return await stream.reduce((a,b) => a + b);
}

There are already reactive extensions available on GitHub for having more control (retry, timer, throttle, combine, ...) over streams.

I wasn't lost at all so you shouldn't be

Many features seem to be borrowed from existing languages, with simplification and easiness of use in mind. I quickly felt at home, and you will see that it matches pretty well the goals of Flutter. I hope that you got the philosophy behind this cute language (it personally reminds me of the simplicity of Lua, which I love, but with a lot more functionalities).

Visit the Dart website to learn a lot more about it (there are more cool features like callable class, factory constructors, class: mixins) and DartPad to experiment with it right in the browser.

 

The framework : Flutter

Overview

Flutter is a cross platform mobile app SDK that is different from Xamarin and React-Native in the way the rendering is done. Google, that is the company behind Flutter, chose to re-implement the entire rendering pipeline on top of Skia and Dart.This means that you have basically the exact same visual experience on every platform that runs Flutter. It is a lot like Hybrid web-based approaches, but without the overhead of the HTML and javascript interpreters. Here you only have the mobile visual components needed and everything is ahead-of-time compiled so you have the best performance.Fortunately, inside of it, Flutter provides specific components that make the user feel like he is on his platform. So you have iOS specific scrolling behaviors, typography, icons. But, you are absolutely not forced to use the,, and that is the real power of Flutter : you are completely free since everything is included.The other great benefit of the engine is that you will have the same rendering on all system versions. If your user has an old 4.0 limited Android device he will have the same visual rendering as the user that has a modern Android.For anyone who debugged obscure visual bugs that appear on specific devices, or made dozens of Xamarin.Forms renderers, it is a real game changer.Check out the official technical overview for more details, and please watch this video from Eric Seidel who is the creator of Flutter.

The tools

Everything is Dart

There isn't any markup language for describing layouts (like XAML with Xamarin Forms or UWP): very unsettling at first. But you will see that it is perfectly adapted to the APIs functional-like philosophy. So, to describe any visual component on the user screen you will create a Widget class that implements a build method which explains how to structure the layout as a tree of nested objects. This method will be called every time your component changes and the Flutter framework manages the diff between this immutable tree and the previous one (you are a React-native developer? This should sound very familiar).

class HomePage extends StatelessWidget {  
    @override  
    Widget build(BuildContext context) {    
        return new Text("Hello world");  
    }
}

So, while this tree is immutable you will not be able to modify it afterwards. So, for example, for hiding a component you wouldn't change its visibility (like you would have done with native UI APIs or Xamarin forms), but simply don't include the component into the next entirely new built tree.

Widget build(BuildContext context) {    
    final text = Text("Hello world");        
    if(!this.isIconVisible) {        
        return text;    
    }    
    return Row(        
        children: <Widget>[             
            Icon(Icons.star),            
            text,        
        ]    
    );
}

Composability

After the previous paragraph you should be very worried about code readability for complex user interfaces.That's why everything should be split into multiple small components you then compose.Imagine you want a vertical list of users, you could do everything in one widget.

class HomePage extends StatelessWidget {  
    final List<User> users;  
    
    HomePage(this.users);  
    
    @override  
    Widget build(BuildContext context) {    
        return ListView.builder(        
            itemCount: users.length,        
            itemBuilder: (c,i) {          
                var user = this.users[i];          
                return Padding(            
                    padding: const EdgeInsets.all(10.0),            
                    child: Column(              
                        children: <Widget>[                
                            Text(
                                user.firstname, 
                                style: const TextStyle(fontWeight: FontWeight.bold),
                            ),                
                            Text(user.lastname),              
                        ],            
                    ),          
                );        
            }    
        );  
    }
}

This could to be difficult to understand for a newcomer, so instead you might prefer to split it into several classes.

class UserTile extends StatelessWidget {  
    final User user;  
    UserTile(this.user);  
    @override  
    Widget build(BuildContext context) {    
        return Padding(      
            padding: const EdgeInsets.all(10.0),      
            child: Column(        
                children: <Widget>[        
                    Text(user.firstname, style: const TextStyle(fontWeight: FontWeight.bold),),          
                    Text(user.lastname),        
                ],      
            ),    
        );  
    }
}

class UserListView extends StatelessWidget {  
    final List<User> users;  
    UserListView(this.users); 
    @override  
    Widget build(BuildContext context) {    
        return new ListView.builder(        
            itemCount: users.length,        
            itemBuilder: (c,i) => new UserTile(this.users[i]),    
        );  
    }
}

class HomePage extends StatelessWidget {  
    final List<User> users;  
    HomePage(this.users);  
    @override  
    Widget build(BuildContext context) => new UserListView(this.users);
}

A lot better, no? You should also have noticed that all the framework is architectured this way: instead of having a padding property in many components, simply nest any component into an Padding object. Have you also noticed how easy it is to create a ListView compared to native solutions? I find the APIs very well designed, all focused on developer experience, simplicity, and productivity. Congratulations to Google engineers.

Wide catalog of widgets

The number of widgets already available is just incredible. It's more adapted to material design, but you also have Cupertino components that look more like iOS native components. Every widget looks clean, I haven't seen any bugs while using it.Check-out the documentation and Flutter Gallery app for examples widget usage and demonstrations.The great thing is that you can make your own catalog of custom widgets very easily, by implementing your visual design guidelines, and everthing will run perfectly on all platforms.

Custom painting

All the rendering is made with Skia, and it is really easy to make a widget completely by drawing manually to a canvas. It is absolutely awesome when you have very specific components that don't fit native components, like charts for example.

This is perfectly doable in Xamarin with SkiaSharp (I used it for my Microcharts library back in the days), but here it is built-in and that follows the same approach than other components.

Any architectural standard?

Unlike Xamarin with its well-known Model-View-ViewModel architectural pattern, Flutter doesn't have highly integrated architecture guidelines for designing your application. However, you can adopt Facebook flux and Redux architectures since Flutter widgets are really similar to React components. There are already many community-driven libraries available for that purpose: green cat, built_redux, redux.dart. Even the Flutter team has experimented with it, although it seems to be more a guidance for developers than a standard.

A rich documentation

I have to say that the documentation is very complete and clear.

Since you have all the code cloned on your computer, you can navigate directly to it at any time and look at the implementation under the documentation comments too. Again, it is simple and handy. The Dart way to document code is also very smart: you don't have to list all the parameters in a list, but just mark them in your description. This removes a lot of boilerplate documentation that is almost always redundant with descriptions in other languages.

/// Defines a flag.
///
/// Throws an [ArgumentError] if there is already an option named [name] or
/// there is already an option using abbreviation [abbr]. Returns the new flag.Flag 
addFlag(String name, String abbr) => ...

Accessing native features with plugins

The Flutter team has included a way to access native features through plugins and platform channels. Yes, it should sound familiar to any Xamarin developer because you have the same concept: a common abstraction over native features implemented differently with platform APIs. The difference here is that you have to deal with native tools for implementing platform-specific code :

  • iOS : XCode, Cocoapods, Objective-C or Swift

  • Android : Gradle, Java or Kotlin

This seems more complex to implement at first compared to Xamarin (because there are C# binding/wrapper around every native API with Xamarin, but you have to switch between multiple environments with Flutter), but it is easier to integrate native dependencies because you don't have to create bindings for them. The great part is that the Flutter team already provides and maintains a great selection of plugins: connectivity, battery, shared_preferences, path_provider ...
I'm sure they took inspiration from Xamarin plugins.

 

Conclusion

The most important thing when developing software is what the user will have between his hands. You could have the best developer tools, but if the user interface doesn't feel good for the user it's not worth it. But Google did great with Flutter because the performance is excellent and the components are beautiful. But, even if they thought about system specificities with Cupertino widgets, it is arguable that your Flutter app could be less well integrated into the user's current operating system version than a native/Xamarin application. I personally find it interesting because it also means that you will have access to all widgets even on older system versions: no more specific UI bugs that could become a nightmare to fix.

It is always sad to admit it when you have invested so much time in a technology you love but today I feel a lot more productive with Flutter than I was with Xamarin and native solutions. The tools are stable, very coherent, the native feeling is real, the performances are astonishing, and when you are developing on a platform, you can be sure that it will look perfect at first launch on the other platform: all of this results in a really fun developer experience.

Finally, just try Flutter, you should understand really quickly why this post is so enthusiastic. I hope Microsoft teams will do so and take inspiration from it too!Thank you for reading.

Previous
Previous

Designing truly adaptative user interfaces