Flutter GoogleMap in a PageView build only ONCE - google-maps

I have a PageView.builder and 3 GoogleMap-s in it.
I had to create the 3 widgets only the first time, and I do not want to rebuild them again.
Now it is annoying when I just change the page it is flashing once before load. And slow.
Any way to build a FIXED state on that widget?
I tried:
AutomaticKeepAliveClientMixin
and
#override
bool get wantKeepAlive => true;
but not worked.

maybe you forget to call super.build(context); in build method.
Like this:
class TestInnerPage extends StatefulWidget {
#override
_TestInnerPageState createState() => _TestInnerPageState();
}
class _TestInnerPageState extends State<TestInnerPage>
with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
/// Dont't forget this
super.build(context);
return Container();
}
#override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}

According to the accepted answer, this will be an example using google maps.
class TestInnerPage extends StatefulWidget {
#override
_TestInnerPageState createState() => _TestInnerPageState();
}
class _TestInnerPageState extends State<TestInnerPage>
with AutomaticKeepAliveClientMixin {
//Variables
Completer<GoogleMapController> _controller = Completer();
void onMapCreated(GoogleMapController controller) {
controller.setMapStyle(Utils.mapStyles);
_controller.complete(controller);
}
#override
Widget build(BuildContext context) {
/// Dont't forget this
super.build(context);
return GoogleMap(
myLocationButtonEnabled: false,
compassEnabled: false,
myLocationEnabled: false,
zoomControlsEnabled: false,
// compassEnabled: true,
tiltGesturesEnabled: false,
// markers: _markers,
// polylines: _polylines,
mapType: MapType.normal,
initialCameraPosition: CameraPosition(
zoom: CAMERA_ZOOM,
bearing: CAMERA_BEARING,
tilt: CAMERA_TILT,
target: LatLng(
//SOURCE_LOCATION
7.8731,
80.7718),
),
onMapCreated: onMapCreated,
);
}
#override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}

I had a similar issue working with google map in pageview but after searching online I got a solution that finally worked
All I did was put the google map in a stateful widget, used the with AutomaticKeepAliveClientMixin and #override bool get wantKeepAlive => true; and called in the required widget
This is the stateful widget containing the google map
class GoogleMapWidget extends StatefulWidget{
const GoogleMapWidget({Key? key}) : super(key: key);
#override
_GoogleMapWidgetState createState() => _GoogleMapWidgetState();
}
class _GoogleMapWidgetState extends State<GoogleMapWidget> with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
return Container(
child:GoogleMap(initialCameraPosition: CameraPosition(target:LatLng(0, 0)),)
);
}
}
Then you can call it from your Homepage like so
class Homepage extends StatelessWidget {
#override
build(BuildContext context){
return PageView(
children: <Widget>[
GoogleMapWidget(),
GoogleMapWidget(),
],
);
}
}
I hope this is the answer you're looking for

Related

Flutter: how can I call method from another class?

I am new in Dart and Flutter. Now I am having a problem with calling a method from another class.
I have tried to make the method static, but the method contains setState() method so it is not possible.
So I have to call main.dart >>> showDialogWith() from wallet.dart
main.dart
import 'package:flutter/material.dart';
import 'dialog/operation.dart';
import 'pages/wallet.dart';
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future showDialogWith(String dialogName) async {
Widget dialog;
switch (dialogName) {
case 'operations':
setState(() {
dialog = OperationsDialog();
});
break;
// another cases and default...
}
await showDialog(
context: context,
child: dialog,
);
}
#override
Widget build(BuildContext context) {
body: WalletContent();
}
}
wallet.dart
class WalletContent extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialButton(
onPressed: () {
// here I have to call the 'showDialogWith()' method
},
);
}
}
operation.dart
class OperationsDialog extends StatefulWidget{
OperationsDialog({Key key}) : super(key: key);
#override
_OperationDialogState createState() => new _OperationDialogState();
}
class _OperationDialogState extends State<OperationsDialog> {
#override
Widget build(BuildContext context) {
return new SimpleDialog(
title: new Text('Операции', textAlign: TextAlign.center),
);
}
}
You can pass a function as a parameter.
#override
Widget build(BuildContext context) {
body: WalletContent(showDialogWith);
}
Add a Function field into your WalletContent and assign it to your MaterialButton
class WalletContent extends StatelessWidget {
WalletContent(this.onPressed);
final Function onPressed;
#override
Widget build(BuildContext context) {
return MaterialButton(
onPressed: () => onPressed(...), // Pass your desired string here
);
}
}

Only static members can be accessed in initializers - Completer<GoogleMapController> (Flutter with Google Maps)

