AboutBlogContact
DevelopmentApril 6, 2026 5 min read 25

2GIS Maps Flutter Integration Guide: Maps for CIS Apps (2026)

AunimedaAunimeda
📋 Table of Contents

2GIS Maps Flutter Integration Guide: Maps for CIS Apps (2026)

2GIS has the most accurate and detailed maps for cities in Russia, Kazakhstan, Kyrgyzstan, and CIS countries - better street-level data, interior maps of malls, more accurate building addresses, and offline map support. For apps targeting these markets, 2GIS is often a better choice than Google Maps.

This guide covers the complete Flutter integration using the official 2GIS Flutter SDK (dgis_maps_flutter).


Why 2GIS Over Google Maps for CIS

Feature 2GIS Google Maps
CIS street-level accuracy ✅ Best Good
Courtyard/internal road data ✅ Excellent Partial
New residential district coverage ✅ Fast updates Slower
Indoor maps (malls, airports) ✅ Available Limited
Offline maps ✅ Available Limited (paid)
Public transit CIS cities ✅ Excellent Good
Traffic data Russia/CIS ✅ Real-time Good
Price for CIS traffic Free tier generous Pay per request
Works without Google Services ✅ Yes ❌ No

The last point matters on some Android devices (Huawei, etc.) where Google Play Services are unavailable.


Setup

1. Get API Key

Register at partner.2gis.com to get your API key (free tier available).

2. Add Dependency

# pubspec.yaml
dependencies:
  dgis_maps_flutter: ^1.7.0

3. Android Configuration

<!-- android/app/src/main/AndroidManifest.xml -->
<manifest>
  <uses-permission android:name="android.permission.INTERNET"/>
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

  <application>
    <!-- 2GIS API Key -->
    <meta-data
      android:name="DgisMapApiKey"
      android:value="YOUR_API_KEY_HERE"/>
    <!-- ... -->
  </application>
</manifest>

Set minimum SDK version:

// android/app/build.gradle
defaultConfig {
    minSdkVersion 21
}

4. iOS Configuration

<!-- ios/Runner/Info.plist -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access to show you on the map</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs location access for navigation</string>

Basic Map Display

import 'package:dgis_maps_flutter/dgis_maps_flutter.dart';
import 'package:flutter/material.dart';

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

  @override
  State<MapScreen> createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  late DGisMapController _mapController;

  // Bishkek city center
  static const CameraPosition _initialPosition = CameraPosition(
    target: LatLng(42.8746, 74.5698),
    zoom: 13,
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: DGisMap(
        initialCameraPosition: _initialPosition,
        apiKey: 'YOUR_API_KEY',
        onMapCreated: (controller) {
          _mapController = controller;
        },
        myLocationEnabled: true,
        myLocationButtonEnabled: true,
        trafficEnabled: true, // Show traffic layer
      ),
    );
  }
}

Adding Markers

// Add a single marker
Future<void> addMarker(LatLng position, String title) async {
  await _mapController.addMarker(
    MarkerOptions(
      position: position,
      infoWindowText: InfoWindowText(title: title),
      icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed),
    ),
  );
}

// Add multiple markers (e.g., store locations)
Future<void> addStoreMarkers(List<Store> stores) async {
  final markers = stores.map((store) => Marker(
    markerId: MarkerId(store.id),
    position: LatLng(store.lat, store.lng),
    infoWindow: InfoWindow(
      title: store.name,
      snippet: store.address,
      onTap: () => _onStoreTap(store),
    ),
    icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue),
  )).toSet();

  await _mapController.addMarkers(markers);
}

// Custom marker icon
Future<BitmapDescriptor> _createCustomMarker(String text) async {
  // Create a custom marker from an asset
  return await BitmapDescriptor.fromAssetImage(
    const ImageConfiguration(size: Size(48, 48)),
    'assets/images/map_pin.png',
  );
}

User Location and Geofencing

import 'package:geolocator/geolocator.dart';

class LocationService {
  Future<Position> getCurrentLocation() async {
    bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) throw Exception('Location services disabled');

    LocationPermission permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        throw Exception('Location permission denied');
      }
    }

    return await Geolocator.getCurrentPosition(
      desiredAccuracy: LocationAccuracy.high,
    );
  }

  // Move camera to user location
  Future<void> goToMyLocation(DGisMapController controller) async {
    final position = await getCurrentLocation();
    await controller.animateCamera(
      CameraUpdate.newCameraPosition(
        CameraPosition(
          target: LatLng(position.latitude, position.longitude),
          zoom: 16,
        ),
      ),
    );
  }

  // Stream location updates (for tracking)
  Stream<Position> trackLocation() {
    return Geolocator.getPositionStream(
      locationSettings: const LocationSettings(
        accuracy: LocationAccuracy.high,
        distanceFilter: 10, // Update every 10 meters
      ),
    );
  }
}

