I am trying to develop a user experience where the user is provided a list of venues to travel to, and upon selecting one of those locations, I want it to
Add a marker to the map
Show an info window
Provide the option to navigate to the
location.
I was able to use the information from this PR (that has no official documentation it seems) to go from this:
To this:
However, showing the marker info still does not make the given marker "active". When I select the marker, the functionality of the plugin actually displays options for navigating to the given location on the bottom right, like this:
What I would like is for the selection of the venue at the bottom of the list to automatically display all three criteria in the third image with one press. That is, once again:
The marker of the location
The info window
The option to navigate to that location.
Right now, a user has to click the entry from the list at the bottom, and then rather un-intuitively click the marker on the map in order for the navigation options to appear.
How would I go about programatically launching the "on-tap" event that happens when the user "taps" the marker when selecting an item from the list?
Here is my current code for changing the viewport, adding a marker, and showing the info window:
Future<void> goSomewhere(id, name, distance, address, lat, lng) async {
final GoogleMapController controller = await _controller.future;
controller.animateCamera(CameraUpdate.newCameraPosition(
CameraPosition(target: LatLng(lat, lng), zoom: 14.0)));
setState(() {
_markers.add(Marker(
markerId: MarkerId(id),
position: LatLng(lat, lng),
infoWindow: InfoWindow(
title: name,
snippet: address,
),
onTap: () {
//_pageController.jumpToPage(i);
},
icon: BitmapDescriptor.defaultMarker,
));
print(MarkerId(id));
controller.showMarkerInfoWindow(MarkerId(id));
});
}
Right now, my solution does add the marker and show the info window, but comes up short giving the user options for navigating to the location unless the user clicks the marker. In its current state, I've built something that makes for a very sub-optimal user experience.
I've tried alternative solutions like highlighting the marker, but it does not quite have the functionality I'm looking for. I do not understand what event occurs within the plugin that "selects" the marker on-tap, and all attempts I've found that "programmatically" select the marker do some version of highlighting it. Simply some direction as to where to look to find out what event is being launched when the marker is clicked would be helpful.
I didn't come to a complete answer, but I wanted to share my experience in case anyone else runs into this problem and feels stuck. By going through the Flutter Github / asking around the forums, I was able to get three key take-aways:
As far as I know, there is no way to simulate the "tap" of the marker in Flutter. I tried looking at the Kotlin/Swift APIs for platform channels to write some of this functionality myself, but to be honest it was pretty far out of my wheelhouse and I burned a lot of time on it. This also might result in having to go through the approval process with the nice people at Google for my changes to be accepted, but it's not time that I personally have right now.
I found a package called Maps Launcher that was also mentioned by Craig in the comments section that has the ability to launch the Maps application by two methods - Launch by Query, and Launch by Latitude/Longitude. This functionality is effectively identical to the functionality you get when you tap the marker and then select one of the two maps options on the bottom right.
I discovered that the primary difference between the two
icons that you get when you tap the marker is that one launches
directions by query, and the other by latitude and longitude - just
like the Maps Launcher plugin.
So with these three pieces of information, I redesigned the application to go from this:
To look like this:
Now a user:
Selects a location from the list.
Selecting a new location adds a marker and shows the location's infowindow
Can hit the button on the right to navigate to the given location.
In my opinion, this experience is much more clean - there is no ambiguity between the two maps options you can launch (by query vs by lat/long) when selecting a marker, as a button that launches the maps icon is now on the Listview. This also allows you to navigate to each marker pretty easily just by clicking it in the listview, and then deciding whether or not you want navigation directions by pressing the button on the right.
The code for adding this to your app is pretty simple:
new Flexible(
child: ListView.builder(
itemCount: venues.length,
padding: EdgeInsets.all(4.0),
itemBuilder: (context, index) {
return Card(
color: _selectedIndex != null && _selectedIndex == index
? Colors.greenAccent
: Colors.white,
child: ListTile(
onTap: () {
goSomewhere(
venues[index].venueId,
venues[index].name,
venues[index].distance,
venues[index].formattedAddress,
double.parse(venues[index].latitude),
double.parse(venues[index].longitude)
);
_onSelected(index);
},
title: Text(
venues[index]
.name /*+ ' and ' + venues[index].formattedAddress*/,
style: TextStyle(fontWeight: FontWeight.w500),
),
trailing: Container(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.blue,
),
onPressed: () => MapsLauncher.launchQuery(
venues[index].formattedAddress),
child: Icon(Icons.assistant_navigation),
)),
subtitle: Text(venues[index].formattedAddress),
));
},
))
It's a subtle difference, but I've done some testing and my (small) group of users seem to like this much more as it's much more intuitive. Also, the location they get in their Maps application has a recognizable address regardless of which button they press.
While this is not a "full" answer to the original question, I hope that this might help someone on their Flutter journey to stick to the framework - despite some of these early hurdles.
You beat me to it! I was about to answer this question earlier but when I reloaded the page, I saw you already answered it. Though it appears that you already found the solution to your issue, I thought of posting my answer here still. Please see below:
I couldn't find a way to programmatically display the map toolbar(options for navigating to the given location on the bottom right) as you really need to click a marker to show it. But here's a workaround that I thought would fit your use case:
Disable the map toolbar by setting mapToolbarEnabled: false in GoogleMap() widget. (The map toolbar only works for Android anyway)
Create a custom map toolbar that launches Google Maps and Directions URLs from the app. That toolbar only shows when you select a location from the listview or when you click a marker.
I used the url_launcher package to launch the Google Maps and Directions URLs.
Future<void> _launchUrl(bool isDir, double lat, double lon) async {
String url = 'https://www.google.com/maps/search/?api=1&query=$lat,$lon';
if (isDir) {
url = 'https://www.google.com/maps/dir/?api=1&origin=Googleplex&destination=$lat,$lon';
}
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
Then I used the method above on a custom Map toolbar widget
Widget mapToolBar() {
return Row(
children: [
FloatingActionButton(
child: Icon(Icons.map),
backgroundColor: Colors.blue,
onPressed: () {
_launchUrl(false, {your_lat}, {your_lng})
},
),
FloatingActionButton(
child: Icon(Icons.directions),
backgroundColor: Colors.blue,
onPressed: () {
_launchUrl(true, {your_lat}, {your_lng};
},
),
],
);
}
Then put the custom widget in a Stack widget with the Googlemap() widget
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: <Widget>[
Expanded(
flex: 7,
child: Stack(children: [
GoogleMap(
.
.
.
),
mapToolBar()
]),
),
_pageViewBuilder(context),
]),
);
}
Related
I am pretty new to Flutter and I am trying to build an App based on GoogleMaps. Now I added multiple markers and when you tap them the first time a info window pops up. Then, after tapping the info window, I want to change route to another screen, which then includes the description of the place etc.
However, I am struggling now with making it possible to change route after tapping the infowindow, meaning nothing happens when I tap it.
Is it possible to use the 'OnTap' function on the infowindow? I'm happy for any solutions.
My current code for the marker section:
markers.add(Marker( //add first marker
markerId: MarkerId(showLocation.toString()),
position: showLocation, //position of marker
infoWindow: InfoWindow( //popup info
title: 'Marker Title First ',
snippet: 'My Custom Subtitle',
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
},
),
icon: BitmapDescriptor.defaultMarker, //Icon for Marker
));
I just found a supposedly working solution on this problem (Problem with google_maps_flutter, marker onTap(){} doesn't work), however, I guess due to the implementation of null safety it is not working anymore, since I had to initialize the field 'BuildContext'. I did so using 'late' before it, but it still does not work.
We found the problem, I have given multiple markers the same MarkerId. After I changed it, it worked fine. Sorry guys, case closed!
My project is a todo app and I have these parts:
Meetings
Checks
Alarms
and all of these parts have a details screen, add screen, list screen, edit screen and search screen.
I'm tired of creating a separate screen for each section, so I need a way to implement all these screens using inheritance and less code.
Any idea will be great.
Set Navigator route to screen with an argument for the part type you want to render (e.g. Meetings).
Receive that arg in the new screen, then render specific widgets based on that.
For example, set the Navigator to pass the part:
Navigator.of(context).pushNamed(
DetailsScreen.routeName,
arguments: 'details',
);
Then in the new screen, use the argument to render the rest of the page:
final sourcePage = ModalRoute.of(context)!.settings.arguments as String;
// passed from last page
You can use this var to render different elements in the page:
return Scaffold(
appBar: AppBar(
title: Text('$sourcePage'),
backgroundColor: sourcePage.toLowerCase() == 'details'
? Colors.green
: Colors.black26,
),
);
I am trying to implement list of multiple pincode suggestions in a similar fashion as the filter options shown in above image(Relevance, Open now, Top-rated, Visited, ...).
Now here 7 options are provided in google maps which are fixed but I'll only be able to set pincode suggestions based on users current location & number of suggestion will vary, so a way to dynamically add these items is required.
Here In the Image we can select any number of suggestions & I also want the same for my implementation. Similarly this should be horizontally scrollable single strip.
I am not aware of the technical term for these but I've seen it in many popular apps. Still, I have not been able to find anything similar in android documentation, material UI and stackoverflow. So if it's something popular or standard then please point me into right direction.
What I've tried: Radio Groups/Buttons, Customizable Checkbox, Horizontal Scroll without scroll bar & similar other stuff but no luck.
Thanks a lot in advance for the help!!!
Those are called Chip in Flutter.
Here's how you can achieve a horizontal scrolling strip with check/uncheck behavior (in a StatefulWidget's State class) :
Map<String, bool> filters = {
"Relevance": true,
"Open now": false,
"Top": false,
"Something": true,
"Another thing": false,
"Many filters": false,
};
// In build()
ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: filters.entries.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () => setState(() => filters[filters.keys.elementAt(index)] = !filters.values.elementAt(index)),
child: Chip(
backgroundColor: filters.values.elementAt(index) ? Colors.white : Colors.grey,
label: Text(filters.keys.elementAt(index))),
);
})
You could also extract the filters map in a separate "business logic" class with a state management solution.
I'm currently working over an app similar to good old Tamagotchi with modern twists. Right now I prepared some code and didn't need any character animations but now I'm stuck. I want to make a scaffold with title, line of buttons below it, yet still on top of the screen, second bunch on bottom and create some kind of interactivity inside between those two, with character made from assets, some animations and reactions on touch. To be clear - something like this:
class _AnimationState extends State<Animation> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
"some title here"
),
body:
Column(
children: [
Row(here some buttons)
Column(with animation, height as was left on screen)
Row(another line of buttons)
],
),
);
}
}
I was thinking about flame engine, found even tutorial, but when I'm trying to implement it i receive exception
The method '_mulFromInteger' was called on null. Receiver: null Tried
calling: _mulFromInteger(0)
This is a tutorial I worked on:
https://medium.com/flutter-community/sprite-sheet-animations-in-flutter-1b693630bfb3
And code i tried to implement:
Center(
child:
Flame.util.animationAsWidget(Position(70, 70),
animation.Animation.sequenced('minotaur.png', 5, textureWidth: 50),
),
),
The other idea was to use Sprite Widget but I never used any animations in my previous projects, nor in Flutter, C# or whatever language i had in college.
I would appreciate any advices, tutorials or examples how to handle animation from assets.
That tutorial is quite old, a lot has happened in the world of Flame since then.
I'm guessing you are using version 1.0.0-rc9 of Flame?
Something like this should work with the that version:
final animationData = SpriteAnimationData.sequenced(
amount: 5, stepTime: 1.0, textureSize: Vector2.all(50));
final spriteAnimation = await SpriteAnimation.load(
'minotaur.png' animationData);
...
Center(
child: SpriteAnimationWidget(
animation: spriteAnimation,
playing: true,
anchor: Anchor.center,
),
)
An example of it can be found here.
There is also some short documentation for it here.
my problem:
The suggestionbox doesn't hide, except i choose one suggestion. hideSuggestionsOnKeyboardHide changes absolutely nothing...
I saw that others solved this problem with another package named keyboardvisibility.
But for me this is not a solution, beause i don't want the suggestionbox to disappear as soon as the keyboard disappears, I want the suggestionbox to disappear as soon as I tap somewhere outside the suggestionbox. Hope someone can help.
Here my code if it helps:
TypeAheadField(
hideOnEmpty: true,
hideOnError: true,
suggestionsBoxController: _suggestionsBoxController,
textFieldConfiguration: TextFieldConfiguration(
controller: typeAheadControllerA,
decoration: const InputDecoration(
hintText: 'Artikelnummer',
icon: const Icon(
Icons.business_center,
color: Colors.grey,
))),
suggestionsCallback: (pattern) {
return autoA
.where((autoA) =>
autoA.toLowerCase().contains(pattern.toLowerCase()))
.toList();
},
itemBuilder: (context, suggestion) {
return ListTile(
title: Text(suggestion),
);
},
transitionBuilder: (context, suggestionsBox, controller) {
return suggestionsBox;
},
onSuggestionSelected: (suggestion) {
typeAheadControllerA.text = suggestion;
},
)
Another minor problem/question:
How can i change the width of the suggestionbox / adapt to the width of the input field?
When I set the icon, logically the width of the input field decreased but the width of the suggestion box did not adapt to the width of the input field. I tried with suggestionsBoxDecoration offsetX. With offsetX I can move the box to the right, but the box keeps its width and therefore goes too far to the right but then i tried with wrapping the typeaheadfield in a padding widget or sizedbox but nothing works. If nobody knows how to solve this, I will probably remove the icons...
Thanks for help :)
You just use the keyboard "done" on iOS physical devices (I haven't tested it on Android yet - assuming there is something similar?), or click another field if there is one. The iOS simulator just doesn't bring up the keyboard when typing so it annoyingly seems like it can't be hidden without selecting a suggestion.
Tapping outside the field but not on another field doesn't unfocus normal TextFields either.
Use this metod: hideSuggestionsOnKeyboardHide: true,
You should wrap your scaffold with GestureDetector and with the property onTap() you call _suggestionBoxController.close();. Another thing if it does not work for you maybe you have a lot of code that is executed in one single file a simple refactoring can be your solution and set hideSuggestionsOnKeyboardHide : true (it was my case). For the width of the box suggestionBoxController.resize()