Skip to content

Save Favorites

In this guide, you will learn how to save/delete a favorite location and view the list of favorites.

save_favorites - example flutter screenshot

Setup

First, get an API key token, see the Getting Started guide.

Prerequisites

Make sure you completed the Environment Setup - Flutter Examples guide before starting this guide.

Build and Run

Go to the save_favorites directory within the Flutter examples directory - that is the name of this example project.

Note - the gem_kit directory containing the Maps SDK for Flutter should be in the plugins directory of the example, e.g. example_pathname/plugins/gem_kit - see the environment setup guide above.

Run: flutter pub get

Configure the native parts:

First, verify that the ANDROID_SDK_ROOT environment variable is set to the root path of your android SDK.

In android/build.gradle add the maven block as shown, within the allprojects block, for both debug and release builds:

allprojects {
    repositories {
        google()
        mavenCentral()
        maven {
           url "${rootDir}/../plugins/gem_kit/android/build"
        }
    }
}

in android/app/build.gradle within the android block, in the defaultConfig block, the android SDK version minSdk must be set as shown below.

Additionally, for release builds, in android/app/build.gradle, within the android block, add the buildTypes block as shown:

Replace example_pathname with the actual project pathname

android {
    defaultConfig {
        applicationId "com.magiclane.gem_kit.examples.example_pathname"
        minSdk 21
        targetSdk flutter.targetSdk
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }
    buildTypes {
        release {
            minifyEnabled false
            shrinkResources false

            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
    }
}

In the ios/Podfile configuration file, at the top, set the minimum ios platform version to 14 like this:

platform :ios, '14.0'

We recommend you to run these commands after you copy the gem_kit into your project: |flutter clean |flutter pub get |and |cd ios |pod install

Then run the project:

flutter run --debug
or
flutter run --release

Import Necessary Packages

First, import the required packages in your Dart code.

import 'package:gem_kit/core.dart';
import 'package:gem_kit/landmark_store.dart';
import 'package:gem_kit/map.dart';

import 'favorites_page.dart';
import 'landmark_panel.dart';

import 'package:flutter/material.dart';

App entry and initialization

const projectApiToken = String.fromEnvironment('GEM_TOKEN');

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

This code initializes the projectApiToken with the required authorization token and launches the app.

How It Works

This example demonstrates the following key features:

  • Allows users to save and remove landmarks from their list of favorites.

  • Interacts with a map where users can tap on landmarks, check if they are favorites, and toggle them.

save_favorites - example flutter screenshot

Build the Main Application

Define the main application widget, MyApp.

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Save Favorites',
      home: MyHomePage(),
    );
  }
}

Handle Map and Favorites in the Stateful Widget

Create the stateful widget, MyHomePage, which will handle the map and favorite locations functionality.

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

Define State Variables and Methods

Within _MyHomePageState, define the necessary state variables and methods to manage favorites.

class _MyHomePageState extends State<MyHomePage> {
  late GemMapController _mapController;
  Landmark? _focusedLandmark;

  // LandmarkStore object to save landmarks.
  late LandmarkStore? _favoritesStore;

  bool _isLandmarkFavorite = false;

  final favoritesStoreName = 'Favorites';

  @override
  void dispose() {
    GemKit.release();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.deepPurple[900],
        title: const Text('Favourites', style: TextStyle(color: Colors.white)),
        actions: [
          IconButton(
              onPressed: () => _onFavouritesButtonPressed(context),
              icon: const Icon(
                Icons.favorite,
                color: Colors.white,
              ))
        ],
      ),
      body: Stack(children: [
        GemMap(onMapCreated: _onMapCreated, appAuthorization: projectApiToken),
        if (_focusedLandmark != null)
          Positioned(
            bottom: 10,
            child: LandmarkPanel(
              onCancelTap: _onCancelLandmarkPanelTap,
              onFavoritesTap: _onFavoritesLandmarkPanelTap,
              isFavoriteLandmark: _isLandmarkFavorite,
              landmark: _focusedLandmark!,
            ),
          )
      ]),
      resizeToAvoidBottomInset: false,
    );
  }

  // The callback for when the map is ready to use.
  void _onMapCreated(GemMapController controller) {
    _mapController = controller;

    _favoritesStore = LandmarkStoreService.getLandmarkStoreByName(favoritesStoreName);
    _favoritesStore ??= LandmarkStoreService.createLandmarkStore(favoritesStoreName);

    _registerLandmarkTapCallback();
  }

Define Landmark Selection and Management

save_favorites - example flutter screenshot

save_favorites - example flutter screenshot

Implement methods to manage landmark selection and favorites.

