Flutter Flow: Place Manager Dashboard Template

flutter

This Place Manager Dashboard template will help you manage your location data in various ways.

  • Easily create polygon and polyline data by choosing locations directly on a map.

  • Add single locations with custom markers

  • Example pages that load your polygons, polylines, and other location data in Google maps, tile maps, and MapBox.

  • Custom mapping functions to transform Supabase data into the Flutter Flow Types defined by the map widgets.

  • More coming soon!

Next we will discuss how to get you set up. At minimum you will need to set up the database. The rest of this document will give you a deeper understanding on how to properly change it to fit your needs.

Screens

Let’s go over some of the screens included in the app

Destinations

You can think of destinations as a center point for a group of location data. Maybe it’s an office location or neighborhood you want to focus on.

The create and edit destination pages are similar. You will only get the places, polygons, and polyline option in the edit page.

Select location opens the Flutter Flow place picker to choose your location. Choose Destination Icon is helpful in case you want to distinguish destinations in a different way as well as setting that as a marker in a map.

Places

When you click places on the destination edit page, you will see your list of locations.

The create and edit location page is the most simple edit page included in the app. You can choose your location with the place picker, add a custom marker, and set a website url.

Polygons page

When you click polygons on the destination edit page, you will see your list of polygons.

The edit and create pages for polygon enable you set different high level properties of the polygon.

Clicking the Add Coordinates button will bring you to the list of coordinates for your polygon.

The edit and create pages for polygon coordinate is where you set the coordinate name and location details.

You can click the Get Coordinates From Map button to get coordinates on a Google Map. Simply drag the map and the center of the marker is what will be set as your coordinate.

Clicking save location will update the location on the edit page. Click Update Polygon Coordinates to save this location to your database.

Polylines pages

The polylines have a similar functionality as the polygon pages. When you click polylines on the destination edit page, you will see your list of polylines.

The edit and create pages for polylines let you set the properties for the polyline.

Clicking Add coordinates takes you to the list of coordinates for that polygon

The edit and create pages for polyline coordinate allow you to set coordinates and a name for the polyline.

Map Example

This is an example a polygon with the coordinates from the previous screenshots. The Google and Tile maps support polygons and polylines.

The Mapbox Map uses the Mapbox SDK and does not currently support polygons and polylines at this time in the template.

You can use a Map Box tile map if you wish to use polygons and polylines on a MapBox map.

Mapbox SDK setup

You can easily add the Flutter Map Box SDK with custom markers. Start with the official documentation, create an account, and create a public access token.

https://docs.mapbox.com/flutter/maps/guides/install/?source=post_page-----7d1d256cc1dd---------------------------------------

Setting Up Google Maps on Flutter Flow

To get started follow the Flutter Flow Google Maps instructions : Generate Maps Keys | FlutterFlow Documentation

Add the Flutter Flow Google Map widget to a blank page

To complete the set up you will need to include a Flutter Flow Google Map widget on a blank page if it is not already used somewhere. This is a work around that sets up the the keys for you when you download and/or publish the app.

You can follow Flutter Flow official instructions to understand how to do this: Google Maps Widget | FlutterFlow Documentation

Setting up the database

You can set your location table on Supabase and Firebase to have any column you desire.

The following is for Supabase but I will include instructions for Firebase soon. You can set up your Firebase Collections with a similar structure.

If you don’t have tables you can start with these on Supabase.

Press enter or click to view image in full size

Press enter or click to view image in full size

You can also run this in the SQL Editor on Supabase.

Create the place table.

CREATE TABLE public.place (
  id bigint generated by default as identity not null,
  created_at timestamp with time zone not null default now(),
  title text null,
  description text null,
  latitude double precision null,
  longitude double precision null,
  image_url text null,
  constraint place_pkey primary key (id)
) TABLESPACE pg_default;

Make sure to turn on RLS and set up your RLS policies before releasing to production to keep your data secure. This policy allows us any user to read from the table.

-- Enable RLS on the place table
ALTER TABLE place ENABLE ROW LEVEL SECURITY;

-- Allow authenticated users to read all templates
CREATE POLICY "Allow users to read places"
ON place FOR SELECT
USING (true);