Drawing Routes

// Draw polyline route between two points
Future<void> drawRoute(LatLng from, LatLng to) async {
  // 2GIS routing API
  final routePoints = await _getRoutePoints(from, to);

  await _mapController.addPolyline(
    PolylineOptions(
      points: routePoints,
      color: Colors.blue,
      width: 4,
    ),
  );
}

// Get route via 2GIS Directions API (REST)
Future<List<LatLng>> _getRoutePoints(LatLng from, LatLng to) async {
  final response = await http.get(Uri.parse(
    'https://routing.api.2gis.com/routing/7.0.0/global'
    '?key=YOUR_API_KEY'
    '&points=${from.longitude},${from.latitude}'
    '|${to.longitude},${to.latitude}'
    '&type=pedestrian', // or 'car', 'bicycle'
  ));

  final data = jsonDecode(response.body);
  final points = data['result'][0]['paths'][0]['points']['coordinates'] as List;

  return points.map((p) => LatLng(p[1] as double, p[0] as double)).toList();
}

Address Search (Geocoding)

// Search for addresses / places
Future<List<SearchResult>> searchAddress(String query, LatLng nearLocation) async {
  final response = await http.get(Uri.parse(
    'https://catalog.api.2gis.com/3.0/suggest'
    '?key=YOUR_API_KEY'
    '&q=${Uri.encodeComponent(query)}'
    '&location=${nearLocation.longitude},${nearLocation.latitude}'
    '&radius=10000'  // Search radius in meters
    '&locale=ru'     // ru, en, kk (Kazakh)
  ));

  final data = jsonDecode(response.body);
  return (data['result']['items'] as List)
      .map((item) => SearchResult(
        name: item['name'],
        fullName: item['full_name'],
        lat: item['point']?['lat'],
        lng: item['point']?['lon'],
      ))
      .where((r) => r.lat != null)
      .toList();
}

// Reverse geocoding: coordinates → address
Future<String> reverseGeocode(LatLng position) async {
  final response = await http.get(Uri.parse(
    'https://catalog.api.2gis.com/3.0/items/geocode'
    '?key=YOUR_API_KEY'
    '&lat=${position.latitude}'
    '&lon=${position.longitude}'
    '&fields=items.point,items.address'
  ));

  final data = jsonDecode(response.body);
  return data['result']['items'][0]['address_name'] ?? 'Unknown location';
}

Address Input Widget with Autocomplete

class AddressSearchField extends StatefulWidget {
  final Function(String address, LatLng position) onSelected;
  final LatLng centerLocation;

  @override
  Widget build(BuildContext context) {
    return TextField(
      decoration: const InputDecoration(
        hintText: 'Enter address',
        prefixIcon: Icon(Icons.search),
      ),
      onChanged: (query) async {
        if (query.length < 3) return;
        final results = await searchAddress(query, widget.centerLocation);
        setState(() => _suggestions = results);
      },
    );
    // Show suggestions in dropdown below
  }
}

Yandex MapKit Alternative

If you need Yandex-specific features (traffic in Russia), the Yandex MapKit Flutter SDK is also available:

dependencies:
  yandex_mapkit: ^3.3.0

Both 2GIS and Yandex MapKit cover CIS well. 2GIS has better interior maps and offline support. Yandex has better real-time traffic data for Russian cities and deeper integration with Yandex services.


API Rate Limits and Pricing (2026)

API Free Tier Paid
Map tiles 50,000 loads/month $0.80 per 1,000
Search / Geocoding 10,000 requests/month $0.50 per 1,000
Routing 10,000 requests/month $0.60 per 1,000
Traffic Included with map -

For most startup apps, the free tier is sufficient during development and early production.

Build your CIS app with Aunimeda →

Read Also

Telegram Bot vs WhatsApp Bot: Which to Build for CIS Markets (2026)aunimeda
Development

Telegram Bot vs WhatsApp Bot: Which to Build for CIS Markets (2026)

Detailed comparison of Telegram and WhatsApp bots for Russian, Kazakh, and Kyrgyz markets. Audience data, technical capabilities, costs, and when to build each.

Kaspi Pay API Integration Guide for Web and Mobile Apps (2026)aunimeda
Development

Kaspi Pay API Integration Guide for Web and Mobile Apps (2026)

The complete developer guide to integrating Kaspi Pay in your web or mobile application. Authentication, payment flow, webhooks, and handling edge cases for the Kazakhstan market.

Telegram Mini App with Payments: Complete Developer Tutorial (2026)aunimeda
Development

Telegram Mini App with Payments: Complete Developer Tutorial (2026)

Build a Telegram Mini App with in-app payments from scratch. Covers Telegram WebApp API, bot setup, React frontend, Node.js backend, and Telegram Payments integration.

Need IT development for your business?

We build websites, mobile apps and AI solutions. Free consultation.

Get Consultation All articles