---
title: How to build your geolocation mobile app with Kuzzle & Flutter
description: In this article we will create an app using Flutter and Kuzzle, operating on open data, to see public bike stations of the city of Montpellier on a mobile app.
image: https://blog.kuzzle.io/hubfs/blog%20post%20hubspot_%20%20kuzzle%20flutter%20(1).png
---

[![Kuzzle logo](https://blog.kuzzle.io/hubfs/kuzzle-white-logo.svg)](https://kuzzle.io/)

- [FR](https://blog.kuzzle.io/fr)
- [Careers](https://kuzzle.io/company/recruitment/)
- [Contact Us](https://kuzzle.io/contact-us/)
- [Newsletter](https://kuzzle.io/newsletter/)
- [Blog Home](https://blog.kuzzle.io/)

- [Use Cases](https://kuzzle.io/use-case/)
- [Documentation](https://docs.kuzzle.io/)
- [Company](https://kuzzle.io/company/)

# How to build your geolocation mobile app with Kuzzle & Flutter

By Alicia Thermos on November 13, 2020

Example of an Open data app using Flutter and Kuzzle backend.

# Introduction

In this article we will create an app using Flutter and Kuzzle, operating on open data, to see public bike stations of the city of Montpellier on a mobile app.

For this we will need a backend part where we will retrieve the open data every minute and publish them as realtime data. Then a mobile app will display a map with a marker for each bike station, with its number of free bikes / total bikes. It'll subscribe to realtime messages (using websocket) to update the map when notified about a change.

 

Full source code here  [https://github.com/jenow/kuzzle-flutter](https://github.com/jenow/kuzzle-flutter)

 

# Prerequisites

 

For this we will use develop a Kuzzle application for the backend part and **Flutter** for the mobile app part.

In order for Kuzzle to work properly it needs an **ElasticSearch** and a **Redis** instance.

The easiest way to run an **ElasticSearch** and a **Redis** would be to use our cli [Kourou](https://docs.kuzzle.io/core/2/guides/essentials/kourou-cli)

 

```
$ npm i kourou -g 
$ kourou app:start-services
```

# The backend app

So let’s start by installing kuzzle:

 

```
$ npm i kuzzle
```

 

Then start writing our backend application by importing kuzzle and instantiate it:

 

```
const { Backend } = require('kuzzle')
const app = new Backend('bike-stations')
```

 

Now we need to extend the Kuzzle native API to add a new route, for getting all available bike stations. To do this we need to define a controller with one or multiple actions:

 

```
app.controller.register('bike-stations', {
  actions: {
    get: {
      handler: () => getStations()
    }
  }
})
```

 

Now we have a new API controller named “**bike-stations**”, exposing a single action named “**get**”, which we will use to retrieve all bike stations.

To know more about how controllers work in Kuzzle, see this guide: [https://docs.kuzzle.io/core/2/plugins/guides/controllers](https://docs.kuzzle.io/core/2/plugins/guides/controllers/)

 

Now let’s implement the **getStations** function. It needs to fetch XML data from the open data Montpellier web service, then convert it to JSON using the **xml2json** library:

 

```
const getStations = () => {
  return new Promise((resolve, reject) => {
    https.get('https://data.montpellier3m.fr/sites/default/files/ressources/TAM_MMM_VELOMAG.xml',
    res => {
      let body = '';
      res.on('data', (chunk) => (body += chunk.toString()));
      res.on('error', reject);
      res.on('end', () => {
        if (res.statusCode >= 200 && res.statusCode <= 299) {
          resolve(parser.toJson(body, {
            coerce: true,
            object: true
          }))
        } else {
          reject('Request failed. status: ' + res.statusCode + ', body: ' + body)
        }
      })
    })
  })
}
```

 

Also what we will do is to run a routine which will fetch these data and send them as a realtime message every minute. We will run that after we run our Kuzzle instance.

 

```
app.start().then(() => {
 setInterval(async () => {
   const res = await getStations()
   await app.sdk.realtime.publish('bike-stations', 'stations', res)
 }, 60000)
}).catch(err => {
 console.error(err)
})
```

 

And that’s all for our backend part. Now we just need to run it

 

```
$ node index.js
```

# The mobile app

Now let’s write our flutter app. First we will add the dependencies which are the **Kuzzle Dart SDK** and the **flutter** **map**.

 

```
dependencies:
 flutter_map: 0.10.1+1
 kuzzle: 2.0.1
```

 

## The model

 

**bike\_station.dart:**

```
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong/latlong.dart';
 
class BikeStation {
 final double lat;
 final double lng;
 final int total;
 final int free;
 
 BikeStation({
   this.lat,
   this.lng,
   this.total,
   this.free,
 });
```

 

 

This will represent a bike station with their location, number of free bikes and number of total bike.

 

Let’s also add a helper method to be easily able to add this station to our map by returning a marker widget with the right location and informations:

 

```
Marker toWidget() => Marker(
       width: 80.0,
       height: 80.0,
       point: LatLng(this.lat, this.lng),
       builder: (ctx) => Container(
         child: Stack(
           children: <Widget>[
             Container(
               alignment: Alignment.center,
               child: new Container(
                 width: 35.0,
                 height: 35.0,
                 decoration: new BoxDecoration(
                   color: Colors.red,
                   shape: BoxShape.circle,
                 ),
               ),
             ),
             Container(
                 alignment: Alignment.center,
                 child: Text(
                   '${this.free}/${this.total}',
                   style: TextStyle(
                       color: Colors.black,
                       fontWeight: FontWeight.bold,
                       fontSize: 13.0),
                 )),
           ],
         ),
       ),
     );
}
```

 

## The map

 

**map.dart**

```
class Map extends StatefulWidget {
 final List<BikeStation> stations;
 
 Map(this.stations);
 
 @override
 MapState createState() {
   return MapState(stations);
 }
}
 
class MapState extends State<Map> {
 List<BikeStation> stations;
 List<Marker> _markers = List<Marker>();
 
 MapState(this.stations);
  @override
 void initState() {
   super.initState();
 }
 
 Widget build(BuildContext context) {
   for (BikeStation s in stations) {
     _markers.add(s.toWidget());
   }
   return FlutterMap(
     options: MapOptions(
       center: LatLng(43.6100166, 3.8518451),
       zoom: 14.0,
       maxZoom: 18,
     ),
     children: <Widget>[
       TileLayerWidget(
         options: TileLayerOptions(
           urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
           subdomains: ['a', 'b', 'c'],
         ),
       ),
       MarkerLayerWidget(
         options: MarkerLayerOptions(
           markers: _markers,
         ),
       ),
     ],
   );
 }
}
```

 

This widget will represent our map with the list of bike stations.

You can see that we add our bike stations to our marker list by using our previously written method **toWidget**.

 

## The core

 

It’s time to write our core application by using our model and map previously written in our **main.dart** file.

 

### The main method

**main.dart:**

```
void main() {
 runApp(MyApp());
}
```

 

### MyApp class

 

```
class MyApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'Kuzzle & Flutter',
     theme: ThemeData(
       primarySwatch: Colors.blue,
       visualDensity: VisualDensity.adaptivePlatformDensity,
     ),
     home: MyHomePage(title: 'Kuzzle & Flutter Demo'),
   );
 }
}
```

 

Until now nothing fancy, this is the basics to create a flutter app.

We will now write the core of our application by calling our previously created custom Kuzzle API route to get the list of bike stations and then also subscribe to our collection to be updated of any changes about those bike stations.

 

1. Instantiate kuzzle
2. Connect to Kuzzle
3. Getting bike stations
4. Display everything

 

To instantiate Kuzzle simply call the [constructor](https://docs.kuzzle.io/sdk/go/2/core-structs/kuzzle/constructor/) by passing a protocol as parameter

 

```
kuzzle = Kuzzle(
     WebSocketProtocol(
       Uri(
         scheme: 'ws',
         host: 'my-kuzzle-host',
         port: 7512,
       ),
     ),
   );
```

 

 

 

Same to connect to our kuzzle instance simply call the [connect](https://docs.kuzzle.io/sdk/go/2/core-structs/kuzzle/connect/) method

 

```
kuzzle.connect();
```

 

 

Now let’s call our new API route to get all the bike stations. To do this we will call the [query](https://docs.kuzzle.io/sdk/go/2/core-structs/kuzzle/query/) method to do a request once we are connected to our Kuzzle instance.

 

```
kuzzle.connect().then((_) {
     kuzzle.query(KuzzleRequest(
       controller: 'bike-stations',
       action: 'get'
     )).then((res) {
       for (dynamic station in res.result['vcs']['sl']['si']) {
         setState(() {
           BikeStation bs = BikeStation(
             lat: station['la'],
             lng: station['lg'],
             total: station['to'],
             free: station['fr'],
           );
           stations.add(bs);
         });
       }
     });
   });
```

 

So here we build a **KuzzleRequest** which will represent a request to our custom controller and action. Once retrieved we will add them to a list of BikeStation. We do this inside a **setState** handler so our map will automatically update all our markers (we will see how we instanciate our map in the next step)

 

 

For now we have the logic to connect and get bike stations to our front-end. The class **MyHomePage** should look like this for now:

 

**main.dart**

```
class MyHomePage extends StatefulWidget {
 MyHomePage({Key key, this.title}) : super(key: key);
 
 final String title;
 
 @override
 _MyHomePageState createState() => _MyHomePageState();
}
 
class _MyHomePageState extends State<MyHomePage> {
 List<BikeStation> stations = List<BikeStation>();
 Kuzzle kuzzle;
 
 @override
 void initState() {
   kuzzle = Kuzzle(
     WebSocketProtocol(
       Uri(
         scheme: 'ws',
         host: 'my-kuzzle-host',
         port: 7512,
       ),
     ),
   );
 
   kuzzle.connect().then((_) {
     kuzzle.query(KuzzleRequest(
       controller: 'bike-stations',
       action: 'get'
     )).then((res) {
       for (dynamic station in res.result['vcs']['sl']['si']) {
         setState(() {
           BikeStation bs = BikeStation(
             lat: station['la'],
             lng: station['lg'],
             total: station['to'],
             free: station['fr'],
           );
           stations.add(bs);
         });
       }
     });
   super.initState();
 }
 
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: Text(widget.title),
     ),
     body: new Map(this.stations),
   );
 }
}
```

 

 

Now we should have a map with markers at each bike stations

 

![](https://blog.kuzzle.io/hs-fs/hubfs/Google%20Drive%20Integration/Kuzzle%20Flutter.png?width=441&name=Kuzzle%20Flutter.png)

 

#### 5 - Update in realtime

The last step is to receive the messages published by the backend every minute to update our bike stations.

For this we simply need to [subscribe](https://docs.kuzzle.io/sdk/go/2/controllers/realtime/subscribe/) to our index/collection where we publish our message.

 

```
kuzzle.realtime.subscribe('bike-stations', 'stations', {}, (message) {
       stations.clear();
       for (dynamic station in message.result['_source']['vcs']['sl']['si']) {
         setState(() {
           BikeStation bs = BikeStation(lat: station['la'], lng: station['lg'], total: station['to'], free: station['fr']);
           stations.add(bs);
         });
       }
     });
```

 

For every message we receive we clear the stations we already have and put the ones received. The **setState** will automatically tells flutter to re render our stations.

 

## That’s it

 

If you want to know more about Kuzzle please visit our [website](https://kuzzle.io/).

If you have any questions feel free to join our discord at [http://join.discord.kuzzle.io](http://join.discord.kuzzle.io/)

You can find more information about our [Javascript SDK here](https://docs.kuzzle.io/sdk/js/7/) and more about our [Dart SDK here](https://docs.kuzzle.io/sdk/dart/2/).

To know more about our realtime engine please visit [https://docs.kuzzle.io/core/1/guides/cookbooks/realtime-api/introduction/](https://docs.kuzzle.io/core/1/guides/cookbooks/realtime-api/introduction/).

 

You can find all the source code at [https://github.com/jenow/kuzzle-flutter](https://github.com/jenow/kuzzle-flutter)

 

- [Tweet](https://twitter.com/share)

![Alicia Thermos](https://app.hubspot.com/settings/avatar/47c07be51c528b59e92581e25bfcc712)

By Alicia Thermos on November 13, 2020

### Related posts

 What is Kuzzle?

 Kuzzle is an open source software platform for the Internet of Things and data management

Kuzzle IoT : Open Source Platform to simplify the deployment of your IoT applications [Explore Kuzzle IoT](https://kuzzle.io/kuzzle-iot-platform/)

Kuzzle Backend : Open Source Backend to develop better and faster your modern applications [Explore Kuzzle Backend](https://kuzzle.io/kuzzle-backend/)

Have a project? [Contact us](https://kuzzle.io/contact-us/)

[![Kuzzle IoT platform ](https://no-cache.hubspot.com/cta/default/3803374/9d98b274-c1b5-4f20-99c4-486f28793c8d.png)](https://cta-redirect.hubspot.com/cta/redirect/3803374/9d98b274-c1b5-4f20-99c4-486f28793c8d)

[![New call-to-action](https://no-cache.hubspot.com/cta/default/3803374/59ad3da0-065b-4753-9e8f-930aefee903d.png)](https://cta-redirect.hubspot.com/cta/redirect/3803374/59ad3da0-065b-4753-9e8f-930aefee903d)

### blog categories

- [Corporate (5)](https://blog.kuzzle.io/tag/corporate)
- [Digital twins (6)](https://blog.kuzzle.io/tag/digital-twins)
- [IoT Platform (13)](https://blog.kuzzle.io/tag/iot-platform)
- [Smart Building (4)](https://blog.kuzzle.io/tag/smart-building)

### Recent post

### subscribe

Please fill in the form below to subscribe to our blog

[Tweets by kuzzleio](https://twitter.com/kuzzleio?ref_src=twsrc%5Etfw)

PRODUCTS [![ASK A DEMO IOT](https://no-cache.hubspot.com/cta/default/3803374/89ff7ce1-a492-4366-81bc-1e8063958d28.png)](https://cta-redirect.hubspot.com/cta/redirect/3803374/89ff7ce1-a492-4366-81bc-1e8063958d28)

KUZZLE PRODUCT

[Kuzzle Backend](https://kuzzle.io/kuzzle-backend/)

[Kuzzle IoT](https://kuzzle.io/kuzzle-iot-plateforme-open-source/)

READY-TO-USE FEATURES

[User Authentication & Security](https://kuzzle.io/kuzzle-backend/)

[Real-time Database & Advanced Search](https://kuzzle.io/kuzzle-backend/)

[Real-time Geofencing](https://kuzzle.io/kuzzle-backend/)

[Real-time Pub/Sub Engine](https://kuzzle.io/kuzzle-backend/)

[Kuzzle Admin Console](https://kuzzle.io/kuzzle-backend/)

[Installation On Premise & Cloud Agnostic](https://kuzzle.io/kuzzle-backend/)

[SUCCESS STORIES](https://kuzzle.io/success-stories/)

[Smart City](https://kuzzle.io/success-stories/)

[Smart Building](https://kuzzle.io/success-stories/)

[Smart Industry](https://kuzzle.io/success-stories/)

[Smart Logistics](https://kuzzle.io/success-stories/)

[PRICING](https://kuzzle.io/contact-us/)

[Our pricing](https://kuzzle.io/contact-us/)

[DEVELOPERS](https://docs.kuzzle.io) [![Download Kuzzle](https://no-cache.hubspot.com/cta/default/3803374/29eeb7e7-633d-42a0-b95e-bfe382f33a48.png)](https://cta-redirect.hubspot.com/cta/redirect/3803374/29eeb7e7-633d-42a0-b95e-bfe382f33a48)

GUIDES

[Getting Started](https://docs.kuzzle.io/guide/getting-started)

[Essentials](https://docs.kuzzle.io/guide/essentials)

[Kuzzle in Depth](https://docs.kuzzle.io/guide/kuzzle-depth)

DOCUMENTATION

[SDK Reference](https://docs.kuzzle.io/sdk-reference/kuzzle)

[Koncorde](https://docs.kuzzle.io/core/1/guides/cookbooks/realtime-api/introduction/)

[API Documentation](https://docs.kuzzle.io/core/1/api/essentials/connecting-to-kuzzle/)

[Plugin Reference](https://docs.kuzzle.io/plugins/1/essentials/getting-started/)

[Elasticsearch Cookbook](https://docs.kuzzle.io/guide/1/elasticsearch/introduction/)

COMMUNITY

[Github](https://github.com/kuzzleio/kuzzle)

[Discord](https://discord.gg/tAEtyDZ)

[COMPANY](https://kuzzle.io/company/) [![Subscribe to our newsletter](https://no-cache.hubspot.com/cta/default/3803374/01fb3ce7-cf81-42f1-8069-49f402c4f4b2.png)](https://cta-redirect.hubspot.com/cta/redirect/3803374/01fb3ce7-cf81-42f1-8069-49f402c4f4b2)

ABOUT US

[Who we are](https://kuzzle.io/company/)

[Careers](https://kuzzle.io/company/recruitment/)

[How we work](https://kuzzle.io/company/)

[Press](https://kuzzle.io/company/press/)

[Partner](https://kuzzle.io/company/partner/)

[Contact us](https://kuzzle.io/contact-us/)

FOLLOW US

[Twitter](https://twitter.com/kuzzleio)

[Facebook](https://www.facebook.com/kuzzleio)

[Github](https://github.com/kuzzleio)

[Linkedin](https://www.linkedin.com/company/kuzzle/)

[Youtube](https://www.youtube.com/channel/UCHcEzVQoH10YSyxc7jD3SMw)

- [Privacy policy](https://kuzzle.io/privacy-policy/)

©2021 KUZZLE - All rights Reserved   
Elasticsearch and Kibana are trademarks of Elasticsearch BV, registered in the U.S. and in other countries.