void _registerLandmarkTapCallback() {
  _mapController.registerTouchCallback((pos) async {
    _mapController.setCursorScreenPosition(pos);
    final landmarks = _mapController.cursorSelectionLandmarks();

    if (landmarks.isNotEmpty) {
      _highlightLandmarks(landmarks);
      return;
    }

    final coordinates = _mapController.transformScreenToWgs(XyType(x: pos.x as int, y: pos.y as int));
    if (coordinates == null) return;

    final lmk = Landmark.withCoordinates(coordinates);
    lmk.name = '${coordinates.latitude} ${coordinates.longitude}';
    lmk.setImageFromIcon(GemIcon.searchResultsPin);
    _highlightLandmarks([lmk]);
  });
}

void _highlightLandmarks(List<Landmark> landmarks) {
  _mapController.activateHighlight(landmarks);
  final lmk = landmarks[0];

  setState(() {
    _focusedLandmark = lmk;
  });

  _mapController.centerOnCoordinates(lmk.coordinates);
  _checkIfFavourite();
}

void _checkIfFavourite() {
  final focusedLandmarkCoords = _focusedLandmark!.coordinates;
  final favourites = _favoritesStore!.getLandmarks();

  for (final lmk in favourites) {
    if (focusedLandmarkCoords.latitude == lmk.coordinates.latitude &&
        focusedLandmarkCoords.longitude == lmk.coordinates.longitude) {
      setState(() {
        _isLandmarkFavorite = true;
      });
      return;
    }
  }

  setState(() {
    _isLandmarkFavorite = false;
  });
}

void _onFavoritesLandmarkPanelTap() {
  _checkIfFavourite();

  if (_isLandmarkFavorite) {
    _favoritesStore!.removeLandmark(_focusedLandmark!);
  } else {
    _favoritesStore!.addLandmark(_focusedLandmark!);
  }
  setState(() {
    _isLandmarkFavorite = !_isLandmarkFavorite;
  });
}

void _onCancelLandmarkPanelTap() {
  _mapController.deactivateAllHighlights();
  setState(() {
    _focusedLandmark = null;
    _isLandmarkFavorite = false;
  });
}

Method to Navigate to Favorites

Implement a method to navigate to the favorites list and display it.

void _onFavouritesButtonPressed(BuildContext context) async {
  final favoritesList = _favoritesStore!.getLandmarks();
  final result = await Navigator.of(context).push(MaterialPageRoute<dynamic>(
    builder: (context) => FavoritesPage(landmarkList: favoritesList),
  ));

  if (result is Landmark) {
    _mapController.activateHighlight([result], renderSettings: HighlightRenderSettings());
    _mapController.centerOnCoordinates(result.coordinates);
    setState(() {
      _focusedLandmark = result;
    });
    _checkIfFavourite();
  }
}

Define Favorites Page and Item Classes

Implement the favorites page and individual favorites item widgets.

class FavoritesPage extends StatefulWidget {
  final List<Landmark> landmarkList;
  const FavoritesPage({super.key, required this.landmarkList});

  @override
  State<FavoritesPage> createState() => _FavoritesPageState();
}

class _FavoritesPageState extends State<FavoritesPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        foregroundColor: Colors.white,
        automaticallyImplyLeading: true,
        title: const Text("Favorites list"),
        backgroundColor: Colors.deepPurple[900],
      ),
      body: ListView.separated(
        padding: EdgeInsets.zero,
        itemCount: widget.landmarkList.length,
        separatorBuilder: (context, index) => const Divider(
          indent: 50,
          height: 0,
        ),
        itemBuilder: (context, index) {
          final lmk = widget.landmarkList.elementAt(index);
          return FavoritesItem(landmark: lmk);
        },
      ),
    );
  }
}

class FavoritesItem extends StatefulWidget {
  final Landmark landmark;

  const FavoritesItem({super.key, required this.landmark});

  @override
  State<FavoritesItem> createState() => _FavoritesItemState();
}

class _FavoritesItemState extends State<FavoritesItem> {
  @override
  Widget build(BuildContext context) {
    return ListTile(
      onTap: () => Navigator.of(context).pop(widget.landmark),
      leading: Container(
        padding: const EdgeInsets.all(8),
        width: 50,
        child: Image.memory(
          widget.landmark.getImage(),
        ),
      ),
      title: Text(
        widget.landmark.name,
        overflow: TextOverflow.fade,
        style: const TextStyle(
            color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400),
        maxLines: 2,
      ),
      subtitle: Text(
        '${widget.landmark.coordinates.latitude}, ${widget.landmark.coordinates.longitude}',
        overflow: TextOverflow.fade,
        style: const TextStyle(
            color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400),
        maxLines: 2,
      ),
    );
  }
}

Flutter Examples

Maps SDK for Flutter Examples can be downloaded or cloned with Git