diff --git a/CHANGELOG.md b/CHANGELOG.md index a4443b6..dc2d6e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,3 +25,11 @@ ## 2.0.5 * Support Null Safety and code improvement + +## 2.0.6 + +* Support custom list item builder, error handled and minor fixes + +## 2.0.7 + +* Update dio dependency and minor improvements diff --git a/README.md b/README.md index adfbe39..e03de98 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ dependencies: # Google AutoComplete TextField Widget code - ``` GooglePlaceAutoCompleteTextField( textEditingController: controller, @@ -29,12 +28,40 @@ dependencies: controller.text=prediction.description; controller.selection = TextSelection.fromPosition(TextPosition(offset: prediction.description.length)); } + // if we want to make custom list item builder + itemBuilder: (context, index, Prediction prediction) { + return Container( + padding: EdgeInsets.all(10), + child: Row( + children: [ + Icon(Icons.location_on), + SizedBox( + width: 7, + ), + Expanded(child: Text("${prediction.description??""}")) + ], + ), + ); + } + // if you want to add seperator between list items + seperatedBuilder: Divider(), + // want to show close icon + isCrossBtnShown: true, + // optional container padding + containerHorizontalPadding: 10, + + + + ) ``` + # Customization Option - You can customize a text field input decoration and debounce time + +You can customize a text field input decoration and debounce time # Screenshorts + diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 0f6a5e5..8ffc107 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 28 + compileSdkVersion 33 sourceSets { main.java.srcDirs += 'src/main/kotlin' @@ -40,7 +40,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.example" minSdkVersion 16 - targetSdkVersion 28 + targetSdkVersion 33 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -53,6 +53,16 @@ android { signingConfig signingConfigs.debug } } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = 1.8 + } + + namespace 'com.example.example' } flutter { diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 7913ba0..fe45692 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@ diff --git a/example/android/build.gradle b/example/android/build.gradle index 3100ad2..0cb092f 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.8.21' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:8.0.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 296b146..55de1e6 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip diff --git a/example/lib/main.dart b/example/lib/main.dart index c566e10..f9bb777 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -19,9 +19,9 @@ class MyApp extends StatelessWidget { } class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title}) : super(key: key); + MyHomePage({Key? key, this.title}) : super(key: key); - final String title; + final String? title; @override _MyHomePageState createState() => _MyHomePageState(); @@ -36,7 +36,7 @@ class _MyHomePageState extends State { appBar: AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. - title: Text(widget.title), + title: Text(widget.title ?? ""), ), body: Center( // Center is a layout widget. It takes a single child and positions it @@ -70,23 +70,48 @@ class _MyHomePageState extends State { return Container( padding: EdgeInsets.symmetric(horizontal: 20), child: GooglePlaceAutoCompleteTextField( - textEditingController: controller, - googleAPIKey: "YOUR_GOOGLE_API_KEY", - inputDecoration: InputDecoration(hintText: "Search your location"), - debounceTime: 800, - countries: ["in", "fr"], - isLatLngRequired: true, - getPlaceDetailWithLatLng: (Prediction prediction) { - print("placeDetails" + prediction.lng.toString()); - }, - itmClick: (Prediction prediction) { - controller.text = prediction.description; + textEditingController: controller, + googleAPIKey:"YOUR_GOOGLE_API_KEY", + inputDecoration: InputDecoration( + hintText: "Search your location", + border: InputBorder.none, + enabledBorder: InputBorder.none, + ), + debounceTime: 400, + countries: ["in", "fr"], + isLatLngRequired: false, + getPlaceDetailWithLatLng: (Prediction prediction) { + print("placeDetails" + prediction.lat.toString()); + }, + + itemClick: (Prediction prediction) { + controller.text = prediction.description ?? ""; + controller.selection = TextSelection.fromPosition( + TextPosition(offset: prediction.description?.length ?? 0)); + }, + seperatedBuilder: Divider(), + containerHorizontalPadding: 10, + + // OPTIONAL// If you want to customize list view item builder + itemBuilder: (context, index, Prediction prediction) { + return Container( + padding: EdgeInsets.all(10), + child: Row( + children: [ + Icon(Icons.location_on), + SizedBox( + width: 7, + ), + Expanded(child: Text("${prediction.description??""}")) + ], + ), + ); + }, - controller.selection = TextSelection.fromPosition( - TextPosition(offset: prediction.description.length)); - } - // default 600 ms , - ), + isCrossBtnShown: true, + + // default 600 ms , + ), ); } } diff --git a/example/pubspec.lock b/example/pubspec.lock index a0212ba..5967b8c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,65 +5,74 @@ packages: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.3.0" charcode: dependency: transitive description: name: charcode - url: "https://pub.dartlang.org" + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.1" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.17.2" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - url: "https://pub.dartlang.org" + sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "1.0.5" dio: dependency: transitive description: name: dio - url: "https://pub.dartlang.org" + sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3" + url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.4.0" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -80,40 +89,53 @@ packages: path: ".." relative: true source: path - version: "2.0.4" + version: "2.0.6" http_parser: dependency: transitive description: name: http_parser - url: "https://pub.dartlang.org" + sha256: e362d639ba3bc07d5a71faebb98cde68c05bfbcfbbb444b60b6f60bb67719185 + url: "https://pub.dev" source: hosted version: "4.0.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" source: hosted - version: "0.12.10" + version: "0.12.16" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" + source: hosted + version: "0.5.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.9.1" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.8.3" rxdart: dependency: transitive description: name: rxdart - url: "https://pub.dartlang.org" + sha256: "7358d9e183dd9002a16c17200186c7af56dd9e210b030853ca669ccd02e3d5d6" + url: "https://pub.dev" source: hosted version: "0.27.2" sky_engine: @@ -125,57 +147,73 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" source: hosted - version: "1.8.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.6.0" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: "53bdf7e979cfbf3e28987552fd72f637e63f3c8724c9e56d9246942dc2fa36ee" + url: "https://pub.dev" source: hosted version: "1.3.0" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "0.1.4-beta" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 9841a8d..20d3a3d 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -14,7 +14,7 @@ description: A new Flutter application. version: 1.0.0+1 environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=2.12.0 <4.0.0" dependencies: flutter: @@ -22,7 +22,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.2 + cupertino_icons: ^1.0.1 google_places_flutter: path: ../ diff --git a/lib/DioErrorHandler.dart b/lib/DioErrorHandler.dart new file mode 100644 index 0000000..006149e --- /dev/null +++ b/lib/DioErrorHandler.dart @@ -0,0 +1,150 @@ +import 'package:dio/dio.dart'; + +import 'error_response.dart'; + +class DioErrorHandler { + ErrorResponse errorResponse = ErrorResponse(); + String errorDescription = ""; + + + + + ErrorResponse handleDioError(DioException dioError) { + switch (dioError.type) { + case DioExceptionType.cancel: + + errorResponse.message = "Request to API server was cancelled"; + break; + case DioExceptionType.connectionTimeout: + errorResponse.message = "Connection timeout with API server"; + break; + case DioExceptionType.unknown: + + if((dioError.message?.contains("RedirectException")??false)){ + errorResponse.message = "${dioError.message}"; + }else { + errorResponse.message = "Please check the internet connection"; + } + break; + case DioExceptionType.receiveTimeout: + + errorResponse.message = "Receive timeout in connection with API server"; + break; + case DioExceptionType.badResponse: + try { + + if (dioError.response?.data['message'] != null) { + errorResponse.message = dioError.response?.data['message']; + } else { + if ((dioError.response?.statusMessage ?? "").isNotEmpty) + errorResponse.message = dioError.response?.statusMessage; + else + return _handleError( + dioError.response!.statusCode, dioError.response!.data); + } + } catch (e) { + + if ((dioError.response?.statusMessage ?? "").isNotEmpty) + errorResponse.message = dioError.response?.statusMessage; + else + return _handleError( + dioError.response!.statusCode, dioError.response!.data); + } + + break; + case DioExceptionType.sendTimeout: + + errorResponse.message = "Send timeout in connection with API server"; + break; + default: + + errorResponse.message = "Something went wrong"; + break; + } + return errorResponse; + } + + ErrorResponse _handleError(int? statusCode, dynamic error) { + switch (statusCode) { + case 400: + return getMas(error); + // case 401: + // return checkTokenExpire(error); + case 404: + return getMas(error); + case 403: + return getMas(error); + case 500: + errorResponse.message = 'Internal server error'; + return errorResponse; + default: + return getUnKnownMes(error); + } + } + + // checkTokenExpire(error) { + // // print("my error ${error}"); + // if (error['msg'].toString().toLowerCase() == + // "Token has expired".toLowerCase()) { + // UIData.tokenExpire(error['msg']); + // return; + // } + // errorResponse.message = error['msg'].toString(); + // return errorResponse; + // } + + getMas(dynamic error) { + print("myError ${error.runtimeType}"); + if (error.runtimeType != String) { + errorResponse.message = + error['message'].toString(); //?? S.of(Get.context).something_wrong; + } else { + if (error['msg'] != null) { + errorResponse.message = error['msg'].toString(); + } else { + errorResponse.message = "Something Wrong"; + } //S.of(Get.context).something_wrong; + } + return errorResponse; + } + + getUnKnownMes(dynamic error) { + if (error['msg'] != null) { + errorResponse.message = error['msg'].toString(); + } else if (error['message'] != null) { + errorResponse.message = error['message'].toString(); + } else { + errorResponse.message = "Something went wrong"; + } + return errorResponse; + } +} + + +class ErrorHandler { + + static final ErrorHandler _inst=ErrorHandler.internal(); + ErrorHandler.internal(); + + factory ErrorHandler() { + return _inst; + } + ErrorResponse errorResponse=ErrorResponse(); + + ErrorResponse handleError(var error) { + if(error.runtimeType.toString().toLowerCase() =="_TypeError".toLowerCase()){ + // return error.toString(); + errorResponse.message ="The Provided API key is invalid"; + return errorResponse; + } + + + else if(error is DioException) { + return DioErrorHandler().handleDioError(error); + } + errorResponse.message = "The Provided API key is invalid"; + return errorResponse; + } + + +} \ No newline at end of file diff --git a/lib/error_response.dart b/lib/error_response.dart new file mode 100644 index 0000000..8fb8150 --- /dev/null +++ b/lib/error_response.dart @@ -0,0 +1,20 @@ +class ErrorResponse { + String? message; + int? status; + + ErrorResponse( {this.message, this.status}); + + ErrorResponse.fromJson(Map json) { + message = json['message']; + status = json['status']; + + } + + Map toJson() { + final Map data = {}; + data['message'] = message; + data['status'] = status; + + return data; + } +} \ No newline at end of file diff --git a/lib/google_places_flutter.dart b/lib/google_places_flutter.dart index 4c37024..8c0320c 100644 --- a/lib/google_places_flutter.dart +++ b/lib/google_places_flutter.dart @@ -1,6 +1,5 @@ library google_places_flutter; -import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -11,9 +10,11 @@ import 'package:rxdart/subjects.dart'; import 'package:dio/dio.dart'; import 'package:rxdart/rxdart.dart'; +import 'DioErrorHandler.dart'; + class GooglePlaceAutoCompleteTextField extends StatefulWidget { InputDecoration inputDecoration; - ItemClick? itmClick; + ItemClick? itemClick; GetPlaceDetailswWithLatLng? getPlaceDetailWithLatLng; bool isLatLngRequired = true; @@ -22,18 +23,30 @@ class GooglePlaceAutoCompleteTextField extends StatefulWidget { int debounceTime = 600; List? countries = []; TextEditingController textEditingController = TextEditingController(); + ListItemBuilder? itemBuilder; + Widget? seperatedBuilder; + void clearData; + BoxDecoration? boxDecoration; + bool isCrossBtnShown; + bool showError; + double? containerHorizontalPadding; + double? containerVerticalPadding; GooglePlaceAutoCompleteTextField( {required this.textEditingController, required this.googleAPIKey, this.debounceTime: 600, this.inputDecoration: const InputDecoration(), - this.itmClick, - this.isLatLngRequired=true, + this.itemClick, + this.isLatLngRequired = true, this.textStyle: const TextStyle(), this.countries, this.getPlaceDetailWithLatLng, - }); + this.itemBuilder, + this.boxDecoration, + this.isCrossBtnShown = true, + this.seperatedBuilder,this.showError=true,this + .containerHorizontalPadding,this.containerVerticalPadding}); @override _GooglePlaceAutoCompleteTextFieldState createState() => @@ -50,21 +63,57 @@ class _GooglePlaceAutoCompleteTextFieldState final LayerLink _layerLink = LayerLink(); bool isSearched = false; + bool isCrossBtn = true; + late var _dio; + + CancelToken? _cancelToken = CancelToken(); + + + + @override Widget build(BuildContext context) { + return CompositedTransformTarget( link: _layerLink, - child: TextFormField( - decoration: widget.inputDecoration, - style: widget.textStyle, - controller: widget.textEditingController, - onChanged: (string) => (subject.add(string)), + child: Container( + padding: EdgeInsets.symmetric(horizontal: widget.containerHorizontalPadding??0, vertical: widget.containerVerticalPadding??0), + alignment: Alignment.centerLeft, + decoration: widget.boxDecoration ?? + BoxDecoration( + shape: BoxShape.rectangle, + border: Border.all(color: Colors.grey, width: 0.6), + borderRadius: BorderRadius.all(Radius.circular(10))), + child: Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: TextFormField( + decoration: widget.inputDecoration, + style: widget.textStyle, + controller: widget.textEditingController, + onChanged: (string) { + subject.add(string); + if (widget.isCrossBtnShown) { + isCrossBtn = string.isNotEmpty ? true : false; + setState(() {}); + } + }, + ), + ), + (!widget.isCrossBtnShown) + ? SizedBox() + : isCrossBtn && _showCrossIconWidget() + ? IconButton(onPressed: clearData, icon: Icon(Icons.close)) + : SizedBox() + ], + ), ), ); } getLocation(String text) async { - Dio dio = new Dio(); String url = "https://maps.googleapis.com/maps/api/place/autocomplete/json?input=$text&key=${widget.googleAPIKey}"; @@ -82,34 +131,50 @@ class _GooglePlaceAutoCompleteTextFieldState } } + if (_cancelToken?.isCancelled == false) { + _cancelToken?.cancel(); + _cancelToken = CancelToken(); + } - Response response = await dio.get(url); - PlacesAutocompleteResponse subscriptionResponse = - PlacesAutocompleteResponse.fromJson(response.data); + try { + Response response = await _dio.get(url); + ScaffoldMessenger.of(context).hideCurrentSnackBar(); - if (text.length == 0) { - alPredictions.clear(); - this._overlayEntry!.remove(); - return; - } + Map map = response.data; + if (map.containsKey("error_message")) { + throw response.data; + } + + PlacesAutocompleteResponse subscriptionResponse = + PlacesAutocompleteResponse.fromJson(response.data); + + if (text.length == 0) { + alPredictions.clear(); + this._overlayEntry!.remove(); + return; + } - isSearched = false; - if (subscriptionResponse.predictions!.length > 0) { + isSearched = false; alPredictions.clear(); - alPredictions.addAll(subscriptionResponse.predictions!); - } + if (subscriptionResponse.predictions!.length > 0 && (widget.textEditingController.text.toString().trim()).isNotEmpty) { + alPredictions.addAll(subscriptionResponse.predictions!); + } - //if (this._overlayEntry == null) - this._overlayEntry = null; - this._overlayEntry = this._createOverlayEntry(); - Overlay.of(context)!.insert(this._overlayEntry!); - // this._overlayEntry.markNeedsBuild(); + this._overlayEntry = null; + this._overlayEntry = this._createOverlayEntry(); + Overlay.of(context)!.insert(this._overlayEntry!); + } catch (e) { + var errorHandler = ErrorHandler.internal().handleError(e); + _showSnackBar("${errorHandler.message}"); + } } @override void initState() { + super.initState(); + _dio = Dio(); subject.stream .distinct() .debounceTime(Duration(milliseconds: widget.debounceTime)) @@ -135,30 +200,34 @@ class _GooglePlaceAutoCompleteTextFieldState link: this._layerLink, offset: Offset(0.0, size.height + 5.0), child: Material( - elevation: 1.0, - child: ListView.builder( - padding: EdgeInsets.zero, - shrinkWrap: true, - itemCount: alPredictions.length, - itemBuilder: (BuildContext context, int index) { - return InkWell( - onTap: () { - if (index < alPredictions.length) { - widget.itmClick!(alPredictions[index]); - if (!widget.isLatLngRequired) return; - - getPlaceDetailsFromPlaceId( - alPredictions[index]); - - removeOverlay(); - } - }, - child: Container( + child: ListView.separated( + padding: EdgeInsets.zero, + shrinkWrap: true, + itemCount: alPredictions.length, + separatorBuilder: (context, pos) => + widget.seperatedBuilder ?? SizedBox(), + itemBuilder: (BuildContext context, int index) { + return InkWell( + onTap: () { + var selectedData = alPredictions[index]; + if (index < alPredictions.length) { + widget.itemClick!(selectedData); + + if (widget.isLatLngRequired) { + getPlaceDetailsFromPlaceId(selectedData); + } + removeOverlay(); + } + }, + child: widget.itemBuilder != null + ? widget.itemBuilder!( + context, index, alPredictions[index]) + : Container( padding: EdgeInsets.all(10), child: Text(alPredictions[index].description!)), - ); - }, - )), + ); + }, + )), ), )); } @@ -168,7 +237,7 @@ class _GooglePlaceAutoCompleteTextFieldState alPredictions.clear(); this._overlayEntry = this._createOverlayEntry(); if (context != null) { - Overlay.of(context)!.insert(this._overlayEntry!); + Overlay.of(context).insert(this._overlayEntry!); this._overlayEntry!.markNeedsBuild(); } } @@ -188,15 +257,47 @@ class _GooglePlaceAutoCompleteTextFieldState prediction.lng = placeDetails.result!.geometry!.location!.lng.toString(); widget.getPlaceDetailWithLatLng!(prediction); + } + + void clearData() { + widget.textEditingController.clear(); + if (_cancelToken?.isCancelled == false) { + _cancelToken?.cancel(); + } + + setState(() { + alPredictions.clear(); + isCrossBtn = false; + }); + + if (this._overlayEntry != null) { + try { + this._overlayEntry?.remove(); + } catch (e) {} + } + } + + _showCrossIconWidget() { + return (widget.textEditingController.text.isNotEmpty); + } + + _showSnackBar(String errorData) { + if(widget.showError){ + final snackBar = SnackBar( + content: Text("$errorData"), + ); + + // Find the ScaffoldMessenger in the widget tree + // and use it to show a SnackBar. + ScaffoldMessenger.of(context).showSnackBar(snackBar); + } -// prediction.latLng = new LatLng( -// placeDetails.result.geometry.location.lat, -// placeDetails.result.geometry.location.lng); } } PlacesAutocompleteResponse parseResponse(Map responseBody) { - return PlacesAutocompleteResponse.fromJson(responseBody as Map); + return PlacesAutocompleteResponse.fromJson( + responseBody as Map); } PlaceDetails parsePlaceDetailMap(Map responseBody) { @@ -206,3 +307,6 @@ PlaceDetails parsePlaceDetailMap(Map responseBody) { typedef ItemClick = void Function(Prediction postalCodeResponse); typedef GetPlaceDetailswWithLatLng = void Function( Prediction postalCodeResponse); + +typedef ListItemBuilder = Widget Function( + BuildContext context, int index, Prediction prediction); diff --git a/pubspec.lock b/pubspec.lock index 248816a..24f995f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,58 +5,66 @@ packages: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.3.0" charcode: dependency: transitive description: name: charcode - url: "https://pub.dartlang.org" + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.1" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.17.2" dio: dependency: "direct main" description: name: dio - url: "https://pub.dartlang.org" + sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3" + url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.4.0" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -71,35 +79,48 @@ packages: dependency: transitive description: name: http_parser - url: "https://pub.dartlang.org" + sha256: e362d639ba3bc07d5a71faebb98cde68c05bfbcfbbb444b60b6f60bb67719185 + url: "https://pub.dev" source: hosted version: "4.0.0" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" source: hosted - version: "0.12.10" + version: "0.12.16" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" + source: hosted + version: "0.5.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.9.1" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.8.3" rxdart: dependency: "direct main" description: name: rxdart - url: "https://pub.dartlang.org" + sha256: "7358d9e183dd9002a16c17200186c7af56dd9e210b030853ca669ccd02e3d5d6" + url: "https://pub.dev" source: hosted version: "0.27.2" sky_engine: @@ -111,57 +132,73 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" source: hosted - version: "1.8.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.6.0" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: "53bdf7e979cfbf3e28987552fd72f637e63f3c8724c9e56d9246942dc2fa36ee" + url: "https://pub.dev" source: hosted version: "1.3.0" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "0.1.4-beta" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 77e6135..bbe2ddc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,17 +1,17 @@ name: google_places_flutter description: Custom Google places autocomplete widget for Android and iOS both. Use https://pub.dev/packages/google_places_flutter -version: 2.0.5 +version: 2.0.7 author: Shruti Mahajan homepage: https://github.com/Shrutimahajan/Google-AutoComplete-TextField-Flutter environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.12.0 <4.0.0' dependencies: flutter: sdk: flutter - dio: ^4.0.0 + dio: ^5.4.0 rxdart: ^0.27.2