mapbox_search 4.4.0 copy "mapbox_search: ^4.4.0" to clipboard
mapbox_search: ^4.4.0 copied to clipboard

A Flutter package for place search using MapBox Api and for Static map image

example/lib/main.dart

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Mapbox Search v6 Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const HomePage(),
    );
  }
}

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _currentIndex = 0;
  final TextEditingController _apiKeyController = TextEditingController();

  final List<Widget> _pages = [
    const GeocodingPage(),
    const BatchGeocodingPage(),
    const StaticImagePage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Mapbox Search v6'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        bottom: PreferredSize(
          preferredSize: const Size.fromHeight(60),
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: TextField(
              controller: _apiKeyController,
              decoration: const InputDecoration(
                labelText: 'Mapbox API Key (Required for all features)',
                border: OutlineInputBorder(),
                filled: true,
                fillColor: Colors.white,
              ),
              onChanged: (value) {
                // Key is shared via MapBoxSearch.init or passed directly.
                // For this example, we'll let child widgets access this controller or init globally.
                MapBoxSearch.init(value);
              },
            ),
          ),
        ),
      ),
      body: _apiKeyController.text.isEmpty && MapBoxSearch.apiKey == null
          ? const Center(child: Text("Please enter your API Key to proceed"))
          : IndexedStack(index: _currentIndex, children: _pages),
      bottomNavigationBar: NavigationBar(
        selectedIndex: _currentIndex,
        onDestinationSelected: (index) => setState(() => _currentIndex = index),
        destinations: const [
          NavigationDestination(icon: Icon(Icons.place), label: 'Geocoding'),
          NavigationDestination(icon: Icon(Icons.layers), label: 'Batch'),
          NavigationDestination(icon: Icon(Icons.image), label: 'Static Image'),
        ],
      ),
    );
  }
}

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

  @override
  State<GeocodingPage> createState() => _GeocodingPageState();
}

class _GeocodingPageState extends State<GeocodingPage> {
  final TextEditingController _queryController = TextEditingController();
  String _result = "Results will appear here";
  bool _loading = false;

  GeoCodingApiV6 get _api => GeoCodingApiV6();

  Future<void> _forwardGeocode() async {
    if (_queryController.text.isEmpty) return;
    setState(() => _loading = true);
    try {
      final response = await _api.forward(_queryController.text);
      response.fold((success) {
        final buffer = StringBuffer();
        buffer.writeln("Found ${success.features.length} results:");
        for (var f in success.features) {
          buffer.writeln(
            "- ${f.properties.name} (${f.properties.fullAddress})",
          );
        }
        setState(() => _result = buffer.toString());
      }, (failure) => setState(() => _result = "Error: ${failure.message}"));
    } catch (e) {
      setState(() => _result = "Exception: $e");
    } finally {
      setState(() => _loading = false);
    }
  }

  Future<void> _reverseGeocode() async {
    setState(() => _loading = true);
    try {
      final response = await _api.reverse((lat: 38.8977, long: -77.0365));
      response.fold((success) {
        final buffer = StringBuffer();
        buffer.writeln("Reverse (White House):");
        if (success.features.isNotEmpty) {
          buffer.writeln(
            "- ${success.features.first.properties.placeFormatted}",
          );
        }
        setState(() => _result = buffer.toString());
      }, (failure) => setState(() => _result = "Error: ${failure.message}"));
    } catch (e) {
      setState(() => _result = "Exception: $e");
    } finally {
      setState(() => _loading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        children: [
          TextField(
            controller: _queryController,
            decoration: const InputDecoration(
              labelText: 'Search Query',
              border: OutlineInputBorder(),
            ),
          ),
          const SizedBox(height: 16),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              ElevatedButton(
                onPressed: _loading ? null : _forwardGeocode,
                child: const Text('Forward'),
              ),
              ElevatedButton(
                onPressed: _loading ? null : _reverseGeocode,
                child: const Text('Reverse (WH)'),
              ),
            ],
          ),
          const SizedBox(height: 16),
          Expanded(
            child: Container(
              width: double.infinity,
              padding: const EdgeInsets.all(8),
              color: Colors.grey[200],
              child: SingleChildScrollView(child: Text(_result)),
            ),
          ),
        ],
      ),
    );
  }
}

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

  @override
  State<BatchGeocodingPage> createState() => _BatchGeocodingPageState();
}

class _BatchGeocodingPageState extends State<BatchGeocodingPage> {
  String _result = "Batch results will appear here";
  bool _loading = false;

  GeoCodingApiV6 get _api => GeoCodingApiV6();

  Future<void> _runBatch() async {
    setState(() => _loading = true);
    try {
      final queries = [
        ForwardQuery(query: "Eiffel Tower", limit: 1),
        ReverseQuery(
          location: (lat: 40.6892, long: -74.0445),
          limit: 1,
        ), // Statue of Liberty
      ];

      final response = await _api.batch(queries);
      response.fold((success) {
        final buffer = StringBuffer();
        buffer.writeln("Batch Request 1 (Eiffel Tower):");
        if (success.batch[0].features.isNotEmpty) {
          buffer.writeln(
            "- ${success.batch[0].features.first.properties.placeFormatted}",
          );
        }
        buffer.writeln("\nBatch Request 2 (Statue of Liberty Coords):");
        if (success.batch[1].features.isNotEmpty) {
          buffer.writeln(
            "- ${success.batch[1].features.first.properties.placeFormatted}",
          );
        }
        setState(() => _result = buffer.toString());
      }, (failure) => setState(() => _result = "Error: ${failure.message}"));
    } catch (e) {
      setState(() => _result = "Exception: $e");
    } finally {
      setState(() => _loading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: _loading ? null : _runBatch,
            child: const Text('Run Batch (Eiffel Tower + Statue of Liberty)'),
          ),
          const SizedBox(height: 16),
          Container(
            margin: const EdgeInsets.all(16),
            padding: const EdgeInsets.all(16),
            width: double.infinity,
            height: 300,
            color: Colors.grey[200],
            child: SingleChildScrollView(child: Text(_result)),
          ),
        ],
      ),
    );
  }
}

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

  @override
  State<StaticImagePage> createState() => _StaticImagePageState();
}

