Flutter from an iOS Perspective

Gepostet am: 29. April 2019

Flutter is an open source mobile SDK which can be used to build iOS and Android apps with the same code base. In this article we want to get a more detailed look from an iOS perspective to see how we can create Apps with a native iOS look and feel and how we can adapt our iOS Knowledge to Flutter.

My colleague Tino described the basics in his past few articles. If you haven’t read them you can find them here:

Flutter is using Dart as a programming language and since the first stable version was released there are many apps available. If you want to get an impression you can see a few apps created with Flutter on this page. Because Flutter is in active development you can see the Roadmap here.

Project

By default Flutter is creating an Xcode project in Objective-C. If we want to have our Xcode files in Swift we have to add the parameter -i swift. To create a Flutter project we can execute the following statement in the terminal:  flutter create -i swift [projectname].

Package Manager

The most widely used package managers in iOS are third party tools like CocoaPods or Carthage. In the generated Xcode project Flutter is using CoocaPods as well. The dart packages are managed by the so-called Pub Package Manager and we can manage dependencies in the file pubspec.yaml. When you add a package below dependencies it’s recognized as a package needed for our app to work. Dependencies needed only for development can be added below dev_dependencies. Once you add a package you need to run the command flutter packages get. A lot of available Dart packages are listed here.

Asynchronous Code

In Dart you can use async/await to execute asynchronous code. In Swift the pendant for this is a closure or a callback. It’s important to know that the code in Dart is only executed in a single thread. Asynchronous Code runs in separate isolates. Every isolate can run on a different CPU core but they can’t share memory. Instead they can only interact with each other by sending messages via ports (more information about ReceivePort and SendPort).

Architecture Patterns

In iOS we are using architecture patterns like MVC, MVP, MVVM or Viper. In Flutter there are good alternatives called ScopedModel and BLoC (Business Logic Component). BLoC is created and used by Google and ScopedModel is a third party package. You can find both here.

Design

Flutter includes two specific design languages which contain platform specific widgets. Material design for Android and cupertino design for iOS. To use the cupertino design we have to import it in our dart files.

Widgets

In iOS we are using the framework UIKit, which provides view and window architecture, event handling and different input types. Flutter has its own rendering engine which is using the concept of rendering a chain of widgets. There are two different types of widgets: stateful and stateless. A stateful widget is dynamic, able to dynamically change its own content. A stateless widgets is the opposite and not dynamic. It only shows the data passed into its constructor at creation time.

Storyboard

In iOS we are mostly using storyboards to create our user interface. In Flutter the user interface is written in code by creating these widgets and combining them to a widget tree. After a while it feels very familiar and because a lot of code can be reused, it doesn’t need much more time than the native iOS method. One big advantage is the support of stateful hot reload. That means that changes in the source code can be shown immediately on device or in the simulator without restarting the App. If you don’t want to generate everything by hand you can use the tool flutterstudio.

iOS-Layout-Structures

Flutter supports a few iOS based layout structures. A layout structure has the ending Scaffold and it describes what the root layout and the behavior structure should look like. CupertinoTabScaffold places the tab bar at the bottom, CupertinoPageScaffold places the navigation bar on top and the content is always placed in-between.

CupertinoPageScaffold CupertinoTabScaffold

UITabBarController

In iOS it makes sense to start the user interface with an UITabBarController. In Flutter we can replicate this by creating a widget with a simple tab bar containing elements of type BottomNavigationBarItem. We use the structure CupertinoTabScaffold with the widget CupertinoTabBar. In tabBuilder  we have to define the content of the tab bar entries.

UINavigationController

In iOS the UINavigationController is one of the most used navigation components. It allows to create a navigation stack with push and pop view controllers inside and there is also a navigation bar on top with more information like a title and back button. When we want to add a  UINavigationBar we can do so by creating a CupertinoNavigationBar widget. We define this inside a  CupertinoPageScaffold layout structure.

After placing the  CupertinoTabBarCupertinoNavigationBar widgets inside the  CupertinoTabScaffold and CupertinoPageScaffold layout structure, the Flutter app looks like this:

TabBar

Navigation

To add navigation options, eg. pushing a screen with the name "/detail" to our stack, we can use following method.

To pop this screen we can use.

We can add all the routes we want to use in our main.dart file.

UIBarButtonItem

