I've recently started getting into flutter, but just as I was about to write a few widget tests, I noticed that I wasn't terribly sure how to mock out the Google Maps Flutter package.
Many examples I've seen include using the library "mockito" to mock out classes, but this assumes that the Google Maps widget will be injected into the widget to be tested. Unfortunately, with their given documentation and startup guide, this doesn't seem to be very possible:
class MapsDemo extends StatefulWidget {
#override
State createState() => MapsDemoState();
}
class MapsDemoState extends State<MapsDemo> {
GoogleMapController mapController;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(15.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Center(
child: SizedBox(
width: 300.0,
height: 200.0,
child: GoogleMap(
onMapCreated: _onMapCreated,
),
),
),
RaisedButton(
child: const Text('Go to London'),
onPressed: mapController == null ? null : () {
mapController.animateCamera(CameraUpdate.newCameraPosition(
const CameraPosition(
bearing: 270.0,
target: LatLng(51.5160895, -0.1294527),
tilt: 30.0,
zoom: 17.0,
),
));
},
),
],
),
);
}
void _onMapCreated(GoogleMapController controller) {
setState(() { mapController = controller; });
}
}
Note that the GoogleMaps widget cannot be passed in because onMapCreated is a required function, and that function relies private class method (give the parent widget access to GoogleMapsController). Many other examples of mockito mock functions that don't have this sort of callback function to set state.
There doesn't seem to be any other packages I've seen that can effectively mock out the GoogleMaps widget, so I don't really have any sort of example to follow. Ideally, what I was expecting was some sort of behavior like proxyquire or sinon in node.s (where you don't need to pass in the mocked libraries into function.constructors), but it looks like mockified classes need to be passed into the tested widgets.
Are there any other ideas on how to mock out this library for testing? Or should I just live with testing the actual functionality?
I managed to mock the GoogleMaps by mocking the channels it uses:
setUpAll(() async {
SystemChannels.platform_views.setMockMethodCallHandler((MethodCall call) {
switch (call.method) {
case 'create':
return Future<int>.sync(() => 1);
default:
return Future<void>.sync(() {});
}
});
MethodChannel('plugins.flutter.io/google_maps_0', StandardMethodCodec())
.setMockMethodCallHandler((MethodCall methodCall) async {
return null;
});
}
I got inspiration from this webview plugin test (which is a PlatformView like the GoogleMaps widget), as well as this GoogleMaps plugin test
Related
Hei there,
I have the Problem that my WebView doesn't load. It only shows me a loading Screen.
I am using the flutter_webview_plugin in Flutter for Web.
I have no idea why its always loading. It does this always, with whatever Website I tried.
import 'package:flutter/material.dart';
import 'package:website_aalen_by_night/widgets/info_card.dart';
import 'package:website_aalen_by_night/widgets/nav_bar.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'AAlen by Night',
theme: ThemeData.dark(),
home: WebsiteAalenByNight(),
debugShowCheckedModeBanner: false,
);
}
}
class WebsiteAalenByNight extends StatefulWidget {
#override
WebsiteState createState() => WebsiteState();
}
ScrollController controller = new ScrollController();
class WebsiteState extends State {
#override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;
FlutterWebviewPlugin().onHttpError.listen((WebViewHttpError item) {
print(" WebView onHttpError.code: ${item.code}");
});
return Scaffold(
backgroundColor: Color.fromRGBO(79, 79, 79, 1),
body: Stack(
children: <Widget>[
Scrollbar(
child: ListView(
controller: controller,
children: <Widget>[
InfoCard(),
Image(
image: AssetImage("lib/images/map.png"),
//height: height,
width: width,
fit: BoxFit.cover,
),
InfoCard(),
LimitedBox(
maxWidth: width,
maxHeight: height,
child: WebviewScaffold(
url: "https://www.google.com",
withZoom: false,
withLocalStorage: false,
withJavascript: true,
withLocalUrl: true,
),
),
],
),
),
NavBar(),
],
),
);
}
}
The goal of this is to implement a Map with Markers on the Screen. So I believed I can try a WebView but with that I did come to a stop soon.
Maybe there is a better Way to implement a Map (tried a few other Things like using a map plugin instead but I didn't find any which works for Flutter for Web).
It is really important to get it working on Flutter for Web and NOT on
any other Platform!
Thanks for helping me....
Maybe in the Future I am able to answer such questions for others :D
I don't think that https://github.com/fluttercommunity/flutter_webview_plugin works in web since it uses native libraries.
HOWEVER, the great advantage of using a browser for your flutter app, is that you can use HTML and you don't need a webview there!
check this example of using a nested youtube player
void main() {
ui.platformViewRegistry.registerViewFactory(
'hello-world-html',
(int viewId) => IFrameElement()
..width = '640'
..height = '360'
..src = 'https://www.youtube.com/embed/IyFZznAk69U'
..style.border = 'none'
);
runApp(Directionality(
textDirection: TextDirection.ltr,
child: SizedBox(
width: 640,
height: 360,
child: HtmlElementView(viewType: 'hello-world-html'),
),
));
}
source
Of course the communication with the content it's another history, since it's an iframe and in web browsers CORS is enabled it means you can't access the iframe from flutter, in the case of Google maps, they have an URL api and pass your marker location there ;)
I'm trying to add marker to my map widget based on the string in a Json file. However i have no idea how the marker thingy works after the .addMarker no longer applied. Can anyone please explain on it?
The markers: attribute in the Flutter_Google_maps widget now takes a Set<Marker>().
to add markers, first you should create an empty Set of type Marker and assign it to the markers: attribute in the google maps widget. Consequently, you append the desired Marker() to the created set using the add() method. Finally, you use setState() to rebuild the widget and display the updated Markers set on the UI.
Example:
class MapsScreen extends StatefulWidget {
#override
_MapsScreenState createState() => _MapsScreenState();
}
class _MapsScreenState extends State<MapsScreen> {
Completer<GoogleMapController> _mapsController = Completer();
Set<Marker> _myMarkers = Set<Marker>();
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
_myMakers.add(
markerId: MarkerId("current"),
position: LatLng(yourLatitude, yourLongitude),
);
setState((){});
)},
),
body: GoogleMap(
initialCameraPosition: _anyCameraPosition,
markers: _myMarkers,
onMapCreated: (GoogleMapController controller) {
_mapsController.complete(controller);
},
);
);
}
}
you have the option to use any state management pattern you prefer.
I use the package google_maps_flutter to use Google maps in my app. My problem is how to set a listener, show when I press in the map to get the coordination of this place. I don't find anything in documentation.
The only thing which I find is with controllerMap, which I use to set marker listener, is that it has a method,
.addListener(listener)
Any idea?
I have solved the problem using the onMarkerTapped callback methods below:
Note: mapController below is an instance of the GoogleMap Controller
mapController.**onMarkerTapped**.add((marker){
String title= marker.options.infoWindowText.title;
String latitude= marker.options.position.latitude.toString();
String longitude= marker.options.position.longitude.toString();
});
google map plugin has a lot of errors:), I prefer using this plugin : flutter_map
full example :
import 'package:location/location.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong/latlong.dart';
class ContactPage extends StatefulWidget {
#override
ContactPageState createState() => new ContactPageState();
}
class ContactPageState extends State<ContactPage>
with TickerProviderStateMixin {
static LatLng myLocation = new LatLng(51.5, -0.09);
#override
void initState() {
super.initState();
setState(() {
new LatLng(51.5, -0.09);
});
}
#override
Widget build(BuildContext context) {
Size screenSize = MediaQuery.of(context).size;
double heigh = screenSize.height;
TextStyle whiteStyle = new TextStyle(fontSize: 20.0, color: Colors.white);
return new Directionality(
textDirection: TextDirection.rtl,
child: new Container(
padding: new EdgeInsets.only(bottom: 10.0, left: 1.0, right: 1.0),
color: Colors.white,
child: new FlutterMap(
options: new MapOptions(
center: myLocation,
zoom: 15.0,
maxZoom: 15.0,
minZoom: 3.0,
onTap: _handleTap),
layers: [
new TileLayerOptions(
urlTemplate:
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
subdomains: ['a', 'b', 'c']),
new MarkerLayerOptions(markers: markers)
],
)
)),
);
}
_handleTap(LatLng point) {
setState(() {
myLocation = point;
});
}
}
This functionality is currently not available in version 2.0 of the google flutter plugin, however there are two pull requests that have added this functionality.
1121
941
Pull request 1121 has example code on how to use the tap functionality.
With the current documentation, I only made the following change to the example of #lionelsanou and #Pradeep given above:
String latitude= marker.values.first.position.toString();
String longitude= marker.values.last.position.toString();
and it worked for me.
I'm working on Flutter for an app that uses Google Maps. The app is made up of 2 activities: a list and the map.
This paragraph is just some background information. You may skip it. Usually Google Maps let you call a map by tapping a static map which opens into a dynamic one. The static map is just a small widget. The app need to directly display a full screen map on a single activity.
The issue we faced has to do with Overlays. Auby Khan from medium.com provided the code needed to display a full screen map. So we created the first activity with a button that navigates you to the second activity:
new IconButton(
icon: new Icon(Icons.map),
tooltip: 'openMap',
onPressed: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ControlParkMapNormal()),
);
},
),
And in the second activity, it would display the map:
final size = MediaQueryData.fromWindow(ui.window).size;
final GoogleMapOverlayController controller =
GoogleMapOverlayController.fromSize(
width: size.width,
height: size.height,
);
final mapController = controller.mapController;
final Widget mapWidget = GoogleMapOverlay(controller: controller);
class ControlParkMapNormal extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: new Scaffold(
appBar: AppBar(
title: Text("ControlPark"),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.list),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ControlPark()),
);
},
),
],
),
body: MapsDemo(mapWidget, controller.mapController),
),
navigatorObservers: <NavigatorObserver>[controller.overlayController],
);
}
}
class MapsDemo extends StatelessWidget {
MapsDemo(this.mapWidget, this.controller);
final Widget mapWidget;
final GoogleMapController controller;
#override
Widget build(BuildContext context) {
return Center(child: mapWidget);
}
}
When Navigating from Activity 1 > Activity 2 > Activity 1, the map would remain displayed. It would seem that the map is permanently overlayed on all subsequent activities. The transition from Activity 1 > Activity 2 > Activity 1 > Activity 2 yields this error:
I/flutter (12701): Another exception was thrown: 'package:flutter/src/widgets/navigator.dart': Failed assertion: line 1303 pos 14: 'observer.navigator == null': is not true.
Here's the block of code for the observer navigator:
#override
void initState() {
super.initState();
for (NavigatorObserver observer in widget.observers) {
assert(observer.navigator == null);
observer._navigator = this;
}
Is there any way to resolve this?
I would like to ask on how to show the map fullscreen using the function MapView.Show function on map_view dart framework which I couldn't implement as a widget in flutter. see my code below:
MapView showMap() {
return _mapView.show(new MapOptions(
mapViewType: MapViewType.normal,
initialCameraPosition:
new CameraPosition(new Location(10.31264, 123.91139), 12.0),
showUserLocation: true,
));
}
should be put inside the child in widget.
#override
Widget build(BuildContext context) {
return new Container(
color: Colors.red,
height: double.infinity,
width: double.infinity,
child: showMap(), // surprisingly not working
);
}
I looked into tutorials on this implementation but it seems I haven't seen any liable sources on this implementation. Does anyone knew how to implement this one? Thanks!
Note: I want to implement as a fullscreen widget.
As far as I know, you have to show the Map_View calling a function when an event happens (like when a button is pressed). It's not a Widget like a Text or a Padding.
If you want to open a fullscreen Map I'd suggest you to try, for example, this.
#override
Widget build(BuildContext context) {
return new Container(
color: Colors.red,
height: double.infinity,
width: double.infinity,
child: Center(
child: RaisedButton(
onPressed: () => showMap(),
child: Text("Click me!"),
),
),
);
}