-
-
Notifications
You must be signed in to change notification settings - Fork 860
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG] Exception "MapCamera
is no longer within the cameraConstraint
after an option change" when using particular camera setups
#1760
Comments
Hi @dumabg, |
MapCamera
is no longer within the cameraConstraint
after an option change"
It happens the same to me. pubspect.yaml: flutter_map: ^6.1.0
flutter_map_cancellable_tile_provider: ^2.0.0
flutter_map_marker_cluster: ^1.3.4 My code: MapOptions _buildMapOptions() => MapOptions(
initialCenter: widget._parametersMap.currentPosition.center,
initialZoom: widget._parametersMap.initialZoom,
maxZoom: widget._parametersMap.maxZoom,
minZoom: widget._parametersMap.minZoom,
initialRotation: widget._parametersMap.rotation,
cameraConstraint: widget._parametersMap.cameraConstraint,
initialCameraFit: markers.isNotEmpty &&
UserPreferences.getInstance().currentPosition == null
? markers.getCameraFit(
minZoom: widget._parametersMap.minZoom,
maxZoom: widget._parametersMap.initialZoom,
)
: null,
keepAlive: true,
onMapReady: () {
// logger.d('onMapReady !!');
},
onPositionChanged: (MapPosition position, bool hasGesture) {
// logger.d('onPositionChanged hasGesture: $hasGesture,
// position: $position');
if (hasGesture) {
ref.read(mapChangedProvider.notifier).changePosition();
}
},
onTap: (_, __) => widget._popupController.hideAllPopups(),
);
@override
Widget build(BuildContext context) {
final markersAsync = ref.watch(markersProvider);
return markersAsync.when(
data: (values) {
if (values.isNotEmpty) {
setState(() {
markers = List.from(values);
});
}
return PopupScope(
popupController: widget._popupController,
child: Stack(
children: [
FlutterMap(
mapController: widget._mapController,
options: _buildMapOptions(),
children: [
...
],
),
);
},
loading: () => const CustomProgress(),
error: (error, stackTrace) {
context.showSnackbar(
message: error.toString(),
type: MessageType.error,
);
return const SizedBox.shrink();
},
);
} Error: MapCamera is no longer within the cameraConstraint after an option change.
'package:flutter_map/src/map/controller/internal.dart':
Failed assertion: line 276 pos 7: 'newOptions.cameraConstraint.constrain(newCamera) == newCamera' What is the reason for the error? |
I can't tell exactly without a more minimal reproducible example, but there's a few things that could be causing issues:
We need an MRE to investigate this further (standalone, no state from other parts of app, no dependencies, etc.). |
@JaffaKetchup @josxha here is a complete single file minimum reproducible example // ignore_for_file: prefer_const_constructors, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int cnt = 0;
@override
void initState() {
super.initState();
Future(() async {
await Future.delayed(Duration(seconds: 1));
cnt++;
setState(() {});
});
}
@override
Widget build(BuildContext context) {
final camConstraint = cnt > 0
? CameraConstraint.contain(
bounds: LatLngBounds(LatLng(43.6884447292, 20.2201924985),
LatLng(48.2208812526, 29.62654341)))
: null;
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: FlutterMap(
options: MapOptions(
interactionOptions: InteractionOptions(
flags: InteractiveFlag.pinchZoom |
InteractiveFlag.drag |
InteractiveFlag.doubleTapZoom |
InteractiveFlag.scrollWheelZoom,
rotationWinGestures: MultiFingerGesture.none,
rotationThreshold: double.infinity,
cursorKeyboardRotationOptions:
CursorKeyboardRotationOptions.disabled()),
minZoom: 1,
maxZoom: 20,
initialZoom: 5.9,
initialCenter: LatLng(45.80565, 24.937853),
cameraConstraint: camConstraint),
children: []));
}
} pubspec.yaml name: flutter_map_constraint_issue
description: "A new Flutter project."
publish_to: "none"
version: 1.0.0+1
environment:
sdk: ">=3.2.5 <4.0.0"
dependencies:
cupertino_icons: ^1.0.2
flutter:
sdk: flutter
flutter_map: ^6.1.0
dev_dependencies:
flutter_lints: ^2.0.0
flutter_test:
sdk: flutter
flutter:
uses-material-design: true the example above produces after 1 second the error ════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building KeyedSubtree-[GlobalKey#de288]:
MapCamera is no longer within the cameraConstraint after an option change.
'package:flutter_map/src/map/controller/internal.dart':
Failed assertion: line 276 pos 7: 'newOptions.cameraConstraint.constrain(newCamera) == newCamera'
The relevant error-causing widget was:
Scaffold Scaffold:file:///D:/temp/flutter_map_constraint_issue/lib/main.dart:56:12 let me know if i can assist or provide anything else. |
Thanks @iulian0512, we'll look into this :) |
any update on this ? In my case it's not just error in console, but map is not displayed at all EDIT: |
MapCamera
is no longer within the cameraConstraint
after an option change"MapCamera
is no longer within the cameraConstraint
after an option change
The issue seems to occur because
Likely the issue is within the |
The issue can be tracked back to the initial implementation in 2c60d63/#1551. It only occurs with Here's some debug output (when running the MRE here: #1760 (comment)). Not sure if I can fix this!
|
The issue also occurs in the following configuration, assuming the constraint is always constrained to the same bounds as the initial fit: but doesn't occur when using @rorystephenson Do you have any insight into what could be happening here? |
I think I found what is going on here. If you look at when you first create the camera when checking constraints it first looks for an existing camera, and if none then it creates a new camera based on the new options. The bug is that the initialCamera method uses the old initalCenter and initalZoom fields which have default data in them.
|
Thanks for investigating @jetpeter! |
MapCamera
is no longer within the cameraConstraint
after an option changeMapCamera
is no longer within the cameraConstraint
after an option change" when using particular camera setups
I think what @jetpeter has found is a part of a much larger issue in that we treat the This is because we have to wait for Flutter to give us some constraints, so we can fit the camera, which can take some time: flutter/flutter#25827. Therefore, what we try to do, is use the initial center and zoom, then re-fit once we get the constraints. However, this causes this issue indirectly - we re-set Therefore, I propose that we change the way @josxha wdyt? |
I just realised that perhaps this is the intended behaviour. In @iulian0512's example above, his initial zoom lies outside the camera constraint - therefore the error is thrown, because the zoom needs to be higher to fit. Can anyone explain what else they would expect in this case? |
@JaffaKetchup From your original thoughts, from what I can see the issue is the other way round: "However, this causes this issue indirectly - we re-set options which triggers this issue," I think the issue is that the first time the options is set the exception is thrown. The map never gets an opportunity to use the initalCameraFit after sizing if the MapCamera.initailCamera is out of cameraConstraints due to initialZoom and initialCenter default values. As it stands right now for usinger with dynamic constraints initialCameraFit is not usable, you must set initialZoom and initialCenter. To compute the initial values as a user just like the map you require widget size, so you have to use a LayoutBuilder to get the size then compute the center zoom from bounds. I have been doing this for a while since older versions of flutter map required this anyway. For new users it is really confusing that there is two different ways of doing initial placement with one being secretly required if constraints are provided, yet the documentation says that they are ignored if the initialCameraFit is provided. It makes sense to throw an exception if the user requested bounds are outside of the initial position. The problem is if you just provided initalCameraFit and not initialCenter and initalZoom even if your initialCameraFit is inside the constraints you will always get an exception if your constraints are outside of the default center zoom of the map options. Flutter map should ideally have a single initalCamerFit field and not initialCenter initialZoom at all. initalCameraFit could have a centerZoom type instead. Having a few milliseconds of default background is probably acceptable, and honestly might not actually be any different than it is now since tiles should not be loaded until the correct position is computed. Hopefully for users providing initialCameraFit the map is not loading tiles for the default values while the widget is still sizing. |
As far as I can see from my testing, it works perfectly fine, if
This shouldn't be the behaviour. Neither should be 'required'. I agree that having non-null defaults on the
I can certainly see this could be a possible issue. Can you post an MRE of your exact situation ( |
When making a test project I realized that the camera fit issue only happens when you provide your own MapController.
pubspec.yaml
|
#1920 suggests it could be fixed by also providing an |
The bug still exists even if the flutter_map: ^7.0.2 error msg: |
The bug still exists even if the initialCenter is inside the specified camera constraint and not init MapController with the latest versions below. |
I've been experimenting with the issue when crashes occur even if initialCenter is within the constraint bounds. If you are zoomed out enough, the constraints may mean that the camera's initial center actually ends up outside the constraint bounds and the crash will occur. The map will try to use the specified initialCenter, but may not be able to do so if this means exposing areas outside the cameraConstraint settings given the zoom level. It will instead center itself the best it can, triggering the crash if the resulting centre of the map is outside the constraint. This is probably the same issue described by jetpeter above. |
I've worked around the issue to setting initialCameraFit to the same bounds as cameraConstraint. This is much safer than setting an initialZoom and initialCenter, as you know the map's resulting centre will be within the same bounds. |
We would appriciate any PRs! This will require some level of rewrite of the camera system, particularly initialisation. It has outgrown its initial requirements, which I believe is why this is happening. A combinination of race conditions and complicated initialisation/setup. |
Have you tried it with an actual device? I'm developing my application with a real device instead of an emulator and I haven't had that problem occur since applying the Actually, the bound stop working when I started using the emulator. But I'm not sure if I'm using |
I was getting the same exception, i had to adjust the initialCenter with respect to the cameraConstraints Like, For this above bounds it works perfectly with initialZoom of 2.3, if I go any less than this, its throws me again MapCamera, is not within cameraConstraint error. Here is my full code
|
@DevTello - here is the relevant part of my current options: That's all I use, which avoids the crash. You may find the inclusion of the min zoom settings are causing the crash by making the resulting camera center end up outside the cameraConstraint bounds. The cameraConstraint by itself will cap the min zoom level to whatever is required to make the bounding box visible as a whole, while preventing you from being able to zoom out further. Setting max zoom should not be an issue. I didn't know you could inset the initialCameraFit, thanks for that! |
What is the bug?
A FlutterMap with a cameraConstraint CameraConstraint.contain(bounds: widget.bounds) gives the assert error "MapCamera is no longer within the cameraConstraint after an option change"
How can we reproduce it?
Put a ValueListenableBuilder and a FlutterMap with a cameraConstraint. Change the ValueListenableBuilder value to trigger a new repaint.
Do you have a potential solution?
I've comment the assert and seems that it works
Platforms
Android
Severity
Erroneous: Prevents normal functioning and causes errors in the console
The text was updated successfully, but these errors were encountered: