smart_location_field
A single Flutter TextFormField that can behave two ways:
- Normal field — email, password (with a show/hide toggle), or any plain input.
- Google Places search field — restrictable to country-only, state-only, city-only, or normal location search results, with a suggestions dropdown.
Every Google Places network call goes through smart_api (not raw
http/https) via one function, so swapping the exact call is a one-place
edit if needed. Every icon slot (prefix, suffix, the password toggle, the
suggestion row icon) can independently be an icon or an image — your
choice, per field, per slot.
Demo
Location search
Validation (email / password)
Outlined style
Install
Add it as a path or git dependency in your app's pubspec.yaml:
dependencies:
smart_location_field:
path: ../smart_location_field # or a git/pub.flutter-io.cn reference once published
Then:
import 'package:smart_location_field/smart_location_field.dart';
About the smart_api wiring
This package depends on smart_api: ^1.1.1 and calls it from one place:
_defaultSmartApiGet() in lib/src/smart_location_field_widget.dart, using
SmartApiClient.instance.get(...). Nothing uses raw http.
If you ever need a different call for one field, pass apiCaller:
SmartLocationField(
...
apiCaller: (url) async {
final res = await SmartApiClient.instance.get(url);
return res.data as Map<String, dynamic>?;
},
),
Icon or image — your choice, per slot
Every affix has an icon param and an image-path param; the image wins if
both are set. A path starting with http:///https:// loads as a network
image, anything else as an asset.
| Slot | Icon param | Image param |
|---|---|---|
| Prefix | prefixIcon |
prefixImagePath |
| Suffix (non-password) | suffixIcon |
suffixImagePath |
| Password toggle — hidden state | obscureIcon |
obscureImagePath |
| Password toggle — visible state | unObscureIcon |
unObscureImagePath |
| Suggestion row leading icon | suggestionIcon |
suggestionImagePath |
// icon
SmartLocationField(
prefixIcon: Icons.email_outlined,
...
),
// image instead — asset or network, same param
SmartLocationField(
prefixImagePath: 'assets/icons/email.png',
// or: prefixImagePath: 'https://example.com/email.png',
...
),
Validator — your choice
- Pass a full custom function via
validatorand it's used exactly as given (your logic wins). - Or just pass a message via
validatorTextand a built-in "required" check uses it as the error text. - If
isValidator: trueand neither is given, it falls back to"This field is required".
// just a message
SmartLocationField(
isValidator: true,
validatorText: "Email is required",
...
),
// full custom logic
SmartLocationField(
isValidator: true,
validator: (value) {
if (value == null || value.isEmpty) return "Email is required";
if (!value.contains('@')) return "Enter a valid email";
return null;
},
...
),
Usage
Plain email / password fields
SmartLocationField(
controller: controller.emailController,
title: "Email",
hintText: "Enter your Email",
keyboardType: TextInputType.emailAddress,
isValidator: true,
validator: controller.validateEmail,
prefixIcon: Icons.email_outlined,
),
SmartLocationField(
controller: controller.passwordController,
title: "Password",
hintText: "Enter your password",
isValidator: true,
validator: controller.validatePassword,
obscureText: true,
isObscureIcon: true,
// uses the default visibility icons unless you set
// obscureIcon/unObscureIcon or obscureImagePath/unObscureImagePath
),
Country-only search
SmartLocationField(
controller: controller.countryController,
title: "Country",
hintText: "Search a country",
searchLevel: LocationSearchLevel.country,
apiKey: "YOUR_GOOGLE_API_KEY",
onLocationSelected: (name, placeId, lat, lng, details) {
debugPrint('Country: $name (${details['countryCode']}) $lat,$lng');
},
),
Normal location search (addresses, areas, landmarks)
SmartLocationField(
controller: controller.locationController,
title: "Location",
hintText: "Search a location",
searchLevel: LocationSearchLevel.location,
apiKey: "YOUR_GOOGLE_API_KEY",
onLocationSelected: (name, placeId, lat, lng, details) {
debugPrint('Location: $name $lat,$lng');
},
),
State-only search, scoped to one country
SmartLocationField(
controller: controller.stateController,
title: "State",
hintText: "Search a state",
searchLevel: LocationSearchLevel.state,
restrictToCountries: const ['in'],
apiKey: "YOUR_GOOGLE_API_KEY",
onLocationSelected: (name, placeId, lat, lng, details) {
debugPrint('State: $name, country: ${details['countryName']}');
},
),
City-only search, biased to the user's current location
SmartLocationField(
controller: controller.cityController,
title: "City",
hintText: "Search a city",
searchLevel: LocationSearchLevel.city,
apiKey: "YOUR_GOOGLE_API_KEY",
lat: double.tryParse(controller.lat.value ?? "") ?? 0.0,
lng: double.tryParse(controller.lng.value ?? "") ?? 0.0,
onLocationSelected: (name, placeId, lat, lng, details) {
debugPrint('City: $name Lat/Lng: $lat,$lng');
},
),
onLocationSelected payload
void Function(
String name,
String? placeId,
double? lat,
double? lng,
Map<String, dynamic> details,
)
details always carries countryName, countryCode, stateName,
cityName, and formattedAddress — regardless of searchLevel — so you can
read whichever level you need.
Key parameters
| Param | Purpose |
|---|---|
searchLevel |
none / country / state / city / location |
apiKey |
Google Places API key |
apiCaller |
Override the smart_api call for this instance only |
restrictToCountries |
ISO-3166-1 alpha-2 codes, e.g. ['in'] |
lat / lng |
Bias point for search |
minSearchLength |
Characters before a search fires (default 2) |
debounceDuration |
Debounce before hitting the API (default 350ms) |
obscureText / isObscureIcon |
Password-field support |
prefixIcon/prefixImagePath, suffixIcon/suffixImagePath |
Icon-or-image affixes |
validator / validatorText |
Full custom logic, or just a message |
See example/ for a full running demo.
Publishing this package
Once you're happy with it:
flutter pub publish --dry-run
flutter pub publish
(Fill in homepage/repository in pubspec.yaml first — pub.flutter-io.cn requires
a valid homepage or repository URL to publish. Also confirm the smart_api
dependency resolves correctly for whoever installs this package — a public
pub.flutter-io.cn consumer won't have access to a private git/path source unless you
adjust it.)
Libraries
- generated/assets
- This file is automatically generated. DO NOT EDIT, all your changes would be lost.
- main
- smart_location_field
- A Flutter TextFormField that can also work as a Google Places search
box — restrictable to country-only, state-only, or city-only results, or a
normal unrestricted location search — with all network calls routed
through
smart_api.