In iOS we can add UIBarButtonItems to our navigation bar on the right/left as rightBarButtonItem or leftBarButtonItem. In Flutter we can add a CupertinoButton as trailing to our navigation bar to achieve a similar look to a rightBarButtonItem.

UIAlertController

In iOS we can show additional information with an UIAlertController. There are two possible styles: actionSheet and alert. In Flutter we have the CupertinoAlertDialog and CupertinoActionSheet widgets which can do this job. The following example opens a CupertinoAlertDialog when we are pressing the rightBarButtonItem in our navigation bar.

we can show an action sheet with a smiliar procedure. We have to use showCupertinoModalPopup instead of showCupertinoDialog and instead of a content field we only have  actions and cancelButton.

AlertAlert

UIWebView/SFSafariViewController

We can integrate a  UIWebView with the webview_flutter package. The simplest implementation is to create a WebView widget with the parameter initialUrl which is added as a child to CupertinoPageScaffold.

Instead of embedding web content we can open it in an external controller such as  SFSafariViewController. To do so we can use the url_launcher package.

WebViewWebView

UITableView

In iOS we use UITableView to display the content using rows arranged in a single column. We have datasource and delegate methods for UITableView: datasource to provide data that controls the state of the table view and delegate controls how to use the data and manages interactions. In Flutter instead of a table view there is ListViewIt takes a list of children and displays them in the scroll direction.

More Cupertino Widgets

There are a lot of more iOS widgets in the cupertino design package. In the following example we are using UISwitch, UISlider and UISegmentedControl as well as an  MKMapView -like component from the map_native package.

MoreCupertinoWidgetsMap

Good to know

In the next step we want to look at a few specific things needed in almost every iOS project. We want to execute native swift code, add our own pods to the CocoaPods installation created by Flutter and we want to take a quick look at fastlane integration.

Execute Native Swift Code

When we want to execute Swift Code from Flutter we can do so with a FlutterMethodChannel. In our next example we are getting the device information, once with Swift passing to Flutter and once with the device_info plugin directly. When we create a flutter project, flutter is automatically adding a CocoaPods installation to our Xcode project and is managing its own dependencies with it.

Adding a pod to the Flutter CocoaPod installation

We can simply add our own pod by adding it to the Podfile in the iOS subfolder. In the following example we added the pod 'Device'.

We need to add the following lines to  Debug.xcconfig and Release.xcconfig so that Xcode can find the added pods.

Method channels in Swift

After this we can add a FlutterMethodChannel in our AppDelegate by adding it in applicationDidFinishLaunchingWithOptions. The name „samples.inovex.com/native“ identifies the channel. The binaryMessenger is a facility for sending raw messages to Flutter.

We need to add a method call handler to our FlutterMethodChannel to handle defined methods. In our case we specified the method „deviceVersion“, which is given the return value from the method deviceVersion() as a FlutterResult to Flutter.

Method channels in Flutter

Back in Flutter we can access the defined method from our channel with these lines:

We can get the same information directly with the DeviceInfoPlugin from Flutter.

When we change code in Swift we can’t use the hot reload feature in Flutter. We have to quit and restart the app to see the changes. In the following screenshot we have two labels, one with the output from the native Swift code and one with the output directly from Flutter.

FlutterMethodChannel

Network Requests

For network requests we can use the http package and for JSON decoding we have to import dart:convert. A simple network request can be made with the get method from http.

User Defaults

In Flutter can we use SharedPreferences for storing and reading data from UserDefaults.

Fastlane

When the project can be built successfully from command line with the command  flutter build ios --release --no-codesign we can start with the fastlane configuration. The initial setup can be made with fastlane init from the ios subdirectory. After this we can edit Appfile and Fastfile and start a build with  fastlane [name of the lane]. This can be easily integrated in a continuous integration pipeline like Jenkins or GitLab CI.

Conclusion

Flutter is a very interesting technology and is getting a lot of attention since December 2018, when Google made the first stable version public. Cross-platform development is very popular because you don’t need more teams with different technology stacks. You can build everything with one team and one code base which is reducing cost and complexity. Once you understand the concept of Flutter you can develop effectively and with a lot of fun. Of course there are limitations but it’s a young technology with a promising future. We will follow the technology and we are curious how it will develop in the future.

Find out more about our Android and iOS portfolio or join us as a mobile developer!

2019-04-26T14:22:18+00:00