I'm trying to have a page in my app display a user's location on a google map, which works in debug mode, but when I run it in release mode it simply loads indefinitely. The strange thing is that if I click on a different page on my app and go back to the map page it will immediately show. Does anyone know what could be going on here?
I also get the following error: [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: MissingPluginException(No implementation found for method camera#animate on channel plugins.flutter.io/google_maps_0)
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:mapapp/provider/location_provider.dart';
import 'package:provider/provider.dart';
class Map extends StatefulWidget {
const Map({Key? key}) : super(key: key);
#override
_State createState() => _State();
}
class _State extends State<Map> {
#override
void initState() {
super.initState();
Provider.of<LocationProvider>(context, listen: false).initalization();
}
//Sets map style once app is resumed to reload the map
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
dynamic controller;
controller.setMapStyle("[]");
}
}
#override
Widget build(BuildContext context) {
return Scaffold(body: googleMapUI());
}
Widget googleMapUI() {
return Consumer<LocationProvider>(builder: (
consumerContext,
model,
child,
) {
while (model.locationPosition != null) {
return Column(
children: [
Expanded(
child: GoogleMap(
mapType: MapType.normal,
padding: const EdgeInsets.only(top: 40.0),
initialCameraPosition:
CameraPosition(target: model.locationPosition!, zoom: 18),
myLocationEnabled: true,
myLocationButtonEnabled: true,
onMapCreated: (GoogleMapController controller) async {
Provider.of<LocationProvider>(context, listen: false);
},
),
)
],
);
}
return const Center(
child: CircularProgressIndicator(),
);
});
}
}
Related
I'm using : google_maps_flutter: ^2.1.6
flutter version : 3.0.1
dart version : 2.17.1
I can through the controller add marker, polygon, polyline and circles, but I couldn't change this icon that represents my location.
import 'dart:io';
import 'package:exemplo/controller/google_maps_controller.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class GoogleMapsWidget extends StatefulWidget {
const GoogleMapsWidget({super.key});
#override
State<GoogleMapsWidget> createState() => _GoogleMapsWidgetState();
}
class _GoogleMapsWidgetState extends State<GoogleMapsWidget> {
final GoogleMapsController googleMapsController = GoogleMapsController();
#override
void initState() {
googleMapsController.gpsPermission();
if (Platform.isAndroid) {
AndroidGoogleMapsFlutter.useAndroidViewSurface = true;
}
super.initState();
}
#override
void dispose() {
googleMapsController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: googleMapsController,
builder: (BuildContext context, Widget? child) => GoogleMap(
markers: googleMapsController.marker,
polygons: googleMapsController.polygon,
mapType: MapType.normal,
zoomControlsEnabled: true,
myLocationEnabled: true,
initialCameraPosition: CameraPosition(
target: LatLng(
googleMapsController.lat,
googleMapsController.long,
),
zoom: 15,
),
onMapCreated: googleMapsController.onMapCreated,
onTap: googleMapsController.selectLocal,
),
);
}
}
That blue ball in the middle
i start with this code but all what i get white screen
how i can use google map to get current location or choose location.I am using GoogleMap package in flutter to get the required current location.
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget { #override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Google Maps Demo', home: MapSample(), ); } }
class MapSample extends StatefulWidget { #override
State createState() => MapSampleState();
}
class MapSampleState extends State {
Completer _controller = Completer();
static final CameraPosition _kGooglePlex = CameraPosition( target:
LatLng(37.42796133580664, -122.085749655962), zoom: 14.4746, );
static final CameraPosition _kLake = CameraPosition( bearing:
192.8334901395799, target: LatLng(37.43296265331129,
-122.08832357078792), tilt: 59.440717697143555, zoom:
19.151926040649414);
#override Widget build(BuildContext context) { return new Scaffold(
body: GoogleMap(
mapType: MapType.hybrid, initialCameraPosition: _kGooglePlex,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
floatingActionButton:
FloatingActionButton.extended(
onPressed: _goToTheLake,
label:
Text('To the lake!'),
icon: Icon(Icons.directions_boat), ),
);
}
Future _goToTheLake() async {
final GoogleMapController controller = await _controller.future;
controller.animateCamera(CameraUpdate.newCameraPosition(_kLake));
}
}
If you're using Url properly(no spelling/parameter mistakes) with API key and there's no error from the server which happens a lot due to restrictions. Then there are other steps you need to, ask Location permission on app loading using https://pub.dev/packages/permission_handler package like this.
Code can be something like this.
Create an app_persmission_provider file.
class AppPermissionProvider with ChangeNotifier {
PermissionStatus _locationStatus = PermissionStatus.denied;
LatLng? _locationCenter;
final location_package.Location _location =
location_package.Location();
location_package.LocationData? _locationData;
// Getter
get locationStatus => _locationStatus;
get locationCenter => _locationCenter;
get location => _location;
void getLocationStatus() async {
final status = await Permission.location.request();
_locationStatus = status;
notifyListeners();
print(_locationStatus);
}
void getLocation() async {
_locationData = await _location.getLocation();
_locationCenter = LatLng(
_locationData!.latitude as double, _locationData!.longitude as double);
notifyListeners();
}
}
Then declare the provider on the root maybe, and In my case, I'm initializing some functions onInit. By doing so, android will ask for permission for location when your page loads.
#override
void initState() {
super.initState();
appPermission = Provider.of<AppPermissionProvider>(context, listen: false);
appPermission.getLocationStatus();
appPermission.getLocation();
}
And then using consumer.
SafeArea(
child: Consumer<AppPermissionProvider>(
builder: (context, appPermissionProvider, _) => appPermission
.locationCenter ==
null
? const Center(child: CircularProgressIndicator())
:
GoogleMap( myLocationButtonEnabled:
appPermissionProvider.locationStatus ==
PermissionStatus.granted
? true
: false,
myLocationEnabled: true,
initialCameraPosition: CameraPosition(
target: appPermission.locationCenter,
zoom: 14,
),
onMapCreated: onMapCreated,
mapType: MapType.normal,
compassEnabled: true,
),
),
)
Here, I am checking if permission is granted, also I am only enabling myLocationButton if location permission is granted.
You can use google_maps_flutter package ,
A short example how you can use it.
For more clearity you can visit
Here or you can use geolocator.
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
LatLng _initialcameraposition = LatLng(20.5937, 78.9629);
GoogleMapController _controller;
Location _location = Location();
void _onMapCreated(GoogleMapController _cntlr)
{
_controller = _cntlr;
_location.onLocationChanged.listen((l) {
_controller.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(target: LatLng(l.latitude, l.longitude),zoom: 15),
),
);
});
}
I'm working on an flutter application that should have a common codebase for web and mobile.
My app will have a google map and as far as I've seen there's not a single package to satisfy all platforms.
google_maps_flutter - seems to work only for mobile (IOS / Android)
google_maps_flutter_web - seems to work only for web
So most probably I have to create two separate MapWidgets, one for the web and one for mobile using these separate packages.
For mobile:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class MapSample extends StatefulWidget {
MapSample({Key? key}) : super(key: key);
#override
State<MapSample> createState() => MapSampleState();
}
class MapSampleState extends State<MapSample> {
final Completer<GoogleMapController> _controller = Completer();
static const CameraPosition _kGooglePlex = CameraPosition(
target: LatLng(37.42796133580664, -122.085749655962),
zoom: 14.4746,
);
#override
Widget build(BuildContext context) {
return GoogleMap(
mapType: MapType.hybrid,
initialCameraPosition: _kGooglePlex,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
);
}
}
For the web, it's a bit more complicated, it seems that google_maps_flutter_web isn't actually an usable version, from what I understand, (correct me if I'm wrong) and it actually uses another package that's not developed by the flutter team google_maps 6.0.0.
The objective of google_maps_flutter_web probably is to have the same api as google_maps_flutter (google_maps_flutter_platform_interface) and use it seamlessly, but I couldn't really find an example of how to use it...
How should I go about this? Any change I'm mistaken about google_maps_flutter_web and it actually works? Or I should just try to use google_maps which actually works for the web and just switch widgets based on kIsWeb?
Eventually I found a workaround using google_maps and this answer as inspiration:
Abstract MapWidget
import 'package:client_ojp4danube/map/map_widget_stub.dart'
if (dart.library.html) 'package:client_ojp4danube/map/map_web_widget.dart'
if (dart.library.io) 'package:client_ojp4danube/map/map_widget.dart';
import 'package:flutter/material.dart';
abstract class MapWidget extends StatefulWidget {
factory MapWidget() => getMapWidget();
}
WebMap widget that uses google_maps:
import 'dart:html';
import 'package:client_ojp4danube/map/abstract_map_widget.dart';
import 'package:flutter/cupertino.dart';
import 'package:google_maps/google_maps.dart';
import 'dart:ui' as ui;
Widget getMap() {
String htmlId = "7";
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
final myLatlng = new LatLng(30.2669444, -97.7427778);
final mapOptions = new MapOptions()
..zoom = 8
..center = new LatLng(30.2669444, -97.7427778);
final elem = DivElement()
..id = htmlId
..style.width = "100%"
..style.height = "100%"
..style.border = 'none';
final map = GMap(elem, mapOptions);
Marker(MarkerOptions()
..position = myLatlng
..map = map
..title = 'Hello World!');
return elem;
});
return HtmlElementView(viewType: htmlId);
}
class WebMap extends StatefulWidget implements MapWidget {
WebMap({Key? key}) : super(key: key);
#override
State<WebMap> createState() => WebMapState();
}
class WebMapState extends State<WebMap> {
#override
Widget build(BuildContext context) {
return getMap();
}
}
MapWidget getMapWidget() {
print("Intra in get map web ");
return WebMap();
}
Mobile Map Widget
import 'dart:async';
import 'package:client_ojp4danube/map/abstract_map_widget.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class MobileMap extends StatefulWidget implements MapWidget {
MobileMap({Key? key}) : super(key: key);
#override
State<MobileMap> createState() => MobileMapState();
}
class MobileMapState extends State<MobileMap> {
final Completer<GoogleMapController> _controller = Completer();
static const CameraPosition _kGooglePlex = CameraPosition(
target: LatLng(37.42796133580664, -122.085749655962),
zoom: 14.4746,
);
#override
Widget build(BuildContext context) {
return GoogleMap(
mapType: MapType.hybrid,
initialCameraPosition: _kGooglePlex,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
);
}
}
MapWidget getMapWidget() {
return MobileMap();
}
getMapWidget - stub
import 'package:client_ojp4danube/map/abstract_map_widget.dart';
// Created because importing dart.html on a mobile app breaks the build
MapWidget getMapWidget() => throw UnsupportedError(
'Cannot create a map without dart:html or google_maps_flutter');
Actually using the abstract widget that will return the widget suited for the platform
import 'package:client_ojp4danube/map/abstract_map_widget.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(child: MapWidget()),
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
EDIT: A new official plugin has been released: https://pub.dev/packages/google_maps_flutter_web . It already works with the existing google_maps_flutter plugin, just add your api script in the web/index.html .
As the user exilonX suggested, the current way (Apr '22) to use Google Maps on both Flutter web and mobile, is to load the library dynamically based on the device. However, his answer lacks of some important details. It took me almost 1h to make his work working, therefore I'm sharing here a clearer and more organized solution, hopefully it'll save you some time (I couldn't edit his answer due to long edit queue).
Folder structure:
\widget
\map_widget.dart
\web_map_widget.dart
\mob_map_widget.dart
\map_widget_stub.dart
MapWidget:
In the file map_widget.dart you'll have the abstract MapWidget:
import 'package:flutter/material.dart';
import 'map_widget_stub.dart'
if (dart.library.html) 'web_map_widget.dart'
if (dart.library.io) 'mob_map_widget.dart';
abstract class MapWidget extends StatefulWidget {
factory MapWidget() => getMapWidget();
}
NOTE: the only semicolumn you need on the conditional import is at the end of the second if.
Web MapWidget:
This file will contain the google map shown on web:
import 'dart:html';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:google_maps/google_maps.dart';
import 'map_widget.dart';
MapWidget getMapWidget() => WebMap();
class WebMap extends StatefulWidget implements MapWidget {
WebMap({Key? key}) : super(key: key);
#override
State<WebMap> createState() => WebMapState();
}
class WebMapState extends State<WebMap> {
#override
Widget build(BuildContext context) {
final String htmlId = "map";
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
final mapOptions = MapOptions()
..zoom = 15.0
..center = LatLng(35.7560423, 139.7803552);
final elem = DivElement()..id = htmlId;
final map = GMap(elem, mapOptions);
map.onCenterChanged.listen((event) {});
map.onDragstart.listen((event) {});
map.onDragend.listen((event) {});
Marker(MarkerOptions()
..position = map.center
..map = map);
return elem;
});
return HtmlElementView(viewType: htmlId);
}
}
Here you can find more details about the web implementation.
Mobile MapWidget:
This file contains the implementation for mobile (android/ios):
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'map_widget.dart';
MapWidget getMapWidget() => MobileMap();
class MobileMap extends StatefulWidget implements MapWidget {
MobileMap({Key? key}) : super(key: key);
#override
State<MobileMap> createState() => MobileMapState();
}
class MobileMapState extends State<MobileMap> {
final Completer<GoogleMapController> _controller = Completer();
static const CameraPosition _kFalentexHouse =
CameraPosition(target: LatLng(44.497858579692135, 11.336362079086408));
#override
Widget build(BuildContext context) {
return GoogleMap(
mapType: MapType.hybrid,
initialCameraPosition: _kFalentexHouse,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
);
}
}
Stub
Finally, you need a stub:
import 'map_widget.dart';
//the error is shown in case of wrong version loaded on wrong platform
MapWidget getMapWidget() => throw UnsupportedError(
'Cannot create a map without dart:html or google_maps_flutter');
Usage
Now you can use the widget MapWidget as a normal widget:
Scaffold(
body: Center(
child: SizedBox(
height: 300,
width: 300,
child: MapWidget(),
),
),
);
NOTE: in order to make the map work you need to set it up with the key. See the official library documentation for mobile and web.
I have successfully implemented the flutter_secure_storage in my flutter project, when the user writes his email and password, it get's stored, but here is the thing I don't understand. How should I setup screen routes depending if the user has already logged in or not. If it is the same user, so the username and pass are stored in the secure_storage, I want him to go directly to HomeScreen(), but if there is a new user that needs to log in, so there is no data in the secure_storage, then I want him sent to LoginScreen(). I have done this so far:
import 'dart:async';
import 'package:flutter/material.dart';
import 'login_screen.dart';
import 'home_screen.dart';
import 'components/alarm_buttons.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class WelcomeScreen extends StatefulWidget {
static const String id = 'welcome_screen';
#override
_WelcomeScreenState createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen> {
void readData() async {
final storage = FlutterSecureStorage();
String myPassword = await storage.read(key: "p");
String myEmail = await storage.read(key: "e");
print(myEmail);
print(myPassword);
}
#override
void initState() {
final storage = FlutterSecureStorage();
Timer(
Duration(seconds: 2),
() => Navigator.pushNamed(
context,
storage == null ? LoginScreen.id : HomePage.id,
));
readData();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
AlarmButtons(
buttonColour: Colors.grey,
buttonText: 'Log In',
buttonTextColour: Colors.white,
onButtonPress: () {
Navigator.pushNamed(context, LoginScreen.id);
},
),
AlarmButtons(
buttonColour: Colors.white,
buttonText: 'Sign up',
buttonTextColour: Colors.grey,
onButtonPress: () {
Navigator.pushNamed(context, SignUpScreen.id);
},
),
],
),
),
);
}
}
Now the problem starts when I want to return to the Welcome Screen (the starting page of my app shown above), naturally it triggers the initState again and I get back to the HomePage() again. How can I dispose of that, only triggering that initState when the app starts, so after automatic login I can return to the Welcome Screen without triggering it?
Thanks in advance!
You should initial start something like a SplashScreen (or WelcomeScreen in your case). There you can decide to which screen you want to navigate based on the saved data. Example:
class SplashScreen extends StatefulWidget {
const SplashScreen({Key key}) : super(key: key);
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
_startApp();
super.initState();
}
Future<void> _startApp() async {
final storage = FlutterSecureStorage();
String myEmail = await storage.read(key: "e");
if (myEmail == null) {
// TODO Navigate to Login Screen
} else {
// TODO Navigate to Home Screen
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("Splashscreen"),
),
);
}
}
I would like to use this plugin (https://pub.dev/packages/youtube_native_player) because it has minimal buttons, but if I push the back button it continuous playing in background. I can go back but the video starting again with the previous sound's in background etc.
I do not know how to reach it's controller. Can I get some help?
My code:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:uhu/common/widgets/base_stateless_widget.dart';
import 'package:youtube_native_player/android/constrant.dart';
import 'package:youtube_native_player/android/video_player_controller.dart';
import 'package:youtube_native_player/youtube_native_player.dart';
class YoutubeIosWidget extends StatefulWidget {
YoutubeIosWidget({Key key}) : super(key: key);
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<YoutubeIosWidget> {
String position = "Get Current Position";
String status = "Get Player Status";
String videoDuration = "Get Video Duration";
String _source = CommonThings.videoId;
bool isMute = false;
VideoPlayerController _controller;
#override
void initState() {
super.initState();
}
#override
void dispose() {
// This pauses video while navigating to next page.
// super.dispose();
}
#override
Widget build(BuildContext context) {
CommonThings.videoId = "";
return new WillPopScope(
onWillPop: () async => false,
child: new Scaffold(
backgroundColor: Colors.black.withOpacity(0.88),
appBar: AppBar(
backgroundColor: Colors.transparent,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () async {
setState(() {});
Navigator.pop(context);
})),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
YoutubeNativePlayer(
context: context,
source: _source,
quality: YoutubeQuality.HD,
aspectRatio: 16 / 9,
autoPlay: true,
loop: false,
reactToOrientationChange: true,
startFullScreen: true,
controlsActiveBackgroundOverlay: false,
controlsTimeOut: Duration(seconds: 1),
playerMode: YoutubePlayerMode.DEFAULT,
switchFullScreenOnLongPress: true,
callbackController: (controller) {
_controller = controller;
},
onError: (error) {
print(error);
},
onVideoEnded: () => Navigator.pop(context))
],
),
));
}
}
You need to uncomment your dispose code and also close (or dispose) your video controller:
void dispose() {
// This pauses video while navigating to next page.
super.dispose();
_controller.close(); // or _controller.dispose();
}