Cross-platform app development is making a lot of noise these days. As software houses along with their clients are scrambling to keep up, we take an in-depth, technical, look at the 2019 top-rated cross-platform frameworks. I’m hoping to give you a bit more than just a general look or advice on what to choose.
Table of contents
- React Native – the story so far
- The short story of Flutter
- How does React Native work?
- Building UI -React Native
- The data flow
- How does Flutter work
- Everything is a Widget
- Building UI – Flutter
- The architecture
- Familiar solutions
- React Native
– Hot / Live reload
– React Native CLI
- Testing – React Native
- Building app releases
– OEM Debugger (Original Equipment Manufacturer)
- Testing – Flutter
- Flutter – Learning curve
- Supporting newcomers
- Easy building
- Community support
- Not reinventing the wheel
React Native – the story so far
For a more general view of the framework, read about Pros and cons of React Native.
The framework’s quick rise to fame created quite a few misconceptions as to its capabilities and use. You can read more about it in our article “7 React Native myths vs reality”
The short story of Flutter
The first version of Flutter, code-named “Sky”, was presented in 2015 at the Dart developer conference. The first stable version, allowing to compile native Android and iOS applications from one code Version 1.0 was released at the Flutter Live event, December 2018. Among the top advantages of the framework, the creators mentioned smooth operation level with native applications, and maintaining the state of the Hot Reload application. The initial reception of the new framework were very positive, and first flutter reviews gave us a lot to look forward to.
How does React Native work?
React Native contains a set of standard UI elements, equipped with bindings to native components, to be used in the application according to the operating system. The components can be freely grouped in the hierarchical structure of the declarative view. They can render other declarative components, or components with native bindings.
Component (React.Component) is a fundamental concept in React Native. It is an object (or function) that has “props” and “state”. For given props and state, the component should return a view, but only when props or state change. Props are a type of arguments for a component that are set from the outside – the component has no control over them. State, on the other hand, is internal, private data, entirely and exclusively under the control of the component.
The data flow
React Native uses unidirectional data flow, which distinguishes it from other declarative frameworks such as Angular (two-way data binding). This results in better performance and easier debugging. The one-way data flow in React Native is done by giving the component in the rendering phase read-only props and state. It can freely transform them, pass them down the view hierarchy, but shouldn’t be able to mutate them directly. The same goes for child components that will receive this data as props. Modifying props or state directly will not refresh the view. To do this, use the component’s setState method (or a similar function when using Redux or other solutions). Event handling by child components should be accomplished by passing in callbacks’ props to handle events that ultimately implement the parent’s setState as needed.
How does Flutter work
Everything is a Widget
The basic building block of the user interface is the Widget. The operating principle is similar to React Native – Flutter’s documentation actually mentions React as inspiration. Individual elements are arranged in a hierarchical structure, rebuilt each time the state of the application changes. This does not mean, however, that the view is being built again and again. The returned structure is only an intermediary on which the framework modifies the visible interface. In this way, Flutter replaces the imperative style known from native platforms with a declarative style, taking the burden of writing and managing transitions between possible interface states off the programmer’s shoulders.
Flutter provides a wide range of ready-made widgets that comply with Material Design guidelines. The programmer has, among others, buttons, text boxes with titles, or wrappers that manage the position of beams or floating buttons. Arranging items on the screen may cause some confusion as we define the position and looks of components by placing them in containers. This often causes the structure to grow and forces you to remember the behaviors and relationships between several types of containers.
The BLoC (Bussiness Logic Component) architecture is one of the solutions proposed by Google. It places use cases in separate classes. Communication with them takes place via asynchronous streams, where the interface publishes events in the appropriate BLoC “inputs” and at the same time, listens to the outputs for the flow of subsequent application states. The intermediary between these components and the interface is the StreamBuilder widget, which receives data from streams. The basic assumption of this architecture is to adapt the application to operate in an asynchronous environment – where a variety of data flows in at any time and in any order.
Native programmers familiar with RxJava and MVVM architecture will definitely find this to be a comfortable solution. However, this is not the only way, as Flutter can work with e.g. Redux and then state can be propagated by the components themselves. This process can be simplified by InheritedWidget which will shorten the path to the target component to which the data must travel. Provider can also be used as a solution, being a library combining Dependency injection and application state management. With its help, the programmer can provide the necessary values to the appropriate locations in the interface. You can inject any objects, simple data, streams, or objects observing data changes (eg ChangeNotifier).
All of the above solutions are well documented and explained in official guides. This will help many programmers to implement solutions they already know.
Now let’s compare some of the main tools available to programmers working in cross platform frameworks.
Hot / Live reload
All you have to do is install one Expo application on your phone, and you can download the RN application code from the internet or the QR code and instantly update it when it changes. It is not suitable for creating production applications, but rather for rapid prototyping. If we just want to add real push notifications, Crashlytics or any library not included in the Expo SDK, we must first complete the work with Expo and export (expo eject) the project to the “full” version.
We don’t even have to install Expo on the phone, as we can simulate the application in the browser!
React Native CLI
A program to generate a new React Native project. Unfortunately, very limited options, and the program is used from the terminal.
Usually there is a lot of initialization work left to be done e.g. generating certificates, adding dev / prod variants or hooking up Crashlytics. You can prepare a project template to initiate new projects faster, but React Native environment is changing so quickly that we would only use such a template once. Poor in comparison to project designers in Android Studio / XCode.
There are several frameworks for React Native. By default, “Jest” is enabled. We can easily add new unit tests by putting them to __tests__ and running the “npm test” command. You have to be careful when importing native modules – you need mocks, and these are often missing in npm packages, or work poorly.
Building app releases
Everything has to be done natively and we are limited to what Android Studio / XCode offers. We have 2 platforms to manage and there are no advantages of React Native hereFLUTTER
Flutter offers plugins to facilitate work in Android Studio and Visual Studio Code. They support project creation, debugging and the Hot Reload function.
A tool for debugging and profiling apps, from your browser. It allows, among others, to browse the interface in the form of a tree and to check individual widgets. It also allows you to track memory usage, CPU resources and app performance testing.
OEM Debugger (Original Equipment Manufacturer)
It allows you to debug not only Dart code, but also files generated for a specific platform. This is especially useful when writing your own system solutions.
Flutter offers its own plugin designed to perform unit and integration tests, and check widgets. For mock dependencies, the official documentation recommends Mockito.
Flutter supports flavors which allow you to configure various build variants, search among existing Android’s productFlavors and iOS’ schema. You can refer to them directly in the Dart code or call them out from Flutter CLI, but they must be configured separately for each platform first. The process has so far been described in Flutter community articles. What’s up with that, Google?
To prepare the release version, it is necessary to configure each platform individually. The documentation describes the process of editing individual files, but knowledge of Android and iOS will definitely speed up the process.
Flutter – Learning curve
Google actively encourages developers to learn and switch to Flutter. There’s plenty training materials available in form lectures and video tutorials.
The documentation is comprehensive, laden with examples, and thoroughly explains various aspects of working with the framework- from building an interface to writing tests. The authors of the framework gives us recipes for creating a variety of elements of modern applications. They also propose a straightforward and Flutter-friendly architectural model – the BLoC. The section dedicated to programmers familiar with other popular platforms, both mobile and web, is a huge help. It explains the key differences between Flutter and other programming environments.
Cross-platform frameworks keep developing and are gaining ground across IT landscape. Solutions that produce applications similar to native are definitely displacing hybrid solutions, offering better performance as well as native appearance and behavior of the application. Flutter and React Native seem to be top players important in cross-platform field.
The market shows a lot of interest in React Native apps. This comes not only from budget-conscious startups, but also from largest global brands. The framework seems the perfect choice for creating MVP versions of the software.
Both frameworks build views in a declarative way. They introduce analogous concepts of Component and Widget, as basic building blocks of applications, operating on the basis of an unchanged state. Each of them also offers a Hot Reload function that allows you to quickly reload a working application and see introduced changes.
At the core, both technologies are quite similar. They use a declarative UI building style and are quite flexible, allowing the developer to apply various techniques to manage the state of the application. Access to device features is provided by plugins.
React Native in this field relies more on the community, having several separate libraries for handling the same tasks. This can be both a disadvantage and an advantage. Flutter remains under greater control of its authors, who continuously keep providing ready-made solutions. At the same time, they cooperate with a growing community for missing tools, such as database support.
Not reinventing the wheel
That said, both technologies have certain levels of entry. Developers experienced in native app development will have to assimilate patterns known from web productions rather than what their used to, whereas front-end devs will have to face the specific patterns of developing for mobile platforms. Knowledge of particular platforms will also be useful, if only to improve the process of building and implementing applications.