flutter_google_places_hoc081098 1.0.0-nullsafety.0 flutter_google_places_hoc081098: ^1.0.0-nullsafety.0 copied to clipboard
Google places autocomplete widgets for flutter. No wrapper, use https://pub.dartlang.org/packages/google_maps_webservice. Better flutter_google_places, updated by @hoc081098
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_google_places_hoc081098/flutter_google_places_hoc081098.dart';
import 'package:google_api_headers/google_api_headers.dart';
import 'package:google_maps_webservice/places.dart';
const kGoogleApiKey = 'API_KEY';
void main() => runApp(RoutesWidget());
final customTheme = ThemeData(
primarySwatch: Colors.blue,
brightness: Brightness.dark,
accentColor: Colors.redAccent,
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4.00)),
),
contentPadding: EdgeInsets.symmetric(
vertical: 12.50,
horizontal: 10.00,
),
),
);
class RoutesWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
theme: customTheme,
routes: {
'/': (_) => MyApp(),
'/search': (_) => CustomSearchScaffold(),
},
);
}
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Mode _mode = Mode.overlay;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My App'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildDropdownMenu(),
const SizedBox(height: 12),
ElevatedButton(
onPressed: _handlePressButton,
child: Text('Search places'),
),
const SizedBox(height: 12),
ElevatedButton(
onPressed: () {
Navigator.of(context).pushNamed('/search');
},
child: Text('Custom'),
),
],
),
),
);
}
Widget _buildDropdownMenu() {
return DropdownButton<Mode>(
value: _mode,
items: <DropdownMenuItem<Mode>>[
DropdownMenuItem<Mode>(
value: Mode.overlay,
child: Text('Overlay'),
),
DropdownMenuItem<Mode>(
value: Mode.fullscreen,
child: Text('Fullscreen'),
),
],
onChanged: (m) {
if (m != null) {
setState(() => _mode = m);
}
},
);
}
Future<void> _handlePressButton() async {
void onError(PlacesAutocompleteResponse response) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(response.errorMessage ?? 'Unknown error'),
),
);
}
// show input autocomplete with selected mode
// then get the Prediction selected
final p = await PlacesAutocomplete.show(
context: context,
apiKey: kGoogleApiKey,
onError: onError,
mode: _mode,
language: 'fr',
components: [Component(Component.country, 'fr')],
);
await displayPrediction(p, ScaffoldMessenger.of(context));
}
}
Future<void> displayPrediction(
Prediction? p, ScaffoldMessengerState messengerState) async {
if (p == null) {
return;
}
// get detail (lat/lng)
final _places = GoogleMapsPlaces(
apiKey: kGoogleApiKey,
apiHeaders: await GoogleApiHeaders().getHeaders(),
);
final detail = await _places.getDetailsByPlaceId(p.placeId!);
final geometry = detail.result.geometry!;
final lat = geometry.location.lat;
final lng = geometry.location.lng;
messengerState.showSnackBar(
SnackBar(
content: Text('${p.description} - $lat/$lng'),
),
);
}
// custom scaffold that handle search
// basically your widget need to extends [GooglePlacesAutocompleteWidget]
// and your state [GooglePlacesAutocompleteState]
class CustomSearchScaffold extends PlacesAutocompleteWidget {
CustomSearchScaffold()
: super(
apiKey: kGoogleApiKey,
sessionToken: Uuid().generateV4(),
language: 'en',
components: [Component(Component.country, 'uk')],
);
@override
_CustomSearchScaffoldState createState() => _CustomSearchScaffoldState();
}
class _CustomSearchScaffoldState extends PlacesAutocompleteState {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: AppBarPlacesAutoCompleteTextField(
textStyle: null, textDecoration: null)),
body: PlacesAutocompleteResult(
onTap: (p) => displayPrediction(p, ScaffoldMessenger.of(context)),
logo: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [FlutterLogo()],
),
),
);
}
@override
void onResponseError(PlacesAutocompleteResponse response) {
super.onResponseError(response);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(response.errorMessage ?? 'Unknown error')),
);
}
@override
void onResponse(PlacesAutocompleteResponse response) {
super.onResponse(response);
if (response.predictions.isNotEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Got answer')),
);
}
}
}
class Uuid {
final Random _random = Random();
String generateV4() {
// Generate xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx / 8-4-4-4-12.
final special = 8 + _random.nextInt(4);
return '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}-'
'${_bitsDigits(16, 4)}-'
'4${_bitsDigits(12, 3)}-'
'${_printDigits(special, 1)}${_bitsDigits(12, 3)}-'
'${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}';
}
String _bitsDigits(int bitCount, int digitCount) =>
_printDigits(_generateBits(bitCount), digitCount);
int _generateBits(int bitCount) => _random.nextInt(1 << bitCount);
String _printDigits(int value, int count) =>
value.toRadixString(16).padLeft(count, '0');
}