Save Google Maps state in Flutter - google-maps

So I basically have an application with three tabs. On one of them, I have a Google Map with corresponding markers and a listener with a location based Firestore query. When the tab is selected, nearby elements will be queried and presented on the map.
However, when I change the tab and go back to the map again, the map, the listener and all the markers will be setup again (since initState() and dispose() were called).
What is the best way to save all the states from the map and continue on the previous state of the map, when the page is selected?
I have read something regarding Redux and BloC, is this the pattern I will need here? If yes, how can this be applied to the GoogleMap?

I had the same problem, Flutter is basically reloading your googlemaps page everytime you change page. I solved this using IndexedStack. So I have all my pages in the stack all the time and they wont reload. I'm not sure if this is the best way but it works.
child: new IndexedStack(
children: _pages,
index: widget.currentItem,
),
IndexedStack only shows the index item of the Stack, but all the items are in the stack even though they are not shown, so they wont be reloaded when you change the tab. Change the currentItem depending on the index of TabBar index with setState...

You can actually force Flutter to keep the widget tree alive using AutomaticKeepAliveClientMixin like so:
class LocationMapScreen extends StatefulWidget {
#override
_LocationMapScreenState createState() => _LocationMapScreenState();
}
class _LocationMapScreenState extends State<LocationMapScreen>
with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
// ....
}

Related

MediaCapture and Window VisibilityChanged

[Question]
On Windows Phone 8.1, what exactly happens in between the time when the user leaves the app and the OnSuspended event fires? I'm having trouble with the ability to manage objects in that span, in particular MediaCpture object.
To better explain the problem, here is the scenario:
The user is on a page with a video preview being pumped to a CaptureElement
The user taps the Start button
The user taps Back button and returns to the page with a broken MediaCapture
With WinRT there isn't an ObscuredEvent and OnNavigatingFrom doesn’t fire unless you’re going to another page in the same Frame. After some investigation, I've found that the only event that fires is Window.Current.VisibilityChanged
I've gone ahead and hook it when the page is NavigatedTo and unhooked in OnNavigatedFrom (see ex2 below). Inside the event, I check for parameter that tells if the app is hiding or showing and dispose/initialize accordingly(see ex.1 below).
[Problem]
However, this only works with the debugger attached. If I do this without the debugger attached, it doesn't reinitialize and frequently crashes the camera and I have to literally reboot the device.
Code Example 1 (note: e.Visible == false is leaving the app and true when returning)
async void Current_VisibilityChanged(object sender, VisibilityChangedEventArgs e)
{
if (!e.Visible) //means leaving the app
{
await DisposeAll(); //cleans the MediaCapture and CaptureElement
}
else
{
if(mediaCaptureManager != null) await DisposeAll();
await Initialization(); //set up camera again
}
}
Example 2 (hooking into the event)
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Window.Current.VisibilityChanged += Current_VisibilityChanged;
this.navigationHelper.OnNavigatedTo(e);
}
protected async override void OnNavigatedFrom(NavigationEventArgs e)
{
Window.Current.VisibilityChanged -= Current_VisibilityChanged;
this.navigationHelper.OnNavigatedFrom(e);
}
[Update: Resolution]
Instead of using VisibilityChanged, hook into Window.Current.Activated on the page's constructor. With the debugger completely detached, the Activated event will provide the WindowActivationState parameter in the WindowActivatedEventArgs. Like this:
private async void CurrentOnActivated(object sender, WindowActivatedEventArgs e)
{
if(e.WindowActivationState == CoreWindowActivationState.Deactivated)
{
//dispose MediaCapture here
}
else if(e.WindowActivationState == CoreWindowActivationState.CodeActivated || e.WindowActivationState == CoreWindowActivationState.PointerActivated)
{
//initialize MediaCapture here
}
}
See my answer in https://stackoverflow.com/a/28592882/3998132. Using Window.VisibilityChanged in conjunction with your Page\UserControl Loaded\Unloaded handler should solve your issue I believe.
Using Window.Activated is less desirable than Window.VisibilityChanged because Activated relates to being visible AND having focus where as VisibilityChanged only pertains to visibility. For showing a preview having focus is not applicable. Since Windows Store apps on Windows Phone can only have one Window showing there is no difference in using either however if your app becomes universal and runs on let's say on Windows 8+ Modern shell (which can show multiple Store apps with the Snap window feature) or Windows 10 desktop (which can support multiple Store apps showing at the same time) you will not want to stop preview when a user changes focus from your app but your app is still showing.
I'm not sure if it wouldn't be more suitable to use Suspending/Resuming events. Note only that in this case, you will have to debug it properly - it behaves little different while being run with/without debugger attached.
As for the code - hooking your event in OnNavigatedTo/OnNavigatedFrom is not a good idea - when the OS suspends the app and you are using SuspensionManager then OnNavigatedFrom will be called, but when you go back to your app (resume it), then OnNavigatedTo will not be called.
Using Window events may also work here, but why not subscribe it once, somewhere in constructor? - it's window-wide and hence in phone there is only one window, which stands for app, then subscribe once. In this case, you may add a line that recognizes the current page in window and if that page contains mediacapture then dispose (create similar). Then you can also dispose/initialize in navigation events in case user doesn't leave your app and just navigate.

Windows Phone 8.1 (runtime) additional data in OnMapTapped event

I am trying to create a Windows Phone 8.1 (Runtime) app that has a Map Control on it. I would like to attach additional data to the OnMappedTapped event or a way to grab an assigned Location ID when someone clicks on the MapIcon. Is this possible?
Yes. If you want to do something when someone clicks on a MapIcon (pushpin), then add a tap to the map and then do a search for elements that intersect the touch point using the Map.FindMapElementsAtOffset method: http://msdn.microsoft.com/en-us/library/windows/apps/xaml/windows.ui.xaml.controls.maps.mapcontrol.findmapelementsatoffset.aspx
or the Map.FindSubElementsForTouchTargeting method: http://msdn.microsoft.com/en-us/library/windows/apps/xaml/windows.ui.xaml.uielement.findsubelementsfortouchtargeting.aspx
When it comes to storing or associating data to a pushpin, I like to store my data in the standard Tag property as that's something I've been doing for a while with WPF and Silverlight. You will notice that the MapIcon/MapElement classes do not have a Tag property, however they are DependancyObjects which meanswe can easily add custom properties to these classes. Here is a simple extension I often use in my WP apps to add a Tag property to these classes.
public static class MapElementExt
{
public static readonly DependencyProperty TagProperty = DependencyProperty.Register("Tag", typeof(object), typeof(MapElement), new PropertyMetadata(null));
}
You can then set this value like this:
pushpin.SetValue(MapElementExt.TagProperty, MyPinData);
Personally, when it comes to pushpins I normally don't mess with the MapIcon/MapElement classes and just create a UIElement for my pushpin. By doing this I can easily have a lot more control over creating my pushpin and can also easily add Tap events. You can specify the location for a UIElement like this:
MapControl.SetLocation(pushpin, MyGeopoint);
And then add the pushpin to the Map.Children property.
If you want to get the coordinates for a randomly selected point on a map through a touch event you can take the pixel coordinates from the tap event and pass them through the Map.GetLocationFromOffset method. For example:
MyMap.Tapped += (s, e) =>
{
Geopoint loc;
MyMap.GetLocationFromOffset(e.GetPosition(MyMap), out loc);
};

Metro App OnSearchActivated ProgressRing not displaying

I am implementing searching within my Metro application. The search works well, results and UI come up as expected with one problem though.
I try to display a ProgressRing before the search and hide it after the search completes, but it never gets displayed.
What am I missing, code snippet below:
protected override void OnSearchActivated(Windows.ApplicationModel.Activation.SearchActivatedEventArgs args)
{
// Some Metro designer generated code here
// Show progress ring
MainPage.Current.ResetProgressRingState(true);
// Bind search results
MainPage.Current.BindSearchResults(args.QueryText);
// Ensure the current window is active
Window.Current.Activate();
// Hide progress ring
MainPage.Current.ResetProgressRingState(false);
}
I suspect that the BindSearchResults method needs to be awaited in order for the ProgressRing to work correctly. If so, what's the easiest way to make that method awaitable, if not please advise what I am missing here.
If so, what's the easiest way to make that method awaitable, if not please advise what I am missing here.
Mark it as async and have it return Task. Within that method, use await on other asynchronous operations.