enum StaticExampleType { marker, polyline, simple, auto }

class _StaticImagePageState extends State<StaticImagePage> {
  String? _imageUrl;
  MapBoxStyle _selectedStyle = MapBoxStyle.Streets;
  StaticExampleType _selectedType = StaticExampleType.marker;

  StaticImage get _staticImage => StaticImage();

  void _generateImage() {
    try {
      Uri uri;
      switch (_selectedType) {
        case StaticExampleType.marker:
          uri = _staticImage.getStaticUrlWithMarker(
            center: (lat: 37.7749, long: -122.4194), // San Francisco
            marker: MapBoxMarker(
              markerColor: const RgbColor(255, 0, 0),
              markerLetter: 's',
              markerSize: MarkerSize.LARGE,
            ),
            zoomLevel: 13,
            width: 600,
            height: 400,
            style: _selectedStyle,
            render2x: true,
          );
          break;
        case StaticExampleType.polyline:
          uri = _staticImage.getStaticUrlWithPolyline(
            point1: (lat: 37.77343, long: -122.46589),
            point2: (lat: 37.75965, long: -122.42816),
            marker1: MapBoxMarker(
              markerColor: Color.rgb(0, 0, 0) as RgbColor,
              markerLetter: MakiIcons.aerialway.value,
              markerSize: MarkerSize.LARGE,
            ),
            marker2: MapBoxMarker(
              markerColor: Color.rgb(244, 67, 54) as RgbColor,
              markerLetter: 'q',
              markerSize: MarkerSize.SMALL,
            ),
            height: 300,
            width: 600,
            zoomLevel: 15.5,
            style: MapBoxStyle.Dark,
            render2x: true,
            path: MapBoxPath(
              pathColor: RgbColor(
                Colors.redAccent.red,
                Colors.redAccent.green,
                Colors.redAccent.blue,
              ),
              pathWidth: 5,
              pathPolyline:
                  "%7DrpeFxbnjVsFwdAvr%40cHgFor%40jEmAlFmEMwM_FuItCkOi%40wc%40bg%40wBSgM",
              pathOpacity: 0.8,
            ),
            // center: (lat: 37.766541503617475, long: -122.44702324243272),
            auto: true,
          );
          break;
        case StaticExampleType.simple:
          uri = _staticImage.getStaticUrlWithoutMarker(
            center: (lat: 48.8584, long: 2.2945), // Eiffel Tower
            zoomLevel: 14,
            width: 600,
            height: 400,
            style: _selectedStyle,
            render2x: true,
          );
          break;
        case StaticExampleType.auto:
          // Auto-framing example with marker
          uri = _staticImage.getStaticUrlWithMarker(
            center: (
              lat: 40.7128,
              long: -74.0060,
            ), // NYC (Ignored if auto is true usually, but needed for API sig)
            marker: MapBoxMarker(
              markerColor: const RgbColor(255, 165, 0),
              markerLetter: 'a',
              markerSize: MarkerSize.LARGE,
            ),
            auto: true,
            width: 600,
            height: 400,
            style: _selectedStyle,
            render2x: true,
          );
          break;
      }

      setState(() => _imageUrl = uri.toString());
    } catch (e) {
      ScaffoldMessenger.of(
        context,
      ).showSnackBar(SnackBar(content: Text("Error: $e")));
    }
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            DropdownButton<MapBoxStyle>(
              value: _selectedStyle,
              items: MapBoxStyle.values.map((s) {
                return DropdownMenuItem(value: s, child: Text(s.name));
              }).toList(),
              onChanged: (v) {
                if (v != null) setState(() => _selectedStyle = v);
              },
            ),
            DropdownButton<StaticExampleType>(
              value: _selectedType,
              items: StaticExampleType.values.map((t) {
                return DropdownMenuItem(
                  value: t,
                  child: Text(t.name.toUpperCase()),
                );
              }).toList(),
              onChanged: (v) {
                if (v != null) setState(() => _selectedType = v);
              },
            ),
            const SizedBox(height: 16),
            if (_imageUrl != null)
              Container(
                constraints: const BoxConstraints(maxHeight: 400),
                child: Image.network(
                  _imageUrl!,
                  errorBuilder: (context, error, stackTrace) =>
                      const Icon(Icons.error, size: 50),
                  loadingBuilder: (context, child, loadingProgress) {
                    if (loadingProgress == null) return child;
                    return const CircularProgressIndicator();
                  },
                ),
              )
            else
              const SizedBox(
                height: 200,
                child: Center(child: Text("Select options and Generate")),
              ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: _generateImage,
              child: const Text('Generate Static Image'),
            ),
          ],
        ),
      ),
    );
  }
}
96
likes
140
points
9k
downloads

Publisher

verified publisherketanchoyal.dev

Weekly Downloads

A Flutter package for place search using MapBox Api and for Static map image

Repository (GitHub)
View/report issues
Contributing

Documentation

API reference

License

MIT (license)

Dependencies

color, dio, http, json_annotation, uuid

More

Packages that depend on mapbox_search