AboutBlogContact
Mobile DevelopmentApril 6, 2026 5 min read 549Updated: June 22, 2026

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

AunimedaAunimeda
📋 Table of Contents

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 →


Aunimeda develops mobile applications for iOS and Android - from MVP to production-ready apps with full backend integration.

Contact us to discuss your mobile project. See also: Mobile App Development, Mobile Game Development

Read Also

Mobile App Monetization Strategies in 2026: Which Model Fits Your Appaunimeda
Mobile Development

Mobile App Monetization Strategies in 2026: Which Model Fits Your App

A practical breakdown of mobile app monetization models in 2026: freemium, subscriptions, in-app purchases, ads, and paywalls. How to choose and implement the right model for your app type.

Flutter App Development in Bishkek: Cost, Timeline, and What to Expectaunimeda
Mobile Development

Flutter App Development in Bishkek: Cost, Timeline, and What to Expect

Everything you need to know about Flutter mobile app development in Bishkek, Kyrgyzstan in 2026: costs, timelines, team structure, and how to evaluate a development partner.

React Native Push Notifications in 2026: Complete Guide (Expo + Firebase)aunimeda
Mobile Development

React Native Push Notifications in 2026: Complete Guide (Expo + Firebase)

Push notifications are the single highest-ROI feature in mobile apps. Open rates are 7x higher than email. Here's how to implement them correctly in React Native - including background handling, deep linking, and analytics.

Need IT development for your business?

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

Mobile App Development

Get Consultation All articles