I'm using Mapbox in my Flutter app and am having some issues with building out UI the way I want to because of all of the hacks I need to put in place to have bottom sheets and any kind of overlay over a Mapbox map in web. Currently with the setup that I have, you can interact with the map through anything that is in front of it. This includes bottom sheets, Positioned widgets, etc. Here is the setup I'm working with:
import 'dart:ui_web' as ui;
import 'dart:html' as html;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
class WebMapbox extends StatefulWidget {
final double latitude;
final double longitude;
final bool inputFocused;
final bool drawerOpen;
final bool isCreateAnnotation;
final bool forceUpdate;
const WebMapbox({
super.key,
required this.latitude,
required this.longitude,
required this.inputFocused,
required this.drawerOpen,
required this.isCreateAnnotation,
this.forceUpdate = false,
});
@override
WebMapboxState createState() => WebMapboxState();
}
class WebMapboxState extends State<WebMapbox> with TickerProviderStateMixin {
late String iframeId;
bool isDefaultLocation = false;
bool _stateLoaded = false;
html.IFrameElement? iframe; // Changed from late to nullable
bool _sheetOpen = false;
@override
void initState() {
super.initState();
iframeId = _generateIframeId();
_registerHtmlView();
}
@override
void didUpdateWidget(covariant WebMapbox oldWidget) {
super.didUpdateWidget(oldWidget);
if ((oldWidget.latitude != widget.latitude || oldWidget.longitude != widget.longitude || oldWidget.isCreateAnnotation != widget.isCreateAnnotation || oldWidget.forceUpdate != widget.forceUpdate)) {
_sendMapConfiguration();
}
}
String _generateIframeId() {
return 'mapbox-html-view-${DateTime.now().millisecondsSinceEpoch}';
}
void _registerHtmlView() {
final String mapboxHtmlUrl = AppAssets.mapboxPath;
ui.platformViewRegistry.registerViewFactory(iframeId, (int viewId) {
final iframe = html.IFrameElement()
..src = mapboxHtmlUrl
..style.border = 'none'
..style.width = '100%'
..style.height = '100%'
..allow = 'geolocation; accelerometer; gyroscope; magnetometer;'
..onLoad.listen((_) {
_sendMapConfiguration();
});
return iframe;
});
}
void _sendMapConfiguration() {
Future.delayed(Duration(milliseconds: 100), () {
// Try to get the iframe element, but don't reassign if already found
iframe ??= html.document.querySelector('iframe') as html.IFrameElement?;
if (iframe != null) {
iframe!.contentWindow?.postMessage({
'type': 'MAP_CONFIGURATION',
'lat': widget.latitude,
'lng': widget.longitude,
'isDefaultLocation': isDefaultLocation,
'showMarker': widget.isCreateAnnotation,
}, '*');
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
HtmlElementView(
viewType: iframeId
),
if (context.read<HomeBloc>().state.isKeyboardVisible || widget.inputFocused || widget.drawerOpen || _sheetOpen)
PointerInterceptor(
child: GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Container(color: const Color.fromARGB(0, 146, 144, 144)),
))
],
));
}
}
The code at the bottom of the build with the GestureDetector/Pointer interceptor is what I need to put anywhere there is some kind of overlay over this map. UI's need pointer interceptors and this makes it very difficult to build a UI that's similar to mobile. The goal is be to able to interact with the map even when there is a bottom sheet or even some other element like Positioned over the map.
Can anyone give advice as to how they're dealing with Mapbox in Flutter for their web version of their app?