I encountered an error while passing a map controller to my Custom widget. How can I solve this error message?
I already tried instantiating the controller inside the initState(){} but it does not seem to work.
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Completer<GoogleMapController> mapController;
LocationData currentLocation;
var location = new Location();
currentLocationCheck() async {...
}
#override
void initState() {
// TODO: implement initState
super.initState();
currentLocationCheck();
mapController = Completer();
}
#override
Widget build(BuildContext context) {
return Scaffold(.....
);
}
The error message is passed to the List widget. This List widget is then used inside the Scaffold Widget
List<Widget> customItemList = [
SizedBox(width: 10),
customListItemBox(
photoUrl:
"http://marjsia.com/wp-content/uploads/2013/08/P1100993-1024x683.jpg",
title: "Glorietta 2",
description: "Glorietta 2 J.Co Donuts",
latitude: 14.550664464,
longitude: 121.021833246,
mapController: mapController,
),
];
}
Widget customListItemBox(
{String photoUrl,
String title,
String description,
double latitude,
double longitude,
Completer mapController}) {
return GestureDetector(
onTap: () {
_goToLocation(latitude, longitude, mapController);
},
child: Padding(.....
),
);
}
This is the function that uses the controller
Future<void> _goToLocation(
double latitude, double longitude, Completer mapController) async {
final GoogleMapController controller = await mapController.future;
controller.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(latitude, longitude),
zoom: 15,
tilt: 50.0,
bearing: 45,
),
),
);
}
You can init customItemList in initState()
class SearchResultsState extends State<Map> {
Completer<GoogleMapController> _controller = Completer();
List<Widget> customItemList = [];
#override
void initState() {
super.initState();
//_controller = Completer();
customItemList = [
SizedBox(width: 10),
customListItemBox(
photoUrl:
"http://marjsia.com/wp-content/uploads/2013/08/P1100993-1024x683.jpg",
title: "Glorietta 2",
description: "Glorietta 2 J.Co Donuts",
latitude: 14.550664464,
longitude: 121.021833246,
mapController: _controller,
),
];
}

When launching the application the string appears as null

As the title has said, whenever I run the flutter application in my phone (debug mode atm, I don't know if it will work correctly in release mode). The dndguide.toString() appears as null. However, upon a hot reload the string appears normally. Is there a way to avoid this and make it work correctly upon launching? I suspect I put the loadjson() call in the wrong location, but I've tried shaping the code so that the function is called in different areas and no success.
Here is the code for the application:
import 'package:flutter/material.dart';
import 'dart:async' show Future;
import 'package:flutter/services.dart' show rootBundle;
import 'dart:convert';
void main() => runApp(MyApp());
var dndguide;
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<String> _loadAsset() async {
return await rootBundle.loadString('assets/data/HDARG.json');
}
Future loadjson() async {
String jsonString = await _loadAsset();
final jsonResponse = json.decode(jsonString);
dndguide = jsonResponse;
}
#override
Widget build(BuildContext context) {
loadjson();
var scrollcontroller;
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: MediaQuery.of(context).size.height - 24,
margin: EdgeInsets.only(top: 24.0),
width: MediaQuery.of(context).size.width * .90,
child: SingleChildScrollView(
physics: BouncingScrollPhysics(),
controller: scrollcontroller,
scrollDirection: Axis.vertical,
child: Text(dndguide.toString()),
),
),
],
),
),
);
}
}
The json response is called asynchronously, which why the first time it gives null and after hot reloading it appears successfully. You should put some placeholder value into your dndguide variable or call the json in the initState() function of your _MyHomePageState instead of calling it during build process:
#override
void initState() {
super.initState();
loadjson();
}

Get latitude and longitude of icon place in google map

