From 7c407ad57663d7a8b1ff3f08b3473931120aaa04 Mon Sep 17 00:00:00 2001 From: mozaffari Date: Mon, 14 Jun 2021 11:46:26 +0430 Subject: [PATCH 01/11] Ignore pubspec.lock --- .gitignore | 1 + pubspec.lock | 236 --------------------------------------------------- 2 files changed, 1 insertion(+), 236 deletions(-) delete mode 100644 pubspec.lock diff --git a/.gitignore b/.gitignore index 9d7edcf..bd504c0 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,4 @@ build/ !**/ios/**/default.pbxuser !**/ios/**/default.perspectivev3 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +pubspec.lock \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index b775812..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,236 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - url: "https://pub.dartlang.org" - source: hosted - version: "2.5.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.dartlang.org" - source: hosted - version: "1.15.0" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - google_maps_flutter: - dependency: "direct main" - description: - name: google_maps_flutter - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.1" - google_maps_flutter_platform_interface: - dependency: transitive - description: - name: google_maps_flutter_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.2" - http: - dependency: "direct main" - description: - name: http - url: "https://pub.dartlang.org" - source: hosted - version: "0.13.1" - http_parser: - dependency: transitive - description: - name: http_parser - url: "https://pub.dartlang.org" - source: hosted - version: "4.0.0" - js: - dependency: transitive - description: - name: js - url: "https://pub.dartlang.org" - source: hosted - version: "0.6.3" - location: - dependency: "direct main" - description: - name: location - url: "https://pub.dartlang.org" - source: hosted - version: "4.1.1" - location_platform_interface: - dependency: transitive - description: - name: location_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - location_web: - dependency: transitive - description: - name: location_web - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.0" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.10" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" - path: - dependency: transitive - description: - name: path - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - pedantic: - dependency: transitive - description: - name: pedantic - url: "https://pub.dartlang.org" - source: hosted - version: "1.11.0" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - stream_transform: - dependency: transitive - description: - name: stream_transform - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.19" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" -sdks: - dart: ">=2.12.0 <3.0.0" - flutter: ">=1.22.0" From 7a76817e7cf9803d646af0fda22ac5775a9aaf59 Mon Sep 17 00:00:00 2001 From: mozaffari Date: Tue, 15 Jun 2021 10:38:40 +0430 Subject: [PATCH 02/11] Add: Search bar customization --- lib/entities/search_bar_options_model.dart | 53 ++++++++++++++++++++++ lib/place_picker.dart | 1 + lib/widgets/place_picker.dart | 31 +++++++++++-- lib/widgets/search_input.dart | 53 ++++++++++++++-------- 4 files changed, 116 insertions(+), 22 deletions(-) create mode 100644 lib/entities/search_bar_options_model.dart diff --git a/lib/entities/search_bar_options_model.dart b/lib/entities/search_bar_options_model.dart new file mode 100644 index 0000000..98c0e4d --- /dev/null +++ b/lib/entities/search_bar_options_model.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; + +class SearchBarOptions { + ///To Wrap search bar with padding + final EdgeInsetsGeometry? padding; + + ///Search icon padding + final EdgeInsetsGeometry? searchIconPadding; + + ///clear Text button icon padding + final EdgeInsetsGeometry? clearTextIconPadding; + + ///A background color to set if the search bar hasPadding + final Color? backgroundColor; + + ///elavtion of Searchbar in case for example you add a transparetnt background color to searchbar + final double? elevation; + + ///This is used to add extra space on top of autosuggestion. + ///This is usefull for example when you use PlacePicker inside another Scaffold which has appbar + /// + ///Simple Usage: `overlyTopPadding: MediaQuery.of(context).padding.top + kToolbarHeight,` + final double? overlyTopPadding; + + ///Search Bar height, + final double? height; + + ///Icon for SearchBar + final Widget? searchIcon; + + ///Icon for clear text button + final Widget? clearTextIcon; + + ///Decoration to apply to search input + final InputDecoration? inputDecoration; + + ///Decoration for searchbar + final Decoration? decoration; + + const SearchBarOptions({ + this.padding, + this.backgroundColor, + this.elevation, + this.clearTextIcon, + this.overlyTopPadding, + this.searchIcon, + this.inputDecoration, + this.searchIconPadding, + this.decoration, + this.height, + this.clearTextIconPadding, + }); +} diff --git a/lib/place_picker.dart b/lib/place_picker.dart index c73ab89..22f83d1 100644 --- a/lib/place_picker.dart +++ b/lib/place_picker.dart @@ -1,3 +1,4 @@ export 'widgets/widgets.dart'; export 'entities/entities.dart'; export 'package:google_maps_flutter/google_maps_flutter.dart' show LatLng; +export 'entities/search_bar_options_model.dart'; diff --git a/lib/widgets/place_picker.dart b/lib/widgets/place_picker.dart index 6b31169..d9e500c 100644 --- a/lib/widgets/place_picker.dart +++ b/lib/widgets/place_picker.dart @@ -9,6 +9,7 @@ import 'package:place_picker/entities/entities.dart'; import 'package:place_picker/entities/localization_item.dart'; import 'package:place_picker/widgets/widgets.dart'; +import '../place_picker.dart'; import '../uuid.dart'; /// Place picker widget made with map widget from @@ -27,7 +28,14 @@ class PlacePicker extends StatefulWidget { final LatLng? displayLocation; LocalizationItem? localizationItem; - PlacePicker(this.apiKey, {this.displayLocation, this.localizationItem}) { + ///SeachBar customization + SearchBarOptions? searchBarOptions; + PlacePicker( + this.apiKey, { + this.displayLocation, + this.localizationItem, + this.searchBarOptions, + }) { if (this.localizationItem == null) { this.localizationItem = new LocalizationItem(); } @@ -96,9 +104,22 @@ class PlacePickerState extends State { return Scaffold( appBar: AppBar( key: this.appBarKey, - title: SearchInput(searchPlace), + backgroundColor: widget.searchBarOptions?.backgroundColor, + elevation: widget.searchBarOptions?.elevation, + title: Padding( + padding: widget.searchBarOptions?.padding ?? + EdgeInsets.symmetric(horizontal: 16), + child: SearchInput( + searchPlace, + searchBarOptions: widget.searchBarOptions, + ), + ), centerTitle: true, automaticallyImplyLeading: false, + + //we use this because we now have padding + titleSpacing: 0, + toolbarHeight: widget.searchBarOptions?.height, ), body: Column( children: [ @@ -192,7 +213,8 @@ class PlacePickerState extends State { this.overlayEntry = OverlayEntry( builder: (context) => Positioned( - top: appBarBox!.size.height, + top: appBarBox!.size.height + + (widget.searchBarOptions?.overlyTopPadding ?? 0.0), width: size.width, child: Material( elevation: 1, @@ -324,7 +346,8 @@ class PlacePickerState extends State { this.overlayEntry = OverlayEntry( builder: (context) => Positioned( width: size.width, - top: appBarBox!.size.height, + top: appBarBox!.size.height + + (widget.searchBarOptions?.overlyTopPadding ?? 0.0), child: Material(elevation: 1, child: Column(children: suggestions)), ), ); diff --git a/lib/widgets/search_input.dart b/lib/widgets/search_input.dart index 3847983..e0190d5 100644 --- a/lib/widgets/search_input.dart +++ b/lib/widgets/search_input.dart @@ -1,12 +1,15 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:place_picker/entities/search_bar_options_model.dart'; +import 'package:place_picker/place_picker.dart'; /// Custom Search input field, showing the search and clear icons. class SearchInput extends StatefulWidget { final ValueChanged onSearchInput; + final SearchBarOptions? searchBarOptions; - SearchInput(this.onSearchInput); + SearchInput(this.onSearchInput, {this.searchBarOptions}); @override State createState() => SearchInputState(); @@ -54,14 +57,21 @@ class SearchInputState extends State { @override Widget build(BuildContext context) { return Container( - padding: EdgeInsets.symmetric(horizontal: 8), + // padding: EdgeInsets.symmetric(horizontal: 8), child: Row( children: [ - Icon(Icons.search, color: Theme.of(context).textTheme.body1!.color), - SizedBox(width: 8), + Padding( + padding: widget.searchBarOptions?.searchIconPadding ?? + const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + child: widget.searchBarOptions?.searchIcon ?? + Icon(Icons.search, + color: Theme.of(context).textTheme.body1!.color), + ), Expanded( child: TextField( - decoration: InputDecoration(hintText: "Search place", border: InputBorder.none), + decoration: widget.searchBarOptions?.inputDecoration ?? + InputDecoration( + hintText: "Search place", border: InputBorder.none), controller: this.editController, onChanged: (value) { setState(() { @@ -70,23 +80,30 @@ class SearchInputState extends State { }, ), ), - SizedBox(width: 8), if (this.hasSearchEntry) - GestureDetector( - child: Icon(Icons.clear), - onTap: () { - this.editController.clear(); - setState(() { - this.hasSearchEntry = false; - }); - }, + Padding( + padding: widget.searchBarOptions?.clearTextIconPadding ?? + EdgeInsets.zero, + child: GestureDetector( + onTap: () { + this.editController.clear(); + setState(() { + this.hasSearchEntry = false; + }); + }, + child: widget.searchBarOptions?.clearTextIcon ?? + Icon( + Icons.clear, + ), + ), ), ], ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), - color: Theme.of(context).canvasColor, - ), + decoration: widget.searchBarOptions?.decoration ?? + BoxDecoration( + borderRadius: BorderRadius.circular(16), + color: Theme.of(context).canvasColor, + ), ); } } From c3c5217050dd33606795b06676caec720f183982 Mon Sep 17 00:00:00 2001 From: mozaffari Date: Tue, 15 Jun 2021 11:17:26 +0430 Subject: [PATCH 03/11] Fix: set serchBaroptions final --- lib/widgets/place_picker.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/widgets/place_picker.dart b/lib/widgets/place_picker.dart index d9e500c..70c575f 100644 --- a/lib/widgets/place_picker.dart +++ b/lib/widgets/place_picker.dart @@ -29,7 +29,7 @@ class PlacePicker extends StatefulWidget { LocalizationItem? localizationItem; ///SeachBar customization - SearchBarOptions? searchBarOptions; + final SearchBarOptions? searchBarOptions; PlacePicker( this.apiKey, { this.displayLocation, From 6856e3fe1216419c47ffb86a924b66876ad15f00 Mon Sep 17 00:00:00 2001 From: mozaffari Date: Tue, 15 Jun 2021 12:52:56 +0430 Subject: [PATCH 04/11] Add: Custom Widget as loading while search place --- lib/widgets/place_picker.dart | 43 ++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/lib/widgets/place_picker.dart b/lib/widgets/place_picker.dart index 70c575f..1b1630d 100644 --- a/lib/widgets/place_picker.dart +++ b/lib/widgets/place_picker.dart @@ -30,11 +30,16 @@ class PlacePicker extends StatefulWidget { ///SeachBar customization final SearchBarOptions? searchBarOptions; + + ///This is used to create your own widget while as loading of autocomplete of search + /// + final Widget Function()? searchAutoCompletLoadingBuilder; PlacePicker( this.apiKey, { this.displayLocation, this.localizationItem, this.searchBarOptions, + this.searchAutoCompletLoadingBuilder, }) { if (this.localizationItem == null) { this.localizationItem = new LocalizationItem(); @@ -216,24 +221,26 @@ class PlacePickerState extends State { top: appBarBox!.size.height + (widget.searchBarOptions?.overlyTopPadding ?? 0.0), width: size.width, - child: Material( - elevation: 1, - child: Container( - padding: EdgeInsets.symmetric(vertical: 16, horizontal: 24), - child: Row( - children: [ - SizedBox( - height: 24, - width: 24, - child: CircularProgressIndicator(strokeWidth: 3)), - SizedBox(width: 24), - Expanded( - child: Text(widget.localizationItem!.findingPlace, - style: TextStyle(fontSize: 16))) - ], - ), - ), - ), + child: widget.searchAutoCompletLoadingBuilder != null + ? widget.searchAutoCompletLoadingBuilder!() + : Material( + elevation: 1, + child: Container( + padding: EdgeInsets.symmetric(vertical: 16, horizontal: 24), + child: Row( + children: [ + SizedBox( + height: 24, + width: 24, + child: CircularProgressIndicator(strokeWidth: 3)), + SizedBox(width: 24), + Expanded( + child: Text(widget.localizationItem!.findingPlace, + style: TextStyle(fontSize: 16))) + ], + ), + ), + ), ), ); From 5b8578a1e1cf1390071830bb7820afe7f22b0759 Mon Sep 17 00:00:00 2001 From: mozaffari Date: Tue, 15 Jun 2021 14:54:33 +0430 Subject: [PATCH 05/11] Add: ability to use custom widget as result --- lib/widgets/place_picker.dart | 123 ++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 43 deletions(-) diff --git a/lib/widgets/place_picker.dart b/lib/widgets/place_picker.dart index 1b1630d..36c8a2e 100644 --- a/lib/widgets/place_picker.dart +++ b/lib/widgets/place_picker.dart @@ -34,12 +34,27 @@ class PlacePicker extends StatefulWidget { ///This is used to create your own widget while as loading of autocomplete of search /// final Widget Function()? searchAutoCompletLoadingBuilder; + + ///This is used to create your own Widget to show at the buttom of Map + /// + ///Paramters: [locationName, locationResult, nearbyPlaces, maxHeight, selectPlace] + /// + ///__maxHeight includes the height of Map as well. be carefull__ + final Widget Function( + String locationName, + LocationResult? locationResult, + List nearbyPlaces, + double maxHeight, + VoidCallback selectPlace, + )? bottomResultWidgetBuilder; + PlacePicker( this.apiKey, { this.displayLocation, this.localizationItem, this.searchBarOptions, this.searchAutoCompletLoadingBuilder, + this.bottomResultWidgetBuilder, }) { if (this.localizationItem == null) { this.localizationItem = new LocalizationItem(); @@ -126,51 +141,73 @@ class PlacePickerState extends State { titleSpacing: 0, toolbarHeight: widget.searchBarOptions?.height, ), - body: Column( - children: [ - Expanded( - child: GoogleMap( - initialCameraPosition: CameraPosition( - target: widget.displayLocation ?? LatLng(5.6037, 0.1870), - zoom: 15, - ), - myLocationButtonEnabled: true, - myLocationEnabled: true, - onMapCreated: onMapCreated, - onTap: (latLng) { - clearOverlay(); - moveToLocation(latLng); - }, - markers: markers, - ), - ), - if (!this.hasSearchTerm) - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SelectPlaceAction( - getLocationName(), - () => Navigator.of(context).pop(this.locationResult), - widget.localizationItem!.tapToSelectLocation), - Divider(height: 8), - Padding( - child: Text(widget.localizationItem!.nearBy, - style: TextStyle(fontSize: 16)), - padding: EdgeInsets.symmetric(horizontal: 24, vertical: 8), - ), - Expanded( - child: ListView( - children: nearbyPlaces - .map((it) => NearbyPlaceItem( - it, () => moveToLocation(it.latLng!))) - .toList(), - ), + body: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Column( + children: [ + Expanded( + child: GoogleMap( + initialCameraPosition: CameraPosition( + target: widget.displayLocation ?? LatLng(5.6037, 0.1870), + zoom: 15, ), - ], + myLocationButtonEnabled: true, + myLocationEnabled: true, + onMapCreated: onMapCreated, + onTap: (latLng) { + clearOverlay(); + moveToLocation(latLng); + }, + markers: markers, + ), ), - ), - ], + if (!this.hasSearchTerm) + SizedBox( + //user can set height of the bottom result from [maxHeight] of [widget.bottomResultWidgetBilder] + height: widget.bottomResultWidgetBuilder != null + ? null + : constraints.maxHeight / 2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [] + ..addAll(widget.bottomResultWidgetBuilder != null + ? [ + widget.bottomResultWidgetBuilder!( + getLocationName(), + this.locationResult, + nearbyPlaces, + constraints.maxHeight, + () => Navigator.of(context) + .pop(this.locationResult), + ) + ] + : [ + SelectPlaceAction( + getLocationName(), + () => Navigator.of(context) + .pop(this.locationResult), + widget + .localizationItem!.tapToSelectLocation), + Divider(height: 8), + Padding( + child: Text(widget.localizationItem!.nearBy, + style: TextStyle(fontSize: 16)), + padding: EdgeInsets.symmetric( + horizontal: 24, vertical: 8), + ), + Expanded( + child: ListView( + children: nearbyPlaces + .map((it) => NearbyPlaceItem(it, + () => moveToLocation(it.latLng!))) + .toList(), + ), + ), + ])), + ), + ], + ); + }, ), ); } From 7aa9f571a943fc91c47be18a9194f8a659607968 Mon Sep 17 00:00:00 2001 From: mozaffari Date: Fri, 18 Jun 2021 12:30:30 +0430 Subject: [PATCH 06/11] Add: ability to custimize autocomplete of search --- lib/widgets/place_picker.dart | 40 ++++++++++++++++++++++++----- lib/widgets/rich_suggestion.dart | 44 ++++++++++++++++++++------------ 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/lib/widgets/place_picker.dart b/lib/widgets/place_picker.dart index 36c8a2e..e187103 100644 --- a/lib/widgets/place_picker.dart +++ b/lib/widgets/place_picker.dart @@ -48,6 +48,22 @@ class PlacePicker extends StatefulWidget { VoidCallback selectPlace, )? bottomResultWidgetBuilder; + ///This is used to create your own layout for each item in auto complete while searching a place. + /// + final Widget Function( + AutoCompleteItem autoCompleteItem, + VoidCallback onTap, + )? searchAutoCompleteItemBuilder; + + ///This is used to create your own layout for autocomplete suggestion. + ///each item in + ///`List` + /// can be built using [searchAutoCompleteBuilder] + ///default is `Material(elevation: 1, child: Column(children: suggestions))`, + final Widget Function( + List itemList, + )? searchAutoCompleteBuilder; + PlacePicker( this.apiKey, { this.displayLocation, @@ -55,6 +71,8 @@ class PlacePicker extends StatefulWidget { this.searchBarOptions, this.searchAutoCompletLoadingBuilder, this.bottomResultWidgetBuilder, + this.searchAutoCompleteItemBuilder, + this.searchAutoCompleteBuilder, }) { if (this.localizationItem == null) { this.localizationItem = new LocalizationItem(); @@ -324,7 +342,11 @@ class PlacePickerState extends State { aci.offset = 0; aci.length = 0; - suggestions.add(RichSuggestion(aci, () {})); + suggestions.add(RichSuggestion( + aci, + () {}, + autoCompleteItemBuilder: null, + )); } else { for (dynamic t in predictions) { final aci = AutoCompleteItem() @@ -333,10 +355,14 @@ class PlacePickerState extends State { ..offset = t['matched_substrings'][0]['offset'] ..length = t['matched_substrings'][0]['length']; - suggestions.add(RichSuggestion(aci, () { - FocusScope.of(context).requestFocus(FocusNode()); - decodeAndSelectPlace(aci.id); - })); + suggestions.add(RichSuggestion( + aci, + () { + FocusScope.of(context).requestFocus(FocusNode()); + decodeAndSelectPlace(aci.id); + }, + autoCompleteItemBuilder: widget.searchAutoCompleteItemBuilder, + )); } } @@ -392,7 +418,9 @@ class PlacePickerState extends State { width: size.width, top: appBarBox!.size.height + (widget.searchBarOptions?.overlyTopPadding ?? 0.0), - child: Material(elevation: 1, child: Column(children: suggestions)), + child: widget.searchAutoCompleteBuilder != null + ? widget.searchAutoCompleteBuilder!(suggestions) + : Material(elevation: 1, child: Column(children: suggestions)), ), ); diff --git a/lib/widgets/rich_suggestion.dart b/lib/widgets/rich_suggestion.dart index 3b27c75..50d6920 100644 --- a/lib/widgets/rich_suggestion.dart +++ b/lib/widgets/rich_suggestion.dart @@ -4,38 +4,50 @@ import 'package:place_picker/entities/entities.dart'; class RichSuggestion extends StatelessWidget { final VoidCallback onTap; final AutoCompleteItem autoCompleteItem; - - RichSuggestion(this.autoCompleteItem, this.onTap); + final Widget Function( + AutoCompleteItem autoCompleteItem, + VoidCallback onTap, + )? autoCompleteItemBuilder; + RichSuggestion(this.autoCompleteItem, this.onTap, + {this.autoCompleteItemBuilder}); @override Widget build(BuildContext context) { - return Material( - child: InkWell( - child: Container( - padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16), - child: RichText(text: TextSpan(children: getStyledTexts(context))), - ), - onTap: onTap, - ), - ); + return autoCompleteItemBuilder != null + ? autoCompleteItemBuilder!(autoCompleteItem, onTap) + : Material( + child: InkWell( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16), + child: + RichText(text: TextSpan(children: getStyledTexts(context))), + ), + onTap: onTap, + ), + ); } List getStyledTexts(BuildContext context) { final List result = []; final style = TextStyle(color: Colors.grey, fontSize: 15); - final startText = autoCompleteItem.text!.substring(0, autoCompleteItem.offset); + final startText = + autoCompleteItem.text!.substring(0, autoCompleteItem.offset); if (startText.isNotEmpty) { result.add(TextSpan(text: startText, style: style)); } - final boldText = - autoCompleteItem.text!.substring(autoCompleteItem.offset!, autoCompleteItem.offset! + autoCompleteItem.length!); + final boldText = autoCompleteItem.text!.substring(autoCompleteItem.offset!, + autoCompleteItem.offset! + autoCompleteItem.length!); result.add( - TextSpan(text: boldText, style: style.copyWith(color: Theme.of(context).textTheme.body1!.color)), + TextSpan( + text: boldText, + style: + style.copyWith(color: Theme.of(context).textTheme.body1!.color)), ); - final remainingText = autoCompleteItem.text!.substring(autoCompleteItem.offset! + autoCompleteItem.length!); + final remainingText = autoCompleteItem.text! + .substring(autoCompleteItem.offset! + autoCompleteItem.length!); result.add(TextSpan(text: remainingText, style: style)); return result; From 7248927061b66a51b44e0a9cd7238f09224b40ec Mon Sep 17 00:00:00 2001 From: mozaffari Date: Fri, 18 Jun 2021 16:30:23 +0430 Subject: [PATCH 07/11] Add: ability to cutomized most of map options --- lib/entities/map_options.dart | 29 ++++++++++++++++++++++ lib/place_picker.dart | 1 + lib/widgets/place_picker.dart | 45 ++++++++++++++++++++++++++++------- 3 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 lib/entities/map_options.dart diff --git a/lib/entities/map_options.dart b/lib/entities/map_options.dart new file mode 100644 index 0000000..1ac68ff --- /dev/null +++ b/lib/entities/map_options.dart @@ -0,0 +1,29 @@ +import 'package:google_maps_flutter/google_maps_flutter.dart'; + +class MapOptions { + ///this will override the displyLocation of google maps. + final CameraPosition? initialCameraPosition; + final bool? compassEnabled; + final bool? mapToolbarEnabled; + final bool? scrollGestureEnabled; + final bool? myLocationEnabled; + final bool? myLocationButtonEnabled; + final bool? zoomControllEnabled; + final bool? zoomGestureEnabled; + final bool? rotateGestureEnabled; + final BitmapDescriptor? markerIcon; + final MapType? mapType; + MapOptions({ + this.initialCameraPosition, + this.compassEnabled, + this.mapToolbarEnabled, + this.scrollGestureEnabled, + this.myLocationEnabled, + this.myLocationButtonEnabled, + this.zoomControllEnabled, + this.markerIcon, + this.mapType, + this.rotateGestureEnabled, + this.zoomGestureEnabled, + }); +} diff --git a/lib/place_picker.dart b/lib/place_picker.dart index 22f83d1..34938f0 100644 --- a/lib/place_picker.dart +++ b/lib/place_picker.dart @@ -2,3 +2,4 @@ export 'widgets/widgets.dart'; export 'entities/entities.dart'; export 'package:google_maps_flutter/google_maps_flutter.dart' show LatLng; export 'entities/search_bar_options_model.dart'; +export 'entities/map_options.dart'; diff --git a/lib/widgets/place_picker.dart b/lib/widgets/place_picker.dart index e187103..978fa70 100644 --- a/lib/widgets/place_picker.dart +++ b/lib/widgets/place_picker.dart @@ -7,6 +7,7 @@ import 'package:location/location.dart'; import 'package:http/http.dart' as http; import 'package:place_picker/entities/entities.dart'; import 'package:place_picker/entities/localization_item.dart'; +import 'package:place_picker/entities/map_options.dart'; import 'package:place_picker/widgets/widgets.dart'; import '../place_picker.dart'; @@ -64,6 +65,8 @@ class PlacePicker extends StatefulWidget { List itemList, )? searchAutoCompleteBuilder; + MapOptions? mapOptions; + PlacePicker( this.apiKey, { this.displayLocation, @@ -73,6 +76,7 @@ class PlacePicker extends StatefulWidget { this.bottomResultWidgetBuilder, this.searchAutoCompleteItemBuilder, this.searchAutoCompleteBuilder, + this.mapOptions, }) { if (this.localizationItem == null) { this.localizationItem = new LocalizationItem(); @@ -126,8 +130,11 @@ class PlacePickerState extends State { void initState() { super.initState(); markers.add(Marker( - position: widget.displayLocation ?? LatLng(5.6037, 0.1870), + position: widget.mapOptions?.initialCameraPosition?.target ?? + widget.displayLocation ?? + LatLng(5.6037, 0.1870), markerId: MarkerId("selected-location"), + icon: widget.mapOptions?.markerIcon ?? BitmapDescriptor.defaultMarker, )); } @@ -165,13 +172,30 @@ class PlacePickerState extends State { children: [ Expanded( child: GoogleMap( - initialCameraPosition: CameraPosition( - target: widget.displayLocation ?? LatLng(5.6037, 0.1870), - zoom: 15, - ), - myLocationButtonEnabled: true, - myLocationEnabled: true, + initialCameraPosition: + widget.mapOptions?.initialCameraPosition ?? + CameraPosition( + target: widget.displayLocation ?? + LatLng(5.6037, 0.1870), + zoom: 15, + ), + myLocationButtonEnabled: + widget.mapOptions?.myLocationButtonEnabled ?? true, + myLocationEnabled: + widget.mapOptions?.myLocationButtonEnabled ?? true, onMapCreated: onMapCreated, + compassEnabled: widget.mapOptions?.compassEnabled ?? true, + mapToolbarEnabled: + widget.mapOptions?.mapToolbarEnabled ?? true, + scrollGesturesEnabled: + widget.mapOptions?.scrollGestureEnabled ?? true, + zoomControlsEnabled: + widget.mapOptions?.zoomControllEnabled ?? true, + zoomGesturesEnabled: + widget.mapOptions?.zoomGestureEnabled ?? true, + rotateGesturesEnabled: + widget.mapOptions?.rotateGestureEnabled ?? true, + mapType: widget.mapOptions?.mapType ?? MapType.normal, onTap: (latLng) { clearOverlay(); moveToLocation(latLng); @@ -453,8 +477,11 @@ class PlacePickerState extends State { // markers.clear(); setState(() { markers.clear(); - markers.add( - Marker(markerId: MarkerId("selected-location"), position: latLng)); + markers.add(Marker( + markerId: MarkerId("selected-location"), + position: widget.mapOptions?.initialCameraPosition?.target ?? latLng, + icon: + widget.mapOptions?.markerIcon ?? BitmapDescriptor.defaultMarker)); }); } From e7e6632c5c4de84eeed225df68be0810a40ff9af Mon Sep 17 00:00:00 2001 From: mozaffari Date: Fri, 18 Jun 2021 16:38:08 +0430 Subject: [PATCH 08/11] Fix: Camera poistion not exported Update: Simple Usage Documentation --- README.md | 70 +++++++++++++++++++++++++++++++++++++++---- lib/place_picker.dart | 1 + 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ae1b8f5..44af972 100644 --- a/README.md +++ b/README.md @@ -127,11 +127,69 @@ to display the previously selected location. void showPlacePicker() async { LocationResult result = await Navigator.of(context).push(MaterialPageRoute( builder: (context) => - PlacePicker("YOUR API KEY", - displayLocation: customLocation, - ))); - - // Handle the result in your way - print(result); + PlacePicker( + "YOUR_API_KEY", + displayLocation: LatLng(33.2, 66.4), + localizationItem: LocalizationItem(languageCode: "fa_IR"), + searchBarOptions: SearchBarOptions( + padding: EdgeInsets.zero, + backgroundColor: Colors.white, + elevation: 2, + overlyTopPadding: + MediaQuery.of(context).padding.top + kToolbarHeight, + searchIcon: Icon( + Feather.search, + color: Get.theme.accentColor, + ), + clearTextIcon: Icon( + AntDesign.close, + color: Get.theme.accentColor, + ), + inputDecoration: InputDecoration( + hintText: "search_place".tr, + border: InputBorder.none, + hintStyle: TextStyle(), + ), + // height: 50, + searchIconPadding: + EdgeInsets.symmetric(horizontal: 16, vertical: 4), + clearTextIconPadding: + EdgeInsets.symmetric(horizontal: 16, vertical: 4), + decoration: BoxDecoration( + // color: Colors.white, + ), + ), + searchAutoCompletLoadingBuilder: () => Material( + child: SizedBox( + height: 50, + width: 50, + child: Center(child: CircularProgressIndicator()), + ), + ), + bottomResultWidgetBuilder: (_, __, ___, h, _____) => SizedBox( + height: h / 4, + ), + searchAutoCompleteItemBuilder: (a, b) => GestureDetector( + onTap: b, + child: Text( + a.text, + ), + ), + searchAutoCompleteBuilder: (list) => Material( + elevation: 1, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: list, + ), + ), + mapOptions: MapOptions( + myLocationButtonEnabled: true, + ), + ), + ), + ); + + // Handle the result in your way + print(result); } ``` diff --git a/lib/place_picker.dart b/lib/place_picker.dart index 34938f0..59a8e0c 100644 --- a/lib/place_picker.dart +++ b/lib/place_picker.dart @@ -3,3 +3,4 @@ export 'entities/entities.dart'; export 'package:google_maps_flutter/google_maps_flutter.dart' show LatLng; export 'entities/search_bar_options_model.dart'; export 'entities/map_options.dart'; +export 'package:google_maps_flutter/google_maps_flutter.dart'; From 214a8e3cd6d970110e7760c7a10a55d77f21b89c Mon Sep 17 00:00:00 2001 From: mozaffari Date: Fri, 18 Jun 2021 16:42:10 +0430 Subject: [PATCH 09/11] Fix: redmi.md to look better --- README.md | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 44af972..7898cd0 100644 --- a/README.md +++ b/README.md @@ -139,14 +139,14 @@ void showPlacePicker() async { MediaQuery.of(context).padding.top + kToolbarHeight, searchIcon: Icon( Feather.search, - color: Get.theme.accentColor, + color: Colors.red, ), clearTextIcon: Icon( AntDesign.close, - color: Get.theme.accentColor, + color: Colors.red, ), inputDecoration: InputDecoration( - hintText: "search_place".tr, + hintText: "Search Place", border: InputBorder.none, hintStyle: TextStyle(), ), @@ -159,29 +159,29 @@ void showPlacePicker() async { // color: Colors.white, ), ), - searchAutoCompletLoadingBuilder: () => Material( - child: SizedBox( - height: 50, - width: 50, - child: Center(child: CircularProgressIndicator()), - ), - ), - bottomResultWidgetBuilder: (_, __, ___, h, _____) => SizedBox( - height: h / 4, - ), - searchAutoCompleteItemBuilder: (a, b) => GestureDetector( - onTap: b, - child: Text( - a.text, - ), - ), - searchAutoCompleteBuilder: (list) => Material( - elevation: 1, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: list, - ), - ), + //searchAutoCompletLoadingBuilder: () => Material( + // child: SizedBox( + // height: 50, + // width: 50, + // child: Center(child: CircularProgressIndicator()), + // ), + //), + //bottomResultWidgetBuilder: (_, __, ___, h, _____) => SizedBox( + // height: h / 4, + //), + //searchAutoCompleteItemBuilder: (a, b) => GestureDetector( + // onTap: b, + //child: Text( + // a.text, + // ), + //), + //searchAutoCompleteBuilder: (list) => Material( + // elevation: 1, + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: list, + // ), + //), mapOptions: MapOptions( myLocationButtonEnabled: true, ), From e5f4a1a207318fde910be4814fdcab6023446cae Mon Sep 17 00:00:00 2001 From: mozaffari Date: Thu, 24 Jun 2021 08:50:50 +0430 Subject: [PATCH 10/11] Fix: Replace Native My Location with flutter one Resson: native one on tap is not controllable --- lib/widgets/place_picker.dart | 218 +++++++++++++++++++++------------- 1 file changed, 136 insertions(+), 82 deletions(-) diff --git a/lib/widgets/place_picker.dart b/lib/widgets/place_picker.dart index 978fa70..a29389c 100644 --- a/lib/widgets/place_picker.dart +++ b/lib/widgets/place_picker.dart @@ -65,8 +65,12 @@ class PlacePicker extends StatefulWidget { List itemList, )? searchAutoCompleteBuilder; + ///More map options to change MapOptions? mapOptions; + //custom Icon for mylocation button + Widget? myLocationIcon; + PlacePicker( this.apiKey, { this.displayLocation, @@ -77,6 +81,7 @@ class PlacePicker extends StatefulWidget { this.searchAutoCompleteItemBuilder, this.searchAutoCompleteBuilder, this.mapOptions, + this.myLocationIcon, }) { if (this.localizationItem == null) { this.localizationItem = new LocalizationItem(); @@ -110,6 +115,8 @@ class PlacePickerState extends State { bool hasSearchTerm = false; String previousSearchTerm = ''; + Location _location = Location(); + LocationData? _locationData; // constructor PlacePickerState(); @@ -136,6 +143,11 @@ class PlacePickerState extends State { markerId: MarkerId("selected-location"), icon: widget.mapOptions?.markerIcon ?? BitmapDescriptor.defaultMarker, )); + _location.getLocation().then((value) { + setState(() { + _locationData = value; + }); + }); } @override @@ -166,90 +178,132 @@ class PlacePickerState extends State { titleSpacing: 0, toolbarHeight: widget.searchBarOptions?.height, ), - body: LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - return Column( - children: [ - Expanded( - child: GoogleMap( - initialCameraPosition: - widget.mapOptions?.initialCameraPosition ?? - CameraPosition( - target: widget.displayLocation ?? - LatLng(5.6037, 0.1870), - zoom: 15, + body: Stack( + alignment: Alignment.center, + children: [ + LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Column( + children: [ + Expanded( + child: GoogleMap( + initialCameraPosition: + widget.mapOptions?.initialCameraPosition ?? + CameraPosition( + target: widget.displayLocation ?? + LatLng(5.6037, 0.1870), + zoom: 15, + ), + myLocationButtonEnabled: false, + myLocationEnabled: false, + onMapCreated: onMapCreated, + compassEnabled: widget.mapOptions?.compassEnabled ?? true, + mapToolbarEnabled: + widget.mapOptions?.mapToolbarEnabled ?? true, + scrollGesturesEnabled: + widget.mapOptions?.scrollGestureEnabled ?? true, + zoomControlsEnabled: + widget.mapOptions?.zoomControllEnabled ?? true, + zoomGesturesEnabled: + widget.mapOptions?.zoomGestureEnabled ?? true, + rotateGesturesEnabled: + widget.mapOptions?.rotateGestureEnabled ?? true, + mapType: widget.mapOptions?.mapType ?? MapType.normal, + onTap: (latLng) { + clearOverlay(); + moveToLocation(latLng); + }, + markers: markers, + ), + ), + if (!this.hasSearchTerm) + SizedBox( + //user can set height of the bottom result from [maxHeight] of [widget.bottomResultWidgetBilder] + height: widget.bottomResultWidgetBuilder != null + ? null + : constraints.maxHeight / 2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [] + ..addAll(widget.bottomResultWidgetBuilder != null + ? [ + widget.bottomResultWidgetBuilder!( + getLocationName(), + this.locationResult, + nearbyPlaces, + constraints.maxHeight, + () => Navigator.of(context) + .pop(this.locationResult), + ) + ] + : [ + SelectPlaceAction( + getLocationName(), + () => Navigator.of(context) + .pop(this.locationResult), + widget.localizationItem! + .tapToSelectLocation), + Divider(height: 8), + Padding( + child: Text( + widget.localizationItem!.nearBy, + style: TextStyle(fontSize: 16)), + padding: EdgeInsets.symmetric( + horizontal: 24, vertical: 8), + ), + Expanded( + child: ListView( + children: nearbyPlaces + .map((it) => NearbyPlaceItem( + it, + () => + moveToLocation(it.latLng!))) + .toList(), + ), + ), + ])), + ), + ], + ); + }, + ), + () { + if (widget.mapOptions?.myLocationButtonEnabled != null && + widget.mapOptions?.myLocationButtonEnabled == true) { + print( + "User Lat,Lon: ${_locationData?.latitude}, ${_locationData?.longitude}"); + return Positioned.directional( + top: 20, + start: 10, + textDirection: Directionality.of(context), + child: Material( + elevation: 2, + child: InkWell( + onTap: _locationData != null + ? () { + // setState(() { + // _locationData = null; + // }); + clearOverlay(); + moveToLocation(LatLng(_locationData!.latitude!, + _locationData!.longitude!)); + } + : null, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: widget.myLocationIcon ?? + Icon( + Icons.my_location_sharp, + size: 22, ), - myLocationButtonEnabled: - widget.mapOptions?.myLocationButtonEnabled ?? true, - myLocationEnabled: - widget.mapOptions?.myLocationButtonEnabled ?? true, - onMapCreated: onMapCreated, - compassEnabled: widget.mapOptions?.compassEnabled ?? true, - mapToolbarEnabled: - widget.mapOptions?.mapToolbarEnabled ?? true, - scrollGesturesEnabled: - widget.mapOptions?.scrollGestureEnabled ?? true, - zoomControlsEnabled: - widget.mapOptions?.zoomControllEnabled ?? true, - zoomGesturesEnabled: - widget.mapOptions?.zoomGestureEnabled ?? true, - rotateGesturesEnabled: - widget.mapOptions?.rotateGestureEnabled ?? true, - mapType: widget.mapOptions?.mapType ?? MapType.normal, - onTap: (latLng) { - clearOverlay(); - moveToLocation(latLng); - }, - markers: markers, - ), - ), - if (!this.hasSearchTerm) - SizedBox( - //user can set height of the bottom result from [maxHeight] of [widget.bottomResultWidgetBilder] - height: widget.bottomResultWidgetBuilder != null - ? null - : constraints.maxHeight / 2, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [] - ..addAll(widget.bottomResultWidgetBuilder != null - ? [ - widget.bottomResultWidgetBuilder!( - getLocationName(), - this.locationResult, - nearbyPlaces, - constraints.maxHeight, - () => Navigator.of(context) - .pop(this.locationResult), - ) - ] - : [ - SelectPlaceAction( - getLocationName(), - () => Navigator.of(context) - .pop(this.locationResult), - widget - .localizationItem!.tapToSelectLocation), - Divider(height: 8), - Padding( - child: Text(widget.localizationItem!.nearBy, - style: TextStyle(fontSize: 16)), - padding: EdgeInsets.symmetric( - horizontal: 24, vertical: 8), - ), - Expanded( - child: ListView( - children: nearbyPlaces - .map((it) => NearbyPlaceItem(it, - () => moveToLocation(it.latLng!))) - .toList(), - ), - ), - ])), + ), + ), ), - ], - ); - }, + ); + } + return SizedBox(); + }(), + ], ), ); } From c395a0f7935983c5cbca36b8d2fdc203e938b510 Mon Sep 17 00:00:00 2001 From: mozaffari Date: Wed, 22 Sep 2021 22:42:59 +0430 Subject: [PATCH 11/11] Flutter 2.5 Fixes --- lib/widgets/rich_suggestion.dart | 4 ++-- lib/widgets/search_input.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/widgets/rich_suggestion.dart b/lib/widgets/rich_suggestion.dart index 50d6920..012e59e 100644 --- a/lib/widgets/rich_suggestion.dart +++ b/lib/widgets/rich_suggestion.dart @@ -42,8 +42,8 @@ class RichSuggestion extends StatelessWidget { result.add( TextSpan( text: boldText, - style: - style.copyWith(color: Theme.of(context).textTheme.body1!.color)), + style: style.copyWith( + color: Theme.of(context).textTheme.bodyText1!.color)), ); final remainingText = autoCompleteItem.text! diff --git a/lib/widgets/search_input.dart b/lib/widgets/search_input.dart index e0190d5..7ccbe27 100644 --- a/lib/widgets/search_input.dart +++ b/lib/widgets/search_input.dart @@ -65,7 +65,7 @@ class SearchInputState extends State { const EdgeInsets.symmetric(horizontal: 8, vertical: 2), child: widget.searchBarOptions?.searchIcon ?? Icon(Icons.search, - color: Theme.of(context).textTheme.body1!.color), + color: Theme.of(context).textTheme.bodyText1!.color), ), Expanded( child: TextField(