From Realm to Flutter

Ilan Toren
5 min readOct 7, 2021

Previous entries dealt with using Compass to create the aggregation pipeline needed to transform the stored records in the collection into application-ready information (Mongo Compass to transform data to code) and on how to leverage that aggregation pipeline into a server-side function using a Realm application (More aggregation with less aggravation).

What the final app looks like

There are three stages to creating this application:

  • Creating a Realm function to create an entry point to be called from an application (see the previous post)
  • Building a simple API to call the function using the js-realm driver and nodejs.
  • Call Realm function and deserialize the API results from JSON to dart classes and adding markers and polygon to the GoogleMap in Flutter

Creating the Realm function:

The previous version of the function has been slightly changed to accommodate the deserialization from javascript to dart. Here is the new function:

Code for the revised restaurantsByNeighborhood Realm function

The new function differs in that instead of a List<List<double>> which Dart deals with as a List<dynamic,double>. Since null-safety is somewhat new to Dart (see explanation). On the other hand deserialization from a List<Latlng> is simpler. I could argue that one way is more correct, but I’ll admit to being somewhat frustrated with the documentation and no longer willing to fight the Dart serialization.

The HTTP API for use from Dart

In an ideal world, the interface between Realm and the Flutter application would be via the Realm plugin. That plugin for Dart is not quite ready for use in Flutter 2.0. When the plugin is ready the Dart code will directly call the Realm function, but bridging between Realm and Dart with a nodejs based server is simple to set up and is a good solution for a development project.

Steps to create a nodejs application:

  • mkdir projectname; cd projectname
  • yarn init
  • yarn add -D typescript @types/react @types/react-dom
  • yarn add realm

In order to implement the express/node server there are the familiar (if you use node at all) steps. Add dependencies, and server set-up.

The code for the express/node server is very simple, nothing fancy. You could go to a system where the server is behind HTTPS and throw in a reverse proxy to limit use from any given IP. You could also use that to cache some of the responses as any given response from Realm is 250kb. The most important future change is to go from one user per server to one user per call, i.e. add a username/password to the Dart client then pass that through the server to the Realm cloud service. And, of course, find some alternative to having the need for an express server by either getting a stable Dart SDK Mongo Realm or building a platform channel wrapping the deserialization code.

Points of interest in the process:

Protecting access to Google Maps and to Realm:

Realm functions access the atlas cloud database, so the records in the database are protected from change. In this project there is only read-access but if writes were to be added to the project (e.g. restaurant reviews) then it becomes more important. The maps API key, on the other hand, is from GCP (Google Cloud Platform). Keys can be restricted to known IP but it is always good practice to keep the keys separate from the code. The .env file in this project holds that information and the server using the dotenv dependency can bring sensitive or platform-specific variables into the express server. Another feature that should be noted is the cors dependency. Some clients will not connect with the server in its absence, including the android client. The code shows the most simple of uses of Realm: using an API token to log in and a simple call to a Realm function. Since there is only one API token for the server all clients share the same user. That isn’t scalable in the long run as it opens the server up to dos type attacks. A future version should use a different signature or use a cookie to store a username and password.

The big problem: Deserialization of JSON results into Dart objects

I use JetBrains IDE for development. It is a great environment(s) for Flutter as well as Node development. In dealing with the problem of deserialization of JSON into dart objects it is a straightforward process. For deserialization, the JsonToDart plugin (link has explanation) makes it easy to take the results of an API call as in http://10.0.0.7:8080/byNeighborhood?name=Upper%20West%20Side and use the JSON returned to build code for your project.

Dart configuration
* create a .env file under the root for api keys and other secrets that you don’t want to be exposed in your code

The above pubspec.yaml contains the dependencies for the Flutter project. Note that Realm isn’t there but there are dependencies for google maps: one for IOS and android and one for flutter-web. Flutter web is good for development and debugging, but it isn’t suitable for production as the maps API key is exposed within the produced map. Of course, the Flutter team has cautioned against using flutter-web for production. Another thing to note is that google maps is not compatible with macOs.

There are a couple of side points: The Google Maps API key needs to be created on GCP and included into AndroidManifest.xml and the IOS AppDelegate.swift. The instructions on the google_maps_flutter page should be sufficient.

Where to next?

First, the Autocomplete widget is good for input but adding an erase button to it would be better. Secondly, the markers look cluttered on the screen.

Map showing map icons one on top of the next

There is a fix for that I believe by using a MarkerManger, adds a bit of complexity but it looks worthwhile. Finally, most importantly is that all the types of restaurants are shown together. That is easy to modify in Realm but means adding another widget to the Dart application. That will be my next goal and hopefully with less of an interval since the holidays are behind us (חגי תשרי). Healthy Fall — Richard aka Ilan aka Mongo

--

--