Flutter – mobile cross platform technology overview.

Some time ago Google introduced a Flutter – technology for creating cross platform mobile applications. In theory app written in flutter should work as fast as native app and it’s development should be fast and easy. In this article I’ll look closer into it and compare different aspects of development a flutter app.

Programming language

Dart – it’s the language created and used by Google in Flutter. But why Dart? Most of all it’s easy to learn and it’s syntax is very similar to most of the popular object oriented languages – including swift and kotlin. Most of the common IDE’s such as Intelij Idea and Android Studio have support for dart. Beside Flutter works in the way that it needs to create and destroy a lot of short-lived objects very fast and Dart is suitable for this which influences performance of the apps written in Flutter.

User interface

What makes Flutter special when it comes to UI design? First of all it’s approach for creating it. Opposite to other mobile technologies it does not use XML/JSX/other templating language, does not offer any visual editor and there are no separate layout files such as xml/xib/storyboard. Instead it keeps everything together and all is written in Dart. You can think that creating layouts is much faster and easier using a visual tool and keeping UI code along with logic can be messy and make code more complicated and less readable. I also used to think this way, but when I tried it and wrote my first Flutter app I’ve changed my mind right away. I was shocked how easy and clear it is. Let’s consider a sample app code with comments removed.

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'You have pushed the button this many times:',
            ),
            new Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: new Icon(Icons.add),
      ),
    );
  }
}

That’s it! It’s all code required to run the app, and it even count button clicks! Everything is in one place and it’s quite readable. When you writing native iOS application you have to open storyboard, add view controller, put some components in it, add constraints, connect it with a class, add outlets and then switch between storyboard and code. It’s hard to remember everything. Did I set table delegate in code or in storyboard? Did I set the delegate at all? I have to switch and check. Flutter doesn’t have this and instead jumping across files you can focus on your job. Thanks to that creating UI is even faster than using a visual editor.

You’re missing visual representation of UI? No problem! Flutter offers a hot reload feature that allows changes you made in code to be reflected in app without loosing it’s state. This works fine even for big and complicated apps and thanks to this you can check your work instantly and no need to recompile, restart the app and navigate to a screen you’re working on.

In Flutter everything is a widget: a screen, UI component, even stylistic element or behaviour. Because of that building UI or defining it’s behaviour is based on wrapping widgets into other widgets to achieve desired goal, which makes laying out views easier. Layout elements such as padding is also a widget, so other widgets don’t have to have a padding property. If you want a padding, then you wrap widget in a padding widget and that’s it. This makes the API lighter, easier and more intuitive. It also gives a hudge possibilities of customizing and creating you own widgets from smaller widgets. The downside of that solution can be large code nesting when dealing with more complex views, but it can be prevented by dividing widgets into smaller widgets or splitting the code to methods.

Another pain for developers when writing native applications is lifecycle management. Due to his reactive nature Flutter doesn’t have this. You don’t need to worry about managing asynchronous data loading and local state management and coordinate it with UI. If asynchronous operation finishes and you want to change something in the widget – you just change it’s state and a widget will be repainted.

Performance

Flutter doesn’t use a Web Views or OEM widgets. Instead it uses his own rendering engine to draw widgets which results in better performance. It has very thin layer of C/C++ code and most of it’s systems are written in Dart. When building apps the C/C++ engine code is compiled with Android NDK or with LLVM for iOS. The Dart code is compiled using AOT (ahead of time) compiler into a native ARM library. That library is added to the “runner” project and it is builded just like a native application into .apk or .ipa. When launched, the app uses Flutter library to handle events, rendering etc. You can see it on the High-level overview diagram below.

