Skip to main content
GuidesAPI ReferenceExamplesFAQ

Search Location

|

In this guide, you will learn how to integrate map functionality and perform searches for landmarks using the Maps SDK for Flutter.

How It Works

This example demonstrates the following features:

  • Search for landmarks around a specific location using latitude and longitude coordinates.
Initial map view
Text search page

Define state variables and methods

Within _MyHomePageState , define the necessary state variables and methods to manage the map and perform searches.

main.dart
class _MyHomePageState extends State<MyHomePage> {
late GemMapController _mapController;

void _onMapCreated(GemMapController controller) {
_mapController = controller;
}


void dispose() {
GemKit.release();
super.dispose();
}


Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepPurple[900],
title: const Text("Search Location", style: TextStyle(color: Colors.white)),
actions: [
IconButton(
onPressed: () => _onSearchButtonPressed(context),
icon: const Icon(Icons.search, color: Colors.white),
),
],
),
body: GemMap(
key: ValueKey("GemMap"),
onMapCreated: _onMapCreated,
appAuthorization: projectApiToken,
),
);
}

void _onSearchButtonPressed(BuildContext context) async {
// Taking the coordinates at the center of the screen as reference coordinates for search.
final x = MediaQuery.of(context).size.width / 2;
final y = MediaQuery.of(context).size.height / 2;
final mapCoords = _mapController.transformScreenToWgs(XyType(x: x.toInt(), y: y.toInt()));

// Navigating to search screen. The result will be the selected search result (Landmark)
final result = await Navigator.of(context).push(MaterialPageRoute<dynamic>(
builder: (context) => SearchPage(coordinates: mapCoords!),
));

if (result is Landmark) {
// Activating the highlight
_mapController.activateHighlight([result], renderSettings: RenderSettings());
// Centering the map on the desired coordinates
_mapController.centerOnCoordinates(result.coordinates);
}
}
}

Define Search Functionality

Implement the SearchPage widget that allows users to search for landmarks.

search_page.dart
class SearchPage extends StatefulWidget {
final Coordinates coordinates;
const SearchPage({super.key, required this.coordinates});


State<SearchPage> createState() => _SearchPageState();
}

class _SearchPageState extends State<SearchPage> {
List<Landmark> landmarks = [];

final TextEditingController _tecLatitude = TextEditingController();
final TextEditingController _tecLongitude = TextEditingController();


void initState() {
super.initState();

//Set initial coordinates the center of the map
_tecLatitude.text = widget.coordinates.latitude.toString();
_tecLongitude.text = widget.coordinates.longitude.toString();
}


Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
title: const Text("Search Location"),
backgroundColor: Colors.deepPurple[900],
foregroundColor: Colors.white,
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _tecLatitude,
cursorColor: Colors.deepPurple[900],
decoration: const InputDecoration(
hintText: 'Latitude',
hintStyle: TextStyle(color: Colors.black),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.deepPurple, width: 2.0),
),
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _tecLongitude,
cursorColor: Colors.deepPurple[900],
decoration: const InputDecoration(
hintText: 'Longitude',
hintStyle: TextStyle(color: Colors.black),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.deepPurple, width: 2.0),
),
),
),
),
ElevatedButton(
onPressed: _onSearchSubmitted,
child: const Text("Search"),
),
Expanded(
child: ListView.separated(
padding: EdgeInsets.zero,
itemCount: landmarks.length,
controller: ScrollController(),
separatorBuilder: (context, index) => const Divider(indent: 50, height: 0),
itemBuilder: (context, index) {
final lmk = landmarks.elementAt(index);
return SearchResultItem(landmark: lmk);
},
),
),
],
),
);
}

void _onSearchSubmitted() {
final latitude = double.tryParse(_tecLatitude.text);
final longitude = double.tryParse(_tecLongitude.text);

if (latitude == null || longitude == null) {
print("Invalid values for the reference coordinate.");
return;
}

Coordinates coords = Coordinates(latitude: latitude, longitude: longitude);
SearchPreferences preferences = SearchPreferences(
maxMatches: 40,
allowFuzzyResults: true,
);

search(coords, preferences: preferences);
}

late Completer<List<Landmark>> completer;

// Search method. Coordinates are mandatory, preferences are optional.
Future<void> search(
Coordinates coordinates, {
SearchPreferences? preferences,
}) async {
completer = Completer<List<Landmark>>();

// Calling the search around position SDK method.
// (err, results) - is a callback function that calls when the computing is done.
// err is an error code, results is a list of landmarks
SearchService.searchAroundPosition(coordinates, preferences: preferences, (
err,
results,
) async {
// If there is an error or there aren't any results, the method will return an empty list.
if (err != GemError.success) {
completer.complete([]);
return;
}

if (!completer.isCompleted) completer.complete(results);
});

final result = await completer.future;

setState(() {
landmarks = result;
});
}
}

Define Class for the Search Results

Implement the SearchResultItem widget to display search results.

search_page.dart
class SearchResultItem extends StatefulWidget {
final Landmark landmark;

const SearchResultItem({super.key, required this.landmark});


State<SearchResultItem> createState() => _SearchResultItemState();
}

class _SearchResultItemState extends State<SearchResultItem> {

Widget build(BuildContext context) {
return ListTile(
onTap: () => Navigator.of(context).pop(widget.landmark),
leading: Container(
padding: const EdgeInsets.all(8),
width: 50,
child:
widget.landmark.getImage() != null
? Image.memory(widget.landmark.getImage()!)
: SizedBox(),
),
title: Text(
widget.landmark.name,
overflow: TextOverflow.fade,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w400,
),
maxLines: 2,
),
subtitle: Text(
widget.landmark.getFormattedDistance() + widget.landmark.getAddress(),
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w400,
),
),
);
}
}

Define Extensions for Landmarks

Implement an extension to format the address and distance of landmarks.

search_page.dart
extension LandmarkExtension on Landmark {
String getAddress() {
final addressInfo = address;
final street = addressInfo.getField(AddressField.streetName);
final city = addressInfo.getField(AddressField.city);
final country = addressInfo.getField(AddressField.country);

return '$street $city $country';
}

String getFormattedDistance() {
String formattedDistance = '';

double distance = (extraInfo.getByKey(PredefinedExtraInfoKey.gmSearchResultDistance) / 1000) as double;
formattedDistance = "${distance.toStringAsFixed(0)}km";
return formattedDistance;
}
}