Now for polygons we have two tables. For each polygon we save to the polygon table. This will hold important overall data about the polygon such as colors and stroke weights.

create table public.polygon (
  id serial not null,
  title text null,
  description text null,
  fill_color text null,
  fill_opacity numeric null,
  stroke_color text null,
  stroke_opacity numeric null,
  stroke_weight numeric null,
  created_at timestamp with time zone not null default now(),
  zoom numeric null default 10,
  constraint polygon_pkey primary key (id),
) TABLESPACE pg_default;

create table public.polygon_place (
  id serial not null,
  polygon_id integer null,
  name text null,
  title text null,
  description text null,
  address text null,
  city text null,
  state text null,
  zip_code text null,
  latitude numeric null,
  longitude numeric null,
  image_url text null,
  "order" integer null,
  created_at timestamp with time zone not null default now(),
  constraint polygon_place_pkey primary key (id),
  constraint polygon_place_polygon_id_fkey foreign KEY (polygon_id) references polygon(id) on delete CASCADE
) TABLESPACE pg_default;

-- Enable RLS on the tables
ALTER TABLE polygon ENABLE ROW LEVEL SECURITY;
ALTER TABLE polygon_place ENABLE ROW LEVEL SECURITY;

-- Allow authenticated users to read all templates
CREATE POLICY "Allow users to read polygons"
ON polygon FOR SELECT
USING (true);

CREATE POLICY "Allow users to read polygon places"
ON polygon_place FOR SELECT
USING (true);

Similarly polylines have two tables for the same reason as polygon

create table public.polyline (
  id serial not null,
  title text null,
  description text null,
  stroke_color text null,
  stroke_opacity numeric null,
  stroke_weight numeric null,
  geodesic boolean null,
  created_at timestamp with time zone not null default now(),
  zoom numeric null default 10,
  constraint polyline_pkey primary key (id),
) TABLESPACE pg_default;

create table public.polyline_place (
  id serial not null,
  polyline_id integer null,
  title text null,
  description text null,
  address text null,
  city text null,
  state text null,
  zip_code text null,
  latitude numeric null,
  longitude numeric null,
  image_url text null,
  "order" integer null,
  created_at timestamp with time zone not null default now(),
  constraint polyline_place_pkey primary key (id),
  constraint polyline_place_polyline_id_fkey foreign KEY (polyline_id) references polyline (id) on delete CASCADE
) TABLESPACE pg_default;


-- Enable RLS on the tables
ALTER TABLE polyline ENABLE ROW LEVEL SECURITY;
ALTER TABLE polyline_place ENABLE ROW LEVEL SECURITY;

-- Allow authenticated users to read all templates
CREATE POLICY "Allow users to read polylines"
ON polyline FOR SELECT
USING (true);

CREATE POLICY "Allow users to read polyline places"
ON polyline_place FOR SELECT
USING (true);

Adding the custom maps to your Flutter Flow pages

To add the custom maps to your page choose the first icon under the Build menu to the far left of Flutter Flow, the Widget Pallete. Then choose the icon with four squares. Drag the widget you want on the screen.

Press enter or click to view image in full size

Next click on the Widget Tree menu item (3rd item under the Build menu)

Press enter or click to view image in full size

From here we can start adding our local state.

On the far right of Flutter Flow, click the State Management tab.

You can see that I have a placeList local page state variable. It’s a list of data types for the libraries place data type.

If you want to use polygons and polylines you will need to create similar local page state variables.

Polygon local page state variable:

Polyline local page state variable:

Custom Functions

Custom mapping functions are included in the template. There are sometimes a need to customize the transformation depending on different needs. We will discuss some of the custom functions used in the template.

Before querying from the database, you will need to map your data from your backend service to the data types used by the map widgets. To do this you will need to add custom functions to your Flutter Flow project. These are not a requirement so fill free to exclude what you do not need.

Below are functions for places, polygons, and polylines for Supabase. Mapping functions for Firebase are similar and will be added soon.

If you are new to custom functions in Flutter Flow, check out their documentation: https://docs.flutterflow.io/concepts/custom-code/custom-functions/

