OVO Tech Blog

Build An App To Track Santa With Flutter

Introduction

Lyle Dean


Build An App To Track Santa With Flutter

Posted by Lyle Dean on .
Featured

Build An App To Track Santa With Flutter

Posted by Lyle Dean on .

At Kaluza we've used Flutter to experiment with building a native app this year. This article will demonstrate how to build an app with Flutter that will be able to add to the festive fun by tracking Santa!

*Note: the Santa Tracker Google API only works on the 24th of December

Firstly we are going to build on top of this great tutorial on Adding Google Maps to a Flutter app by Google and add a festive sprinkle to it.

After following that tutorial you should have something that looks like the following.

Now we need to add that festive cheer to it! We are going to edit lib/main.dart and change the title and background color to a red accent.

  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Santa Tracker'),
          backgroundColor: Colors.redAccent,
        ),
        body: GoogleMap(
          onMapCreated: _onMapCreated,
          initialCameraPosition: CameraPosition(
            target: const LatLng(0, 0),
            zoom: 2,
          ),
          markers: _markers.values.toSet(),
        ),
      ),
    );
  }

We need to be able to get Santa's location. Each year on the 24th December the following API is accessible

https://santa-api.appspot.com/info?client=web&language=en&fingerprint=&routeOffset=0&streamOffset=0

So in the meantime we also need to handle the case that the API returns an error and display a place holder until the 24th of December.

Now put the updated code below into /lib/locations.dart. The changes we've done here now match the JSON output that will come from the Google Santa Tracker API, we convert the destinations from the JSON payload of the API to a list of lat & lngs, city name and region that we can later use to display inside the Flutter app.

import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:json_annotation/json_annotation.dart';

part 'locations.g.dart';

@JsonSerializable()
class LatLng {
  LatLng({
    this.lat,
    this.lng,
  });

  factory LatLng.fromJson(Map<String, dynamic> json) => _$LatLngFromJson(json);
  Map<String, dynamic> toJson() => _$LatLngToJson(this);

  final double lat;
  final double lng;
}

@JsonSerializable()
class Destination {
  Destination({
    this.location,
    this.id,
    this.city,
    this.region
  });

  factory Destination.fromJson(Map<String, dynamic> json) => _$DestinationFromJson(json);
  Map<String, dynamic> toJson() => _$DestinationToJson(this);

  final LatLng location;
  final String id;
  final String city;
  final String region;
}

@JsonSerializable()
class Locations {
  Locations({
    this.destinations,
  });

  factory Locations.fromJson(Map<String, dynamic> json) =>
      _$LocationsFromJson(json);
  Map<String, dynamic> toJson() => _$LocationsToJson(this);

  final List<Destination> destinations;
}

Future<Locations> getSantaDestinations() async {
  const googleLocationsURL = 'https://santa-api.appspot.com/info?client=web&language=en&fingerprint=&routeOffset=0&streamOffset=0';

  final response = await http.get(googleLocationsURL);
  if (response.statusCode == 200) {
    return Locations.fromJson(json.decode(response.body));
  } else {
    throw HttpException(
        'Unexpected status code ${response.statusCode}:'
        ' ${response.reasonPhrase}',
        uri: Uri.parse(googleLocationsURL));
  }
}

We also need to generate the new JSON (as per the Google tutorial)

flutter pub run build_runner build --delete-conflicting-outputs

Finally, we need to update _MyAppState in /lib/main.dart to reflect the changes in location. We have updated _onMapCreated  to place markers from the getSantaDestinations function. We also update the camera position inside GoogleMap to reflect the first location in the list from getSantaDestinations.

class _MyAppState extends State<MyApp> {
  LatLng cameraPosition = LatLng(0,0);

  final Map<String, Marker> _markers = {};
  Future<void> _onMapCreated(GoogleMapController controller) async {
    final santaLocations = await locations.getSantaDestinations();
    setState(() {
      _markers.clear();
      for (final dest in santaLocations.destinations) {
        final marker = Marker(
          markerId: MarkerId(dest.city),
          position: LatLng(dest.location.lat, dest.location.lng),
          infoWindow: InfoWindow(
            title: dest.city,
            snippet: dest.region,
          ),
        );
        _markers[dest.city] = marker;
      }
      cameraPosition = LatLng(santaLocations.destinations[0].location.lat,santaLocations.destinations[0].location.lng);
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Santa Tracker'),
          backgroundColor: Colors.redAccent,
        ),
        body: GoogleMap(
          onMapCreated: _onMapCreated,
          initialCameraPosition: CameraPosition(
            target: cameraPosition,
            zoom: 2,
          ),
          markers: _markers.values.toSet(),
        ),
      ),
    );
  }
}
Updated Locations of Santa (This is using a mock server since its not the 24th of December)

This is a quick overview of how to list all the destinations that Santa will be visiting on the 24th of December. Ideas on how to extend this could be:

  • Add custom icon to show where Santa is currently
  • List more information from the Google API inside the app
  • Add some nice animations as Santa goes between destinations

And for the finished repo

Lyle Dean

View Comments...