In my flutter app. I am using google_maps_plugin . The link is https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter .
I want to fix the marker in center of map without moving of icon after drag the map. I successfully done by using stack . But my question is how to get the longitude and latitude of icon I placed in stack .
I want it likes http://jsfiddle.net/UuDA6/
The code is shown below.
class MyApp extends StatelessWidget {
MyApp();
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Title',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new AppPage(title: 'Augr'),
);
}
}
class AppState extends InheritedWidget {
const AppState({
Key key,
this.mode,
Widget child,
}) : assert(mode != null),
assert(child != null),
super(key: key, child: child);
final Geocoding mode;
static AppState of(BuildContext context) {
return context.inheritFromWidgetOfExactType(AppState);
}
#override
bool updateShouldNotify(AppState old) => mode != old.mode;
}
class AppPage extends StatefulWidget{
AppPage() : super(key: key);
#override
_AppPageState createState() => new _AppPageState();
}
class _AppPagePageState extends State<MyApp> {
_AppPagePageState();
List<Address> results = [];
String address;
String googleMapsApiKey = 'APIKEY';
GoogleMapController mapController;
Position position;
#override
Widget build(BuildContext context){
return new Scaffold(
appBar: null,
body: Padding(
padding: EdgeInsets.only(top: 25.0),
child:Column(
children: <Widget>[
SizedBox(
width: 200,
height: 300,
child: Stack(
children: <Widget>[
GoogleMap(
onMapCreated: _onMapCreated,
),
InfoView()
],
),
),],
)
),
);
}
void initState() {
super.initState();
}
void _onMapCreated(GoogleMapController controller) {
setState(() {
mapController = controller;
mapController.animateCamera(CameraUpdate.newCameraPosition(
CameraPosition(
bearing: 270.0,
target: LatLng(lattitude, longitude),
tilt: 30.0,
zoom: 17.0,
),
));
});
}
}
class InfoView extends StatelessWidget {
const InfoView({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return new Align(
alignment: Alignment.center,
child: new Icon(Icons.person_pin_circle, size: 40.0), //This the icon showing in google map .
);
}
}
The infoView() is defined the icon to show overlap in google map. I want to fetch the latitude and longitude of the icon place in map.
If any one have the idea about it please share it.

google map flutter plugin

hello I try to use google map plugin for flutter https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter
I use this exemple
https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/example/lib
but in this exemple there is some page. In my application I need only one map at the launch on the app. Problem, with this exemple I didn't manage to use it at my convenience. So I try to use the minimalist example of the read.me but it's a statlesswidget, and I and can't integer Tag fonction or the map_ui.dart like the complet example. So I tried to pass this stateless in statefull but when I do this I have an error
here is what I tried to compile from the two example
exemple 1 https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter
void main() {
GoogleMapController.init();
final GoogleMapOverlayController controller =
GoogleMapOverlayController.fromSize(width: 300.0, height: 200.0);
final Widget mapWidget = GoogleMapOverlay(controller: controller);
runApp(MaterialApp(
home: new Scaffold(
appBar: AppBar(title: const Text('Google Maps demo')),
body: MapUiBody(mapWidget, controller.mapController),
),
navigatorObservers: <NavigatorObserver>[controller.overlayController],
));
}
exemple 2
https://github.com/flutter/plugins/blob/master/packages/google_maps_flutter/example/lib/map_ui.dart
class MapUiBody extends StatefulWidget {
final GoogleMapOverlayController controller;
const MapUiBody(this.controller, GoogleMapController mapController);
#override
State<StatefulWidget> createState() =>
MapUiBodyState(controller.mapController);
}
class MapUiBodyState extends State<MapUiBody> {
MapUiBodyState(this.mapController);
final GoogleMapController mapController;
#override
Widget build(BuildContext context) {
return Column(
);
}
}
with this, I have an error
body: MapUiBody(mapWidget, controller.mapController),
mapWidget: the argument type Widget can't be assigned to the parameter type 'GooglemapoverlayController'
You have
final GoogleMapOverlayController controller;
const MapUiBody(this.controller, GoogleMapController mapController);
where you pass
final GoogleMapOverlayController controller =
GoogleMapOverlayController.fromSize(width: 300.0, height: 200.0);
final Widget mapWidget = GoogleMapOverlay(controller: controller);
...
body: MapUiBody(mapWidget, controller.mapController),
where mapWidget is passed to final GoogleMapOverlayController controller; which is not a Widget.
controller.mapController is probably a GoogleMapController as expected by const MapUiBody(..., GoogleMapController mapController);
but it seems redundant to pass that because you can get it from controller passed to mapWidget anyway.
It's not clear from your code what your intentions are.
Why do you want to pass mapWidget? What should happen with it in MapUiBody?
I succeeded to display map on home page but I can't move the map ..
void main() {
GoogleMapController.init();
final GoogleMapOverlayController controller =
GoogleMapOverlayController.fromSize(width: 300.0, height: 200.0);
final Widget mapWidget = GoogleMapOverlay(controller: controller);
runApp(MaterialApp(
home: new Scaffold(
appBar: AppBar(title: const Text('Google Maps demo')),
body: MapsDemo(mapWidget, controller.mapController),
),
));
}
class MapsDemo extends StatelessWidget {
MapsDemo(this.mapWidget, this.controller);
final Widget mapWidget;
final GoogleMapController controller;
#override
final GoogleMapOverlayController mapController =
GoogleMapOverlayController.fromSize(
width: 300.0,
height: 200.0,
options: GoogleMapOptions(
cameraPosition: const CameraPosition(
target: LatLng(-33.852, 151.211),
zoom: 11.0,
),
trackCameraPosition: true,
),
);
#override
Widget build(BuildContext context) {
return MapUiBody(mapController);
}
}
class MapUiBody extends StatefulWidget {
final GoogleMapOverlayController controller;
const MapUiBody(this.controller);
#override
State<StatefulWidget> createState() =>
MapUiBodyState(controller.mapController);
}
class MapUiBodyState extends State<MapUiBody> {
MapUiBodyState(this.mapController);
final GoogleMapController mapController;
GoogleMapOptions _options;
#override
void initState() {
super.initState();
mapController.addListener(_onMapChanged);
_extractMapInfo();
}
void _onMapChanged() {
setState(() {
_extractMapInfo();
});
}
void _extractMapInfo() {
_options = mapController.options;
}
#override
void dispose() {
mapController.removeListener(_onMapChanged);
super.dispose();
}
Widget _mapTypeCycler() {
final MapType nextType =
MapType.values[(_options.mapType.index + 1) % MapType.values.length];
return FlatButton(
child: Text('change map type to $nextType'),
onPressed: () {
mapController.updateMapOptions(
GoogleMapOptions(mapType: nextType),
);
},
);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(10.0),
child: Center(
child: GoogleMapOverlay(controller: widget.controller),
),
),
Column(
children: <Widget>[
_mapTypeCycler(),
],
),
],
);
}
}