Flutter Flow: Шаблон панели управления Place Manager
FlutterPulseЭта статья переведена специально для канала FlutterPulse. В этом канале вы найдёте много интересных вещей, связанных с Flutter. Не забывайте подписываться! 🚀

Этот шаблон панели управления Place Manager поможет вам управлять данными о местоположениях различными способами.
- Легко создавайте данные полигонов и полилиний, выбирая местоположения непосредственно на карте.
- Добавьте отдельные местоположения с пользовательскими маркерами
- Примеры страниц, которые загружают ваши полигоны, полилинии и другие данные о местоположениях в Google Maps, tile maps и MapBox.
- Пользовательские функции картографирования для преобразования данных Supabase в типы Flutter Flow, определенные виджетами карт.
- Скоро будет больше!
Изучите FlutterFlow Marketplace для отобранной коллекции виджетов, шаблонов и пользовательских плагинов, адаптированных для...
flutterflow.io
Далее мы обсудим, как вам настроиться. Как минимум, вам нужно будет настроить базу данных. Остальная часть этого документа даст вам более глубокое понимание того, как правильно изменить её в соответствии с вашими потребностями.
Экраны
Давайте рассмотрим некоторые из экранов, включенных в приложение
Назначения
Вы можете думать о назначениях как о центральной точке для группы данных о местоположениях. Возможно, это местоположение офиса или района, на котором вы хотите сосредоточиться.

Страницы создания и редактирования назначений похожи. Вы получите опции мест, полигонов и полилиний только на странице редактирования.
Выбор местоположения открывает Flutter Flow place picker для выбора вашего местоположения. Выберите значок назначения полезно, если вы хотите различать назначения по-другому, а также устанавливать это как маркер на карте.

Места
Когда вы нажимаете места на странице редактирования назначения, вы увидите список ваших местоположений.

Страница создания и редактирования местоположения — это самая простая страница редактирования, включенная в приложение. Вы можете выбрать местоположение с помощью place picker, добавить пользовательский маркер и установить URL веб-сайта.

Страница полигонов
Когда вы нажимаете полигоны на странице редактирования назначения, вы увидите список ваших полигонов.

Страницы редактирования и создания полигона позволяют задавать различные свойства высокого уровня для полигона.

Нажатие на кнопку Add Coordinates откроет список координат для вашего полигона.

Страницы редактирования и создания координат полигона позволяют задавать имя координаты и детали местоположения.

Вы можете нажать на кнопку Get Coordinates From Map чтобы получить координаты на карте Google. Просто перетащите карту, и центр маркера будет установлен как ваша координата.

Нажатие на кнопку save location обновит местоположение на странице редактирования. Нажмите Update Polygon Coordinates, чтобы сохранить это местоположение в вашей базе данных.
Страницы полилиний
Полилинии имеют функциональность, аналогичную страницам полигонов. Когда вы нажимаете на polylines на странице редактирования назначения, вы увидите список своих полилиний.

Страницы редактирования и создания полилиний позволяют задавать свойства для полилинии.

Нажатие на Add coordinates открывает список координат для этого полигона

Страницы редактирования и создания координат полилинии позволяют задавать координаты и имя для полилинии.

Пример карты
Это пример полигона с координатами из предыдущих скриншотов. Карты Google и Tile поддерживают полигоны и полилинии.
Карта Mapbox использует Mapbox SDK и в настоящее время не поддерживает полигоны и полилинии в этом шаблоне.
Вы можете использовать карту Map Box, если хотите использовать полигоны и полилинии на карте MapBox.

Настройка Mapbox SDK
Вы можете легко добавить Flutter Map Box SDK с пользовательскими маркерами. Начните с официальной документации, создайте аккаунт и создайте публичный токен доступа.
Настройка Google Maps в Flutter Flow
Чтобы начать, следуйте инструкциям Flutter Flow для Google Maps:
Генерация ключей карт | Документация FlutterFlowУзнайте, как генерировать и использовать ключи карт для интеграции Google Maps в вашем приложении FlutterFlow.
flutterflow.io
Добавление виджета Google Map из Flutter Flow на пустую страницу
Чтобы завершить настройку, вам нужно добавить виджет Google Map из Flutter Flow на пустую страницу, если он еще не используется где-либо. Это обходной путь, который настраивает ключи для вас при скачивании и/или публикации приложения.
Вы можете следовать официальным инструкциям Flutter Flow, чтобы понять, как это сделать:
Виджет Google Maps | Документация FlutterFlowУзнайте, как добавить и настроить виджет Google Maps в вашем приложении FlutterFlow.
flutterflow.io
Настройка базы данных
Вы можете настроить таблицу местоположений в Supabase и Firebase с любыми столбцами, которые вам нужны.
Следующее относится к Supabase, но вскоре я добавлю инструкции для Firebase. Вы можете настроить свои коллекции Firebase с аналогичной структурой.
Если у вас нет таблиц, вы можете начать с этих в Supabase.
Нажмите Enter или кликните, чтобы просмотреть изображение в полном размере

Нажмите Enter или кликните, чтобы просмотреть изображение в полном размере

Вы также можете запустить это в SQL Editor на Supabase.

Создайте таблицу place.
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;
Убедитесь, что вы включили RLS и настроили политики RLS перед выпуском в продакшн, чтобы защитить ваши данные. Эта политика позволяет любому пользователю читать из таблицы.
-- 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);
Для многоугольников у нас есть две таблицы. Для каждого многоугольника мы сохраняем данные в таблицу polygon. Она будет содержать важные общие данные о многоугольнике, такие как цвета и вес обводки.
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);
Аналогично, у polyline есть две таблицы по той же причине, что и у 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);
Добавление пользовательских карт на страницы Flutter Flow
Чтобы добавить пользовательские карты на страницу, выберите первую иконку в меню Build в левой части Flutter Flow, Widget Pallete. Затем выберите иконку с четырьмя квадратами. Перетащите нужный виджет на экран.
Нажмите Enter или кликните, чтобы просмотреть изображение в полном размере

Далее кликните на элемент меню Widget Tree (третий элемент в меню Build)
Нажмите Enter или кликните, чтобы просмотреть изображение в полном размере

Отсюда мы можем начать добавлять наше локальное состояние.
В правой части Flutter Flow нажмите на вкладку State Management.

Вы можете видеть, что у меня есть локальная переменная состояния страницы placeList — это список типов данных для типа данных place.
Если вы хотите использовать полигоны и полилинии, вам нужно создать аналогичные локальные переменные состояния страницы.
Локальная переменная состояния страницы для полигона:

Локальная переменная состояния страницы для полилинии:

Пользовательские функции
В шаблоне включены пользовательские функции картографирования. Иногда требуется настраивать преобразование в зависимости от различных потребностей. Мы обсудим некоторые из пользовательских функций, используемых в шаблоне.
Перед запросом к базе данных вам нужно сопоставить данные из вашего бэкенд-сервиса с типами данных, используемыми виджетами карты. Для этого вам нужно добавить пользовательские функции в ваш проект Flutter Flow. Они не являются обязательными, поэтому вы можете исключить то, что вам не нужно.
Ниже приведены функции для мест, полигонов и полилиний для Supabase. Функции сопоставления для Firebase аналогичны и будут добавлены в ближайшее время.
Если вы новичок в пользовательских функциях в Flutter Flow, ознакомьтесь с их документацией: https://docs.flutterflow.io/concepts/custom-code/custom-functions/
Функция сопоставления полилиний Supabase
Для начала рассмотрим полилинии. Вы можете выбрать любое имя, но убедитесь, что вы изменили его и в коде. Здесь я использую toPolylinePlaceModel

Далее нам нужно определить тип возвращаемого значения. Выберите Data Type и отметьте опции Is List и Nullable. Выберите тип данных polyline.
Нажмите Enter или кликните, чтобы просмотреть изображение в полном размере

Для аргументов мы передаем список строк из таблиц polyline_place и polyline. Нам нужно сделать это, чтобы сопоставить точки для каждой полилинии.
Нажмите Enter или кликните, чтобы просмотреть изображение в полном размере

Замените код по умолчанию на код ниже
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
Для полигонов я использую имя toPolygonPlaceModel.

Тип возвращаемого значения и аргументы похожи на полилинии, но используют тип данных polygon вместо этого.

В качестве аргументов мы передаем список строк из таблиц polygon_place и polygon. Нам нужно сделать это, чтобы мы могли сопоставить точки для каждого полигона.

Замените код по умолчанию на код ниже
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
Для сопоставления мест я использую toPlaceModel в качестве имени функции.
Нажмите Enter или щелкните, чтобы просмотреть изображение в полном размере

Для возвращаемого значения мы используем тип данных place. Убедитесь, что установлен флажок Is List

В качестве аргументов мы передаем список строк из нашей таблицы place.

Замените код по умолчанию на код ниже
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
}
Запрос данных из базы данных
Теперь мы можем выполнять запрос к базе данных, сопоставлять функции и устанавливать локальные переменные состояния страницы.
Сначала выберите элемент меню Widget tree. Затем нажмите на виджет верхнего уровня. В моём случае это HomePage.
Нажмите Enter или кликните, чтобы просмотреть изображение в полном размере

Теперь перейдите в правую часть экрана, чтобы добавить действия

Вот общий обзор перед началом работы. Это всего лишь один из способов запроса данных.
У меня есть одно параллельное действие с тремя разными "ветками" действий. Изначально у вас будет две ветки. Перед началом работы вы можете добавить третью ветку, нажав Add Action в правой части.
Нажмите Enter или кликните, чтобы просмотреть изображение в полном размере

В первой ветке я выполняю запрос к таблице place.

Обратите внимание, что я устанавливаю переменную вывода действия как supabasePlaces. Теперь нажмите кнопку плюс под этим действием, чтобы создать другое действие. Для действия 2 мы обновляем состояние страницы placeList.

При нажатии на Value to set, нажмите Custom Functions, и выберите функцию toPlaceModel.
Нажмите Enter или кликните, чтобы просмотреть изображение в полном размере

Для аргумента supabasePlaces нажмите Action Outputs и выберите supabasePlaces
Нажмите Enter или кликните, чтобы просмотреть изображение в полном размере

Теперь мы сделаем то же самое для полигонов и полилиний. Основное отличие в том, что сначала мы запрашиваем полигоны/полилинии, а затем запрашиваем таблицы мест для каждого.
Начнем с запроса полигонов. Обратите внимание, что у нас есть supabasePolygons как переменная вывода действия

Создайте новое действие под действием полигона для запроса polygonPlaces. Я назвал переменную вывода действия для этого supabasePolygonPlaces

Теперь создадим другое действие для установки состояния страницы polygonList. Аналогично местам мы также выберем Custom Functions и выберем toPolygonPlaceModel.
Аналогично тому, что мы сделали с местами, выберите переменные вывода действия и используйте их в качестве аргументов для этой функции
Нажмите Enter или кликните, чтобы просмотреть изображение в полном размере

Теперь сделаем то же самое для полилиний.
Создайте действие для запроса таблицы polyline с переменной вывода действия supabasePolylines

Создайте действие для запроса таблицы polyline_place с переменной вывода действия supabasePolylinePlace

Теперь обновите состояние страницы polylineList, выбрав пользовательское действие и передав переменные вывода действия в эту функцию

Передайте локальные переменные состояния страницы в виджеты карты
Теперь мы можем установить локальные переменные состояния страницы в соответствии с параметрами виджетов карты


С данными в вашей базе данных вы теперь можете запустить приложение и должны увидеть многоугольники, полилинии и т.д.
Нажмите Enter или кликните, чтобы просмотреть изображение в полном размере
