Technical Director Boris Goryachev talks about how Medusa worked on it for a year and why it was written in Flutter
On May 12, the release of new Medusa mobile applications (iOS, Android) – almost two years after we decided to rewrite them. Why so long? Why not native apps? Why Flutter? All this is told by the technical director of Medusa Boris Goryachev.
Unlike our old applications, we decided not to make new ones native. Firstly, writing the same code twice is tedious. Secondly, it will never work out so that in two different projects written by different people, everything is synchronous and the same. Usually someone works slower, someone goes on vacation, someone has a technical debt that needs to be closed. All our experience in creating and supporting native applications has prompted us – either we are doing something wrong, or this is simply not our way. And we began to look for ours. We tried React Native and Ionic, thought about the Basecamp approach – everything in the web + a thin native layer, even tried to go towards the Progressive Web App and stay on-line.
Around the same time, I attended a Google I / O conference and met people who make Flutter there. I immediately tried Dart and worked a bit on Flutter, but at that time the technology was not yet ready for Medusa: it was impossible to embed various interactive materials and embeds inside our materials, but this is critical for the media. So I decided to wait until Flutter grew up.
During the wait, we did a lot of things on the web side: rewrote the site, wrote a new version of AMP. We wrote and transferred all the projects of Medusa to ui-kit – a single library of components that the site uses and thanks to which a huge number of our game mechanics are possible. We divided desktop and mobile versions of the site, so that the distributing pages (main and section pages) began to be formed in two different places according to their own rules.
In parallel with the work on the site, we were thinking about a new application – we came up with its navigation, meanings, screens, features and so on.
In parallel, important changes took place in the structure of the technical department. We began to actively use the Google calendar and other meeting planning tools, and we switched from Trello to Basecamp. It is so obvious that it’s even strange to talk about it. But it took a lot of time and effort to streamline the chaos. A clear agenda of meetings, a quick follow-up, files that are not lost, and hill chart scopes made it possible to cope with a huge stream of tasks and not die.
Why is it still Flutter. And a little about Dart
Dart is a programming language that was developed by Google. It was announced in 2011, that is, it is still a young language. It has not become a major programming language (at least for now), but at the same time it is very actively used in the company itself. In addition to Google, there are other large companies, for example, Wrike, which use Dart and write full stack on it.
Since both Dart and Flutter are supported by the same company, this makes it possible to change the language to the needs of Flutter. As far as I know, both teams actively interact with each other, so chips constantly appear in the language, allowing you to write more pleasant code on Flutter.
I will not try to explain what Flutter consists of – Wikipedia will cope with this task better, but I will only say that among the authors there are people who have rendered in Chromium.
In December 2018, the Flutter team released the Webview embed library in Flutter. It was raw and, by the way, still has not left the Developers Preview Status, but this fact did not stop to begin to estimate the structure of the future application. For several months I experimented in my free time, and then a final decision was made.
At first I sat down to write a new version of the API specifically for the application. The ideology of this API can be formulated as follows: if something can be done on the backend, then you need to do it on the backend. And not because it is difficult to do something on the client. The fact is that releases on the App Store and Google Play are a laborious and lengthy process. You need to adapt to their work schedules and remember that not all users will immediately update the application. This can be avoided when the main logic happens on your server. Need to move the title 10 pixels down? You are welcome. Quickly remove or add a component? No problems!
For the same reason, the API for a mobile application contains the simplest components possible, from which almost any material is recursively collected. I say “almost” because we show games through WebView. The application does not matter what to show – a card, podcast, news or “Big Top”. Everything is processed by one code, and the task of the application is to take the component and render it (or go to the list of its children and call it recursively).
Another important argument in favor of Flutter: the developer “controls all the pixels.” When you need to make sure that there are correct shadows everywhere, as in the layout in Sketch, or you want transparency to change along curves, or you want font sizes and all typography to be customizable, everything is simple and realistic in Flutter.
Another killer feature of Flutter is development comfort. Hot-reload, which I am so used to in web development, and the fast speed of reloading the application without losing state make the job as easy as possible. You do not need to sit and wait until the application is rebuilt, then wait until the new code is working, and repeat the state in which you are working. Everything happens really fast and cool.
Although Flutter and Dart are not very popular technologies yet, we had no problems finding libraries. Most of what is needed in the application is either in the framework itself or on pub.dev. The community is very pleasant and ready to help. This communication is a real breath of fresh air, very supportive.
In addition, Google itself supports Flutter well. We use Firebase for pushing, analytics, profiles and storing user data (reading history, episode positions, bookmarks), and everything “just works” there. Of course, as a person who has never written native applications before, sometimes it’s wild to climb into gradle files or put properties in info.plist. But usually everything is googled, and there is nothing incredibly complicated.
You can talk endlessly about the difference between iOS and Android. As a rule, they immediately remember that patterns should be familiar to the user and that design guidelines must be followed. We agree with this, but not quite. It is important to remember about your corporate identity, and about common sense, and about the logic of behavior of users already familiar to you.
If you look at the history of the platforms themselves, we see that some patterns change with the devices, while others flow from the system to the system. These are not tablets, nothing stands still. There are dozens of cool examples where companies deviate from the recommendations of Google and Apple and do what they think is right.
The simplest and most vivid example is swipe back in iOS. The area of the screen that accepts swipe is super small by default, about 20 pixels. This is how the Mail application works on iPhones, and it is very inconvenient. On Twitter, this area is slightly expanded, while on Telegram, it is simply huge! Telegram on Android has its own special type of navigation from the chat list to chat, it is a cross between iOS and Android. Yes, the guidelines are cool, but on the iPhone XR, reaching your finger to the upper left edge of the device is very inconvenient. So we decided (of course, relying on data) that in the “Medusa” application, the “Back” button on iOS will be below. But on Android it will not be at all. To do this, there are svaypas (both our own and the new Android svaypas) and a system button “Back”.
Says my colleague, art director of Medusa Nastya Yarovaya:
I have small hands and a big phone. It seems ridiculous, but it affected two important decisions in the development process. First, I proposed to significantly increase the swipe zone back (increased to 50%) – the first deviation from the guidelines. Then, when we worked on navigation and function buttons inside the material, I suggested putting the back button in the general menu below – this is another deviation from the usual pattern in the interface. Now you can reach it with your thumb, because they are mainly scrolled by it.
We know that we will definitely fly for the fact that we have violated a number of recommendations of Apple and Google. But, if anything, we also use telephones and understand that many patterns are outdated, since they were invented for the devices by half.
A little practice: how it works in Flutter
After React and other reactive frameworks, you can build a prototype on Flutter in a couple of days. General idea of Flutter – everything is a widget. Flutter gives two (actually three) paradigms: you can use material widgets that follow the concept of material design. You can use Cupertino widgets – widgets that look and behave like on iOS. And you can write everything yourself.
Roughly speaking, there are two types of widgets that a developer interacts with: stateless and stateful.
Stateless widgets are plus or minus widgets that do not contain logic inside them that should change the state of the widget.
Stateful widgets have a setState function (hello, react!). You change state, and the widget is redrawn.
But of course, a large application on simple setState will not go far, so a state management solution is needed.
There are several solutions for Flutter. For example, there is a BloC pattern in which streams and events from data sources are used, they contain business logic that provokes the birth of new events. These events are heard by widgets, and widgets respond to changes.
Those who come after React may want to try Redux. In theory, there should not be a problem if you wrote in React using this approach.
I tried both approaches and ended up choosing a third. The Jellyfish app uses Provider. This is a library that was not written by Google, but it is interesting that Google, having tried Provider at home, began to abandon its BloC and even decided to repeat Provider as a separate library. But in the end, he abandoned the idea of repeating someone else’s code and began to use and maintain what was born as open source.
The Provider device is pretty simple. This is a widget within which there is a state. This state is changed by functions that internally call notifyListeners (). Those widgets that must respond to changes are in the widget tree – they are either direct children of the widget provider or children of its children. When notifyListeners () is called, children get new values through the context and rebuild occurs.
Typically, providers are advertised at the very “top” of the application, and you can use many providers at once – each is responsible for its own business logic.
Now we use eight providers who are “listening” in different places of the application. For example, here’s how the bookmark provider works, thanks to which readers can save their favorite materials, read them offline and from the same place on different devices.
The reaction is added to the instance to changes to the document from Firebase, which contains keys to materials bookmarked by readers. For each onChange of this document, the associated variable in the provider is updated and notifyListeners is called. As a result, all widgets that listen to this provider show the correct icon.
Through providers, the application theme has also been switched. The application has ThemeProvider, which within itself contains two instances of themes – and, in fact, it’s just a Map with our naming of colors and the colors themselves.
As a result, the correct color comes to the widget like this: