Adicionar mapas a um aplicativo Flutter é uma funcionalidade poderosa, mas muitos desenvolvedores pensam que a única opção é o Google Maps. Felizmente, existem alternativas robustas e de código aberto, como o pacote flutter_map
, que utiliza os dados do OpenStreetMap e oferece uma flexibilidade incrível.
Neste guia, vamos construir um aplicativo simples que exibe um mapa com pins (marcadores) customizados e interativos. É a base perfeita para qualquer projeto que precise de geolocalização, desde apps de entrega até guias turísticos.
Por que usar o flutter_map
?
- É Open Source: Totalmente gratuito e mantido pela comunidade.
- Altamente Customizável: Permite o uso de diversos provedores de mapas e a criação de marcadores com qualquer widget do Flutter.
- Excelente Suporte Web: Funciona perfeitamente em aplicações web, além de mobile e desktop.
Passo 1: Criando o Projeto e Adicionando as Dependências
Vamos começar criando um novo projeto Flutter e adicionando os pacotes necessários.
Crie o projeto:
flutter create flutter_map_example
Navegue para a pasta do projeto:
cd flutter_map_example
Adicione as dependências: Precisaremos de dois pacotes. O
flutter_map
para o mapa em si, e olatlong2
para facilitar o manuseio de coordenadas geográficas.flutter pub add flutter_map latlong2
Este comando adiciona as linhas necessárias ao seu arquivo
pubspec.yaml
e baixa os pacotes.
Passo 2: A Estrutura do Mapa
A estrutura do flutter_map
é baseada em camadas (Layers
). Pense nisso como folhas de papel transparente empilhadas: a primeira é o mapa base, a segunda contém os pins, a terceira pode ter polígonos, e assim por diante.
Vamos substituir o conteúdo do arquivo lib/main.dart
pelo nosso código. A estrutura básica será:
FlutterMap
: O widget principal que contém todo o mapa.MapOptions
: Onde definimos o centro inicial do mapa e o nível de zoom.children
: Uma lista de camadas que serão desenhadas no mapa.
Nossa primeira camada será a TileLayer
, responsável por buscar e exibir as “peças” (tiles) do mapa do OpenStreetMap.
Passo 3: Criando Pins Customizados e Interativos
A verdadeira magia do flutter_map
está na MarkerLayer
. Um Marker
(pin) pode ter como child
qualquer widget do Flutter. Isso nos dá liberdade total para criar marcadores que não são apenas um ícone, mas também contêm texto, botões e gestos.
No nosso exemplo, criaremos uma função auxiliar _buildCityMarker
que retorna um Marker
. Este marcador será composto por:
- Um
InkWell
para capturar o toque e fornecer feedback visual (como a mudança do cursor do mouse na web). - Um
Column
para empilhar o ícone e o texto. - Um
Icon
para o pino visual. - Um
Container
comText
para exibir o nome da cidade.
Passo 4: O Código Completo
Agora, vamos juntar tudo. Substitua todo o conteúdo do seu arquivo lib/main.dart
pelo código abaixo. Ele está completo, comentado e pronto para rodar.
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Mapa ES',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MapaCidadesEsPage(),
debugShowCheckedModeBanner: false,
);
}
}
class MapaCidadesEsPage extends StatefulWidget {
const MapaCidadesEsPage({super.key});
@override
State<MapaCidadesEsPage> createState() => _MapaCidadesEsPageState();
}
class _MapaCidadesEsPageState extends State<MapaCidadesEsPage> {
// Coordenadas geográficas das cidades
static const LatLng _pontoSaoMateus = LatLng(-18.7160, -39.8582);
static const LatLng _pontoVitoria = LatLng(-20.3194, -40.3378);
// Ponto central para inicializar o mapa entre as duas cidades
static const LatLng _pontoCentral = LatLng(-19.5177, -40.0980);
// Lista que vai conter nossos pins (marcadores)
late final List<Marker> _markers;
@override
void initState() {
super.initState();
// Inicializa a lista de marcadores quando o widget é criado
_markers = [
// Pin de São Mateus
_buildCityMarker(
point: _pontoSaoMateus,
cityName: 'São Mateus',
color: Colors.red,
),
// Pin de Vitória
_buildCityMarker(
point: _pontoVitoria,
cityName: 'Vitória',
color: Colors.blue,
),
];
}
/// Função auxiliar para criar um Marker (pin) customizado
Marker _buildCityMarker({
required LatLng point,
required String cityName,
required Color color,
}) {
return Marker(
point: point,
width: 95, // Largura fixa que acomoda bem o maior nome
height: 65, // Altura fixa para o pin
child: InkWell(
onTap: () {
// Ação ao clicar no pin: exibe uma notificação simples (SnackBar)
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Você clicou em $cityName!'),
backgroundColor: color,
duration: const Duration(seconds: 2),
),
);
},
// O conteúdo visual do pin é uma coluna com o ícone e o texto
child: Column(
children: [
Icon(
Icons.location_pin,
color: color,
size: 40,
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2),
decoration: BoxDecoration(
color: Colors.white.withAlpha(204),
borderRadius: BorderRadius.circular(10),
),
child: Text(
cityName,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Mapa - Espírito Santo'),
centerTitle: true,
),
// O widget principal que renderiza o mapa
body: FlutterMap(
// Opções do mapa, como centro e zoom inicial
options: const MapOptions(
initialCenter: _pontoCentral,
initialZoom: 8.0, // Zoom que mostra as duas cidades
),
// O mapa é construído em camadas (layers)
children: [
// Camada 1: O mapa base do OpenStreetMap
TileLayer(
urlTemplate: '[https://tile.openstreetmap.org/](https://tile.openstreetmap.org/){z}/{x}/{y}.png',
userAgentPackageName: 'com.example.flutter_map_example',
),
// Camada 2: Nossos pins (marcadores)
MarkerLayer(
markers: _markers,
),
],
),
);
}
}
Uma Nota Importe sobre o OpenStreetMap
Ao rodar o aplicativo, você notará uma mensagem informativa no seu console de depuração. Ela não é um erro, mas sim um aviso importante da comunidade flutter_map
: os servidores públicos do OpenStreetMap (OSM) não são feitos para uso comercial ou de alto tráfego.
O OSM é um projeto incrível mantido por voluntários, e seus servidores têm capacidade limitada. Para desenvolvimento, testes e projetos pessoais, seu uso é aceitável. No entanto, se você planeja lançar um aplicativo para um grande público, a prática recomendada é usar um provedor de “tiles” comercial, muitos dos quais oferecem generosos planos gratuitos. Mudar de provedor geralmente envolve apenas a troca da urlTemplate
no seu TileLayer
.
Conclusão
E aí está! Com poucas linhas de código, criamos um mapa funcional e interativo no Flutter. A partir daqui, as possibilidades são enormes: você pode carregar os pins de uma API, navegar para uma tela de detalhes ao tocar em um marcador ou até mesmo usar outros pacotes do ecossistema flutter_map
para adicionar pop-ups e clusters de marcadores.
O flutter_map
prova ser uma ferramenta poderosa e acessível para qualquer desenvolvedor que queira integrar mapas em seus projetos sem depender de serviços proprietários.