Supabase Polylines mapping function

First let’s start with Polylines. You can choose any name but make sure to change in the code as well. Here I use toPolylinePlaceModel

Next we need to define the return type. Choose Data Type and check the Is List and Nullable options. Choose the **polyline **data type.

Press enter or click to view image in full size

For the arguments we are passing a list of rows from tables polyline_place and polyline. We need to do this so we can map the points for each polyline.

Press enter or click to view image in full size

Replace the default code with the code below

import 'dart:convert';
import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:timeago/timeago.dart' as timeago;
import '/flutter_flow/custom_functions.dart';
import 'package:ff_commons/flutter_flow/lat_lng.dart';
import 'package:ff_commons/flutter_flow/place.dart';
import 'package:ff_commons/flutter_flow/uploaded_file.dart';
import '/backend/schema/structs/index.dart';
import '/backend/supabase/supabase.dart';
import "package:supabase_google_map_library_s065pd/backend/schema/structs/index.dart"
    as supabase_google_map_library_s065pd_data_schema;

List<supabase_google_map_library_s065pd_data_schema.PolylineStruct>?
    toPolylinePlaceModel(
  List<PolylinePlaceRow>? supabasePolylinePlaces,
  List<PolylineRow>? supabasePolylines,
) {
  /// MODIFY CODE ONLY BELOW THIS LINE

  // Map Supabase polyline list to library polyline list

  return supabasePolylines?.map((polyline) {
        // Get places for this polyline
        final polylinePlaces = supabasePolylinePlaces
                ?.where((place) => place.polylineId == polyline.id)
                .map((place) {
              return supabase_google_map_library_s065pd_data_schema
                  .createPolylinePlaceStruct(
                id: place.id,
                title: place.title,
                coordinates:
                    LatLng(place.latitude ?? 0.0, place.longitude ?? 0.0),
                polylineId: place.polylineId,
                order: place.order,
              );
            }).toList() ??
            [];

        return supabase_google_map_library_s065pd_data_schema.PolylineStruct(
          id: polyline.id,
          title: polyline.title,
          zoom: polyline.zoom ?? 11,
          strokeWeight: polyline.strokeWeight ?? 1,
          strokeColor: polyline.strokeColor,
          strokeOpacity: polyline.strokeOpacity,
          geodesic: polyline.geodesic,
          polylinePlaces: polylinePlaces,
        );
      }).toList() ??
      [];

  /// MODIFY CODE ONLY ABOVE THIS LINE
}

Supabase Polygon mapping function

For polygons I use the name toPolygonPlaceModel.

The return type and arguments are similar to polylines but use the **polygon **data type instead.

For the arguments we are passing a list of rows from tables polygon_place and polygon. We need to do this so we can map the points for each polyline.

Replace the default code with the code below

import 'dart:convert';
import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:timeago/timeago.dart' as timeago;
import '/flutter_flow/custom_functions.dart';
import 'package:ff_commons/flutter_flow/lat_lng.dart';
import 'package:ff_commons/flutter_flow/place.dart';
import 'package:ff_commons/flutter_flow/uploaded_file.dart';
import '/backend/schema/structs/index.dart';
import '/backend/supabase/supabase.dart';
import "package:supabase_google_map_library_s065pd/backend/schema/structs/index.dart"
    as supabase_google_map_library_s065pd_data_schema;