View instantiating another view

I'm separating my code similar to MVC (models, views, and controllers).
Let's say I have two view classes, one is conceptually a page (contains many items) and another is just a widget (like a list of recent news).
Here are the ways I see of doing it:
The controller instantiates both the page and widget classes, then passes the widget object into the page. If the page has a lot of widgets, I'd have to figure out how to pass it all in without it being confusing.
class PageController {
function foo() {
Widget widget1 = new Widget();
Widget widget2 = new Widget();
Page page = new Page(widget1, widget2);
}
}
class Page {
function Page(Widget widget1, Widget widget2) {
//do something with the widgets
}
}
The page instantiates the widget class. But now the page class has references to all sorts of views instead of being able to place the view where ever as long as it has the appropriate interface.
class PageController {
function foo() {
Page page = new Page();
}
}
class Page {
function Page() {
Widget widget1 = new Widget();
Widget widget2 = new Widget();
//do something with the widgets
}
}
Something else?
What is your advice and why?
Thank you.
I actually side with approach 1 in some ways. I would say Page should be able to be instantiated with or without widgets. You can add a collection of widgets to begin with and/or one at a time through some other provision within the Page as you run through your business logic and rules.
This gives you more flexibility to modify the construction of page as you go.
Widgets contain information on how they function and their presentation within the layout of the page.
Page should only be responsible for getting the information/instructions from the contained widgets and initialize/render them.
This will allow for a more flexible and fluid design.
Conceptually, #2 makes the most sense to me.
It's perfectly fine to have coupling between views if one type of view contains another. For example, it should be the page's job to manage where to place each widget, when to hide certain widgets etc.
However, the page shouldn't care about events pertaining to those widgets unless it needs to. If a widget has a button, it should just callback to a controller and skip telling the page... unless it causes the widget to be resized and the page needs to lay things out differently.
Hopefully this is useful to you.

Flex Mobile persist view data

I built a simple hello world app to check out the Flash Builder 4.5 mobile capabilities.
Here's how it works:
The Default View asks for name in an textinput and has a continue button
When you click the continue button it pushes a new view in the viewNavigator which just displays "Hello " + name in a label.
When you click anywhere in this view, it pops a view (i.e. itself) from the viewNavigator, to go back to the default view
I see only 1 issue with this:
When I get back to the default view, it is in its initial state, i.e. the textInput is blank. It seems as if the viewNavigator created a new view of the default view's class and pushed this, instead of just removing the top view and displaying the previous one.
I see this being especially problematic for programs which display data in a grid and you can click the data to view the detail...when you get back, the grid will be empty.
Any ideas or gotchas to solve this?
EDIT:
Project name: HelloWorld
Code below:
HelloWorldDefaultView.mxml
protected function button1_clickHandler(event:MouseEvent):void {
navigator.pushView(HiView, tName.text);
}
HiView.mxml
protected function view1_clickHandler(event:MouseEvent):void {
navigator.popView();
}
protected function view1_creationCompleteHandler(event:FlexEvent):void {
lblHello.text="Hello " + data;
}
Screenshots
Initial screen
Screen 2
Clicking on screen 2 gets us back to initial screen. Notice the blank textInput
Have you tried to set destructionPolicy="never" and then
protected function button1_clickHandler(event:MouseEvent):void {
data = tName.text;
navigator.pushView(HiView, tName.text);
}
to store the data in current View's data - before changing to another one?
That's the way it is supposed to work for mobile applications.
Check out this article: Flex 4.5 (Hero) – Persistant Data in MobileApplication
As they write:
Each time a View is removed from the display list (via popView() or pushView()) its instance is destroyed, but its data model is stored in memory.
In order to save session state for a View, you must modify the data property. This property will be requested when destroying the current instance of the View class. And the data property value will be assigned back to a newly created instance of the same View class when navigating back to that view.