|
|
|
@ -302,10 +302,12 @@ class _ChannelMessagePathMapScreenState
|
|
|
|
extends State<ChannelMessagePathMapScreen> {
|
|
|
|
extends State<ChannelMessagePathMapScreen> {
|
|
|
|
static const double _labelZoomThreshold = 8.5;
|
|
|
|
static const double _labelZoomThreshold = 8.5;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final MapController _mapController = MapController();
|
|
|
|
Uint8List? _selectedPath;
|
|
|
|
Uint8List? _selectedPath;
|
|
|
|
double _pathDistance = 0.0;
|
|
|
|
double _pathDistance = 0.0;
|
|
|
|
bool _showNodeLabels = true;
|
|
|
|
bool _showNodeLabels = true;
|
|
|
|
bool _didReceivePositionUpdate = false;
|
|
|
|
bool _didReceivePositionUpdate = false;
|
|
|
|
|
|
|
|
int? _focusedHopIndex;
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
void initState() {
|
|
|
|
@ -336,6 +338,22 @@ class _ChannelMessagePathMapScreenState
|
|
|
|
return totalDistance;
|
|
|
|
return totalDistance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void _focusHop(_PathHop hop) {
|
|
|
|
|
|
|
|
if (!hop.hasLocation) return;
|
|
|
|
|
|
|
|
final targetZoom = _didReceivePositionUpdate
|
|
|
|
|
|
|
|
? max(_mapController.camera.zoom, 14.0)
|
|
|
|
|
|
|
|
: 14.0;
|
|
|
|
|
|
|
|
_mapController.move(hop.position!, targetZoom);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void _onHopTapped(_PathHop hop) {
|
|
|
|
|
|
|
|
_focusHop(hop);
|
|
|
|
|
|
|
|
if (!mounted) return;
|
|
|
|
|
|
|
|
setState(() {
|
|
|
|
|
|
|
|
_focusedHopIndex = hop.index;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return Consumer<MeshCoreConnector>(
|
|
|
|
return Consumer<MeshCoreConnector>(
|
|
|
|
@ -419,6 +437,7 @@ class _ChannelMessagePathMapScreenState
|
|
|
|
children: [
|
|
|
|
children: [
|
|
|
|
FlutterMap(
|
|
|
|
FlutterMap(
|
|
|
|
key: mapKey,
|
|
|
|
key: mapKey,
|
|
|
|
|
|
|
|
mapController: _mapController,
|
|
|
|
options: MapOptions(
|
|
|
|
options: MapOptions(
|
|
|
|
initialCenter: initialCenter,
|
|
|
|
initialCenter: initialCenter,
|
|
|
|
initialZoom: initialZoom,
|
|
|
|
initialZoom: initialZoom,
|
|
|
|
@ -470,6 +489,7 @@ class _ChannelMessagePathMapScreenState
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
setState(() {
|
|
|
|
setState(() {
|
|
|
|
_selectedPath = observedPaths[index].pathBytes;
|
|
|
|
_selectedPath = observedPaths[index].pathBytes;
|
|
|
|
|
|
|
|
_focusedHopIndex = null;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
if (points.isEmpty)
|
|
|
|
if (points.isEmpty)
|
|
|
|
@ -725,8 +745,17 @@ class _ChannelMessagePathMapScreenState
|
|
|
|
separatorBuilder: (_, _) => const Divider(height: 1),
|
|
|
|
separatorBuilder: (_, _) => const Divider(height: 1),
|
|
|
|
itemBuilder: (context, index) {
|
|
|
|
itemBuilder: (context, index) {
|
|
|
|
final hop = hops[index];
|
|
|
|
final hop = hops[index];
|
|
|
|
|
|
|
|
final isFocused = _focusedHopIndex == hop.index;
|
|
|
|
return ListTile(
|
|
|
|
return ListTile(
|
|
|
|
dense: true,
|
|
|
|
dense: true,
|
|
|
|
|
|
|
|
enabled: hop.hasLocation,
|
|
|
|
|
|
|
|
selected: isFocused,
|
|
|
|
|
|
|
|
selectedTileColor: Theme.of(
|
|
|
|
|
|
|
|
context,
|
|
|
|
|
|
|
|
).colorScheme.primary.withValues(alpha: 0.12),
|
|
|
|
|
|
|
|
onTap: hop.hasLocation
|
|
|
|
|
|
|
|
? () => _onHopTapped(hop)
|
|
|
|
|
|
|
|
: null,
|
|
|
|
leading: CircleAvatar(
|
|
|
|
leading: CircleAvatar(
|
|
|
|
radius: 14,
|
|
|
|
radius: 14,
|
|
|
|
child: Text(
|
|
|
|
child: Text(
|
|
|
|
|