Reputation: 71
I'm using plugin google_maps_flutter 1.2.0, and i want to instanciate a GoogleMapController for testing some bloc's event, where the controller's value can't be null. Unfortunalty, i don't know how to do it, because to my knows, GoogleMapController can't be instanciate outside of the function 'OnCreated' of the GoogleMap() Widget.
GoogleMap(onMapCreated: (GoogleMapController controller) {
// Can only be instanciate here
},
);
It's impossible to do something like this :
GoogleMapController controller = new GoogleMapsController();
I can only do this, and controller is null :
GoogleMapController controller;
I tried multiple thing but nothing is working, can someone help me ?
Upvotes: 7
Views: 7964
Reputation: 343
You have to use dependency injection to accomplish this. Take a look at the example below
Packages:
map_page.feature
Feature: Map
Background:
Given The app is initialized
And The page is displayed
@success
Scenario: User is centered on their location
When I tap {Icons.my_location} button to get my current location
Then The map is centered on my location
di.dart
void setupLocator() {
// Any other injections...
sl.registerFactory<Completer<GoogleMapController>>(
() => Completer<GoogleMapController>(),
);
};
map_page.dart
class MapPage extends StatefulWidget {
const MapPage({super.key});
@override
State<MapPage> createState() => _MapPageState();
}
class _MapPageState extends State<MapPage> {
final Completer<GoogleMapController> _controller =
sl<Completer<GoogleMapController>>();
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocListener<MapBloc, MapState>(
listener: _onGetLocation,
child: GoogleMap(
onMapCreated: (controller) => _controller.complete(controller),
initialCameraPosition: const CameraPosition(
target: LatLng(0, 0),
zoom: 15,
),
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.my_location),
onPressed: () => context.read<MapBloc>().add(GetMyLocation()),
),
);
}
void _onGetLocation(BuildContext context, MapState state) async {
if (state is SuccessGetMyLocation) {
var controller = await _controller.future;
controller.animateCamera(CameraUpdate.newCameraPosition(
CameraPosition(target: state.latLng, zoom: 15),
));
}
}
}
test_helper.dart
@GenerateNiceMocks(<MockSpec>[
// Any other MockSpec...
MockSpec<Completer<GoogleMapController>>(),
MockSpec<GoogleMapController>(),
])
void main() {}
di_helper.dart
void setupDiHelper() {
// Any other injections...
sl.registerLazySingleton<Completer<GoogleMapController>>(
() => MockCompleter(),
);
};
the_app_is_initialized.dart
/// Usage: The app is initialized
Future<void> theAppIsInitialized(WidgetTester tester) async {
PatrolTester $ = PatrolTester(
tester: tester,
config: const PatrolTesterConfig(),
);
await setupDiHelper();
await $.pump();
}
the_page_is_displayed.dart
/// Usage: The page is displayed
Future<void> thePageIsDisplayed(WidgetTester $) async {
await $.pumpWidget(
MultiBlocProvider(
providers: [
BlocProvider<MapBloc>(
create: (context) => sl<MapBloc>(),
),
],
child: MaterialApp(
home: const MapPage(),
),
),
);
}
i_tap_button_to_get_my_current_location.dart
/// Usage: I tap {Icons.my_location} button to get my current location
Future<void> iTapButtonToGetMyCurrentLocation(
WidgetTester $, IconData icon) async {
var state = SuccessGetMyLocation(Position.fromMap(decode(Fixture.position)));
when(sl<MapBloc>().state).thenReturn(state);
// Create a mock GoogleMapController
final mockController = MockGoogleMapController();
// Stub the Completer's future to return the mock controller
final completer = sl<Completer<GoogleMapController>>();
when(completer.future).thenAnswer((_) async => mockController);
// Stub the animateCamera method on the mock controller
when(mockController.animateCamera(any)).thenAnswer((_) async => {});
await $.tap(find.byIcon(icon));
await $.pump();
}
the_map_is_centered_on_my_location.dart
/// Usage: The map is centered on my location
Future<void> theMapIsCenteredOnMyLocation(WidgetTester $) async {
var controller = await sl<Completer<GoogleMapController>>().future;
// Mock the position
var position = Position.fromMap(decode(Fixture.position));
var mockedLatLng = LatLng(position.latitude, position.longitude);
var mockedScreenCoordinate = const ScreenCoordinate(x: 0, y: 0);
// Add a stub for the getLatLng method
when(controller.getLatLng(mockedScreenCoordinate))
.thenAnswer((_) async => mockedLatLng);
var latLng = await controller.getLatLng(mockedScreenCoordinate);
expect(latLng, equals(mockedLatLng));
}
hooks.dart
abstract class Hooks {
const Hooks._();
static FutureOr<void> beforeEach(String title, [List<String>? tags]) {
if (tags?.contains('success') ?? false) {
provideDummy<MapState>(
SuccessGetMyLocation(Position.fromMap(decode(Fixture.position))),
);
} else if (tags?.contains('failure') ?? false) {
}
}
static FutureOr<void> beforeAll() {}
static FutureOr<void> afterEach(
String title,
bool success, [
List<String>? tags,
]) {}
static FutureOr<void> afterAll() {}
}
map_page_test.dart
This file is autogenerated from map_page.feature
using dart run build_runner build
Upvotes: 0
Reputation: 100
you can use like this - In your state:
final Completer<GoogleMapController> _controller =
Completer<GoogleMapController>();
and in you GoogleMap() widget as this
GoogleMap(
myLocationButtonEnabled: false,
zoomControlsEnabled: false,
onMapCreated: (GoogleMapController controller) =>
_googleMapController.complete(controller),
initialCameraPosition: _initialCameraPosition),
Upvotes: 1
Reputation: 51
If you are using a Flutter 2.5.3 and later you can add a late in front of the variable declaration i.e late GoogleMapController _googleMapController;
In your googlemap widget you can use the variable as normal onMapCreated: (controller) => _googleMapController = controller,
Upvotes: 0
Reputation: 1
You can use use _googleMapController? = controller;
before the build method the make a little if statement (_googleMapController != null)
where you want to use your method or what ever you want to do with the instance.
Upvotes: 0
Reputation: 707
First, GoogleMapController _googleMapController
in your _WidgetState
.
Then
GoogleMap(onMapCreated: (GoogleMapController controller) {
_googleMapController = controller;
}
);
After Google Map Created, you can use _googleMapController everywhere in your widget.
Upvotes: 1