List<supabase_google_map_library_s065pd_data_schema.PolygonStruct>?
    toPolygonPlaceModel(
  List<PolygonPlaceRow>? supabasePolygonPlaces,
  List<PolygonRow>? supabasePolygons,
) {
  /// MODIFY CODE ONLY BELOW THIS LINE

  // Map Supabase polygon list to library polygon list

  return supabasePolygons?.map((polygon) {
        // Get places for this polygon
        final polygonPlaces = supabasePolygonPlaces
                ?.where((place) => place.polygonId == polygon.id)
                .map((place) {
              return supabase_google_map_library_s065pd_data_schema
                  .createPolygonPlaceStruct(
                id: place.id,
                title: place.title,
                coordinates:
                    LatLng(place.latitude ?? 0.0, place.longitude ?? 0.0),
                polygonId: place.polygonId,
                order: place.order,
              );
            }).toList() ??
            [];

        return supabase_google_map_library_s065pd_data_schema.PolygonStruct(
          id: polygon.id,
          title: polygon.title,
          zoom: polygon.zoom ?? 11.0, // Default zoom
          strokeColor: polygon.strokeColor,
          strokeOpacity: polygon.strokeOpacity,
          fillOpacity: polygon.fillOpacity,
          fillColor: polygon.fillColor,
          strokeWeight: polygon.strokeWeight ?? 2,
          polygonPlaces: polygonPlaces,
        );
      }).toList() ??
      [];

  /// MODIFY CODE ONLY ABOVE THIS LINE
}

Supabase Places mapping function

For mapping places I use toPlaceModel as the function name.

Press enter or click to view image in full size

For the return value we are using the place data type. Make sure to check Is List

For the arguments we are passing a list of rows from our place table.

Replace the default code with the code below

import 'dart:convert';
import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:timeago/timeago.dart' as timeago;
import '/flutter_flow/custom_functions.dart';
import 'package:ff_commons/flutter_flow/lat_lng.dart';
import 'package:ff_commons/flutter_flow/place.dart';
import 'package:ff_commons/flutter_flow/uploaded_file.dart';
import '/backend/schema/structs/index.dart';
import '/backend/supabase/supabase.dart';
import "package:supabase_google_map_library_s065pd/backend/schema/structs/index.dart"
    as supabase_google_map_library_s065pd_data_schema;

List<supabase_google_map_library_s065pd_data_schema.PlaceStruct> toPlaceModel(
    List<PlaceRow>? supabasePlaces) {
  /// MODIFY CODE ONLY BELOW THIS LINE

  // map supabase place list to library place list

  return supabasePlaces?.map((place) {
        return supabase_google_map_library_s065pd_data_schema.createPlaceStruct(
          title: place.title,
          description: place.description,
          latLng: LatLng(place.latitude ?? 0.0, place.longitude ?? 0.0),
          imageUrl: place.imageUrl,
          id: place.id,
        );
      }).toList() ??
      [];

  /// MODIFY CODE ONLY ABOVE THIS LINE
}

Query data from the database

Now we can query from the database, map the functions, and set the local page state variables.

First, select the Widget tree menu item. Next, click the top level widget. In my case the top level widget is HomePage.

Press enter or click to view image in full size

Now go to the far right of the screen so that we can add actions

Here is a high level overview before we get started. This is just one way you can query your data.

I have one parallel action with three different action “branches”. You initially will start with two branches. Before getting started you can add the third branch by clicking Add Action on the right side.

Press enter or click to view image in full size

On the first branch I’m querying the place table.

Note that I am setting the action output variable as supabasePlaces. Now click the plus button under this action to create another action. For action 2 we are updating our placeList page state.

When clicking Value to set, click Custom Functions, and choose the toPlaceModel custom function.

Press enter or click to view image in full size

For the supabasePlaces argument click Action Outputs and choose supabasePlaces

Press enter or click to view image in full size

Now we will do the same for polygon and polylines. The main difference is that we are first querying polygon/polylines and then we query the place tables for each.

Let’s start by querying polygons. Notice we have supabasePolygons as the action output variable

Create a new action under the polygon action to query polygonPlaces. I have name the action output variable for this supabasePolygonPlaces

Now let’s create another action to set the polygonList page state. Similar to places we will also choose **Custom Functions **and choose toPolygonPlaceModel.

Similar to what we did with places, choose Action Output Variables and use those as arguments for this function

Press enter or click to view image in full size

Now let’s do the same for polylines.

Create an action to query the polyline table with action output variable supabasePolylines

Create an action to query the polyline_place table with action output variable supabasePolylinePlace

Now update the polylineList page state by choosing the custom action and passing the action output variables to that function

Pass the local page state variables to the map widgets

We can now set the local page state variables to the parameters of the map widgets

With data in your database you can now run the app and should see polygons, polylines, etc.

Press enter or click to view image in full size