(source: https://docs.google.com/presentation/d/1cw7A4HbvM_Abv320rVgPVGiUP2msVs7tfGbkgdrTy0I/edit#slide=id.gbb3c3233b_0_162)

According to flutter developers:

“Flutter’s widgets incorporate all critical platform differences such as scrolling, navigation, icons and fonts to provide full native performance on both iOS and Android.”

Flutter apps are compiled to native code, so performance is competitive with native apps. It aims to offer 60 fps, or 120 fps on 120 Hz devices. Using widgets that are fast makes apps working fast and smooth even with complicated effects and animations. Instead having a large number of layout constraints, each widget have it’s own simple layout model and Flutter use caching to avoid repeating layout the same widget couple of times. Reactive nature makes it render quickly and work fast, but in apps that changes in widgets are frequent and there is a lot of quick reloads it can be slower and more resource consuming than in native app.

Summing up

Flutter is still in early version. There are some bugs and shortcomings, but it’s getting better and it develops very fast. UI designing is fast and easy, you can create a beautiful widgets and animations pleasing to the eye which attract your customers. Code is clean, readable and writing mobile apps nice and effective. For me it’s one of the best mobile cross-platform frameworks and a good alternative for native technologies. It can be compiled both for android and iOS, so instead writing two apps you can write one and reduce needed time by half, without being worse than native applications in the matter of performance.

Resources: https://flutter.io/

Building a simple SRI list-details application

Sri (Scalajs React interface) is a library based on react-native, that allows to use scalajs programming language to build native Android and iOS apps. In this article I’ll show You how to build a simple app using sri. This app will be a simple stack-navigation app containing list and details views. Let’s get started.

First thing you need to do is to set up your environment. Those are the things you need to get started(You need homebrew to run following commands: https://brew.sh/, If you already have those installed you can skip this part):

brew install node
brew install watchman
brew install sbt
npm install -g react-native-cli

Next you need to create a new project. To do that follow instructions on: https://github.com/scalajs-react-interface/sri/blob/master/docs/GettingStarted.md#stack-navigation.
After you have created your project and have packager running use sbt ~ios:devand sbt ~android:devcommands to build it for both platforms. Those commands also run compiler that listen for any scala source files changes and compile it. When you’re developing You should keep one running (depending on platform you’re running) so changes you’ve made in the code are automatically builded and reflected in the app.

Using react-native run-iosor react-native run-androidyou can run your app to see if it’s working. It requires to have Xcode / Android Studio with AVD installed (for instructions on how to set up SDK and simulators see: https://facebook.github.io/react-native/docs/getting-started.html). It may be required to manually run simulator.

If you’re getting ":CFBundleIdentifier", Does Not Existerror while running on iOS, it may help to run react-native upgrade in the root directory of the project.
If you’re getting Application has not been registerederror, open src/main/scala/…/MobileApp.scala file and in line AppRegistry.registerComponent("main", () => component)make sure that in the place of “main” is the name of Your app.

At the beginning create a new scala file called ListScreen. We’ll use it to display a list of people. Class ListScreen must inherit from NavigationScreenComponentNoPS class so it can be display as a view. It should look like:

class ListScreen extends NavigationScreenComponentNoPS {
  def render() = {
    		View(style = GlobalStyles.wholeContainer)()
  	}	
}

Right now main view in our app is AboutScreen. It’s determined in package.scala file. To change default view modify line:

registerStackScreen[AboutScreen](navigationOptions = NavigationStackScreenOptions(title = "About"))

to:

registerStackScreen[ListScreen](navigationOptions = NavigationStackScreenOptions(title = "List"))

From now on when we run our app, it will display ListScreen view which is currently empty. To display a list, first thing we need to do is to create a model class describing a Person object. It should look like this:

trait Person extends js.Object {
 	val firstName: String
  val lastName: String
  	val description: String
  	val age: Int
  	val key: Int
}

Each object on the list need to have a key field unique to each object. Create an array with a example data which will be used to populate our list.

val persons = js.Array(
    	new Person {
      		override val firstName: String = "John"
      		override val lastName: String = "Smith"
      		override val description: String = "Lorem ipsum dolor sit amet, ius ad pertinax oportere accommodare, an vix civibus corrumpit referrentur."
      		override val age: Int = 30
      		override val key: Int = 0
    	}.asInstanceOf[Person],
    	new Person {
      		override val firstName: String = "Steve"
      		override val lastName: String = "Cooper"
      		override val description: String = "Te nam case ludus inciderint, te mea facilisi adipiscing."
      		override val age: Int = 28
      		override val key: Int = 1
    	}.asInstanceOf[Person],
    	new Person {
      		override val firstName: String = "Katie"
      		override val lastName: String = "Dixon"
      		override val description: String = "Sea id integre luptatum. In tota sale consequuntur nec. Erat ocurreret mei ei."
      		override val age: Int = 24
      		override val key: Int = 2
    	}.asInstanceOf[Person]
)

Define styles

object styles extends InlineStyleSheetUniversal {
  	import dsl._

  	val headerFooter = style(
    		height := 30,
    		width := 100,
    		alignSelf := "center",
    		alignItems := "center",
    		justifyContent := "center"
  	)
  	val separator = style(
    		height := StyleSheet.hairlineWidth,
    		backgroundColor := "gray"
  	)
  	val item = style(
    		flex := 1
  	)
  	val itemRow = style(
    		flexDirection := "row",
    		padding := 10,
    		backgroundColor := "#F6F6F6"
  	)
  	val text = style(
    		flex := 1
  	)
}

Create PersonComponent which will represent a single row of the list

class PersonComponent extends ComponentP[PersonComponent.Props] {
  	def render() = {
    		TouchableHighlight(
      			onPress = () => props.onPress(props.item.key),
      			style = styles.item)(
      			View(style = styles.itemRow)(
        			Text(style = styles.text,
          				numberOfLines = 2 )(
          				s"${props.item.firstName} ${props.item.lastName}\n${props.item.age}"
        			)
      			)
    		)
  	}
}

object PersonComponent {
  	case class Props(item: Person,
    onPress: Int => Unit)
  	def apply(props: Props) = CreateElement[PersonComponent](props)
}

Define separator, header and footer

val SeparatorComponent = View(style = styles.separator)(null)
  
val HeaderComponent = ViewC(
  View(style = styles.headerFooter)(
   		TextC("LIST HEADER")
    	),
    	SeparatorComponent
)

val FooterComponent = ViewC(
  SeparatorComponent,
    	View(style = styles.headerFooter)(
      		TextC("LIST FOOTER")
    	)
)

Define functions for rendering and onPress handling

private def renderItem(info: ListItem[Person]) = PersonComponent(
    	PersonComponent.Props(item = info.item, onPress = pressItem)
)

private def pressItem(index: Int) = {
}

Add a list to render method:

def render() = {
    	FlatList(
      		data = persons,
      		ListHeaderComponent = () => HeaderComponent,
      		ListFooterComponent = () => FooterComponent,
      		ItemSeparatorComponent = () => SeparatorComponent,
      		debug = false,
      		horizontal = false,
      		disableVirtualization = false,
      		renderItem = renderItem _
    	)
}

Now save changes, and if you’ve done all steps correctly, the app should display a list of people, but when you tap on someone, nothing happens. To add details, let’s create a new file named DetailsScreen. DetailsScreen class just like ListScreen class must inherit from NavigationScreenComponent and contains render method, but this time, we want to pass a Person which details should be displayed. You must declare, that during creation of a new instance of DetailsScreen.

class DetailsScreen extends NavigationScreenComponentP[Person] {
  	def render() = {
    		View()(
      			TextC(s"${params.get.firstName} ${params.get.lastName}\n${params.get.age}\n${params.get.description}")
    		)
  	}
}

Each view must be registered. Add below line to package.scala file, just after registration of ListScreen

registerStackScreen[DetailsScreen](navigationOptions = NavigationStackScreenOptions(title = "Details"))

To open details by clicking on a person on the list modify pressItem method like so

private def pressItem(index: Int) = {
  navigation.navigate[DetailsScreen](persons(index))   
}

Now your app displays a list, and when you tap on a person it shows details of that person.