oc-front/lib/pages/map.dart
2025-02-18 15:05:19 +01:00

228 lines
8.0 KiB
Dart

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:go_router/go_router.dart';
import 'package:latlong2/latlong.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:oc_front/core/services/specialized_services/datacenter_service.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/models/resources/compute.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/models/resources/storage.dart';
import 'package:oc_front/pages/abstract_page.dart';
import 'package:oc_front/widgets/items/item_row.dart';
class MapFactory implements AbstractFactory {
@override GlobalKey getKey() { return key; }
@override String? getSearch() { return ""; }
@override void back(BuildContext context) { }
static GlobalKey<MapPageWidgetState> key = GlobalKey<MapPageWidgetState>();
@override bool searchFill() { return false; }
@override Widget factory(GoRouterState state, List<String> args) { return MapPageWidget(); }
@override void search(BuildContext context, bool special) { }
}
double menuSize = 0;
class MapPageWidget extends StatefulWidget {
bool isShowed = false;
final DatacenterService _service = DatacenterService();
MapPageWidget(): super(key: MapFactory.key);
@override MapPageWidgetState createState() => MapPageWidgetState();
static void search(BuildContext context) { }
static Widget factory() { return MapPageWidget(); }
}
class MapPageWidgetState extends State<MapPageWidget> {
double currentZoom = 2.0;
LatLng currentCenter = const LatLng(51.5, -0.09);
static final MapController _mapController = MapController();
bool selected = true;
@override Widget build(BuildContext context) {
return FutureBuilder(future: widget._service.all(context), builder: (BuildContext context, AsyncSnapshot snapshot) {
Map<String, Map<AbstractItem, LatLng>> coordinates = {};
List<Marker> markerCoordinates = [];
if (snapshot.data != null&& snapshot.data!.data != null) {
for (var element in snapshot.data!.data!.values) {
if (element["type"] == "storage") {
StorageItem resource = StorageItem().deserialize(element);
var instance = resource.getSelectedInstance();
if (instance == null || instance.location == null) { continue; }
if (coordinates[resource.topic] == null) { coordinates[resource.topic] = {}; }
coordinates[resource.topic]![resource] = LatLng(instance.location!.latitude ?? 0, instance.location!.longitude ?? 0);
} else if (element["type"] == "compute") {
ComputeItem resource = ComputeItem().deserialize(element);
var instance = resource.getSelectedInstance();
if (instance == null || instance.location == null) { continue; }
if (coordinates[resource.topic] == null) { coordinates[resource.topic] = {}; }
var lr = Random().nextInt(max(1, 100)) / 10000;
var lfr = Random().nextInt(max(1, 100)) / 10000;
coordinates[resource.topic]![resource] = LatLng((instance.location!.latitude ?? 0) + lr, (instance.location!.longitude ?? 0) + lfr);
}
}
for (var topic in coordinates.keys) {
for (var coord in coordinates[topic]!.keys) {
markerCoordinates.add(Marker(
width: 25,
height: 30,
point: coordinates[topic]![coord]!,
child: HoverMenu( width: 110, title: Container( alignment: Alignment.center,
constraints: BoxConstraints( maxHeight: 100, maxWidth: 100 ),
child: Icon(FontAwesomeIcons.locationDot,
shadows: <Shadow>[Shadow(color: Color.fromRGBO(0, 0, 0, 1), blurRadius: 10.0)],
color: getColor(topic)) ),
items: [ Container(color: Colors.white,
child: ItemRowWidget(low: true, contextWidth: 290, item: coord)) ]
),
));
}
}
Rect rect = Rect.fromCenter( center: MediaQuery.of(context).size.center(Offset.zero),
width: selected ? menuSize : 0, height: (getMainHeight(context) - 50) > 0 ? (getMainHeight(context) - 50) : 0);
return Expanded(
child : FlutterMap(
mapController: _mapController,
options: MapOptions(
initialCenter: currentCenter,
initialZoom: currentZoom,
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'dev.fleaflet.flutter_map.example',
),
MarkerLayer(
markers: markerCoordinates,
),
],
),
);
} else {
return const CircularProgressIndicator();
}
}
);
}
}
class HoverMenuController {
HoverMenuState? currentState;
void hideSubMenu() {
currentState?.hideSubMenu();
}
}
class HoverMenu extends StatefulWidget {
final Widget title;
final double? width;
final List<Widget> items;
final HoverMenuController? controller;
bool isHovered = false;
HoverMenu({
Key? key,
required this.title,
this.items = const [],
this.width,
this.controller,
}) : super(key: key);
@override
HoverMenuState createState() => HoverMenuState();
}
class HoverMenuState extends State<HoverMenu> {
OverlayEntry? _overlayEntry;
final _focusNode = FocusNode();
@override
void initState() {
super.initState();
_focusNode.addListener(_onFocusChanged);
if (widget.controller != null) {
widget.controller?.currentState = this;
}
}
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
void _onFocusChanged() {
if (_focusNode.hasFocus) {
_overlayEntry = _createOverlayEntry();
Overlay.of(context).insert(_overlayEntry!);
} else {
_overlayEntry?.remove();
_removeOverlay();
}
}
void _removeOverlay() {
widget.isHovered = false;
}
void hideSubMenu() {
_focusNode.unfocus();
}
@override
Widget build(BuildContext context) {
return TextButton(
style: TextButton.styleFrom(
elevation: 0.0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)),
splashFactory: NoSplash.splashFactory,
padding: EdgeInsets.zero,
foregroundColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
),
focusNode: _focusNode,
onHover: (isHovered) {
if (isHovered && !widget.isHovered) {
_focusNode.requestFocus();
isHovered = true;
} else {
_focusNode.unfocus();
widget.isHovered = false;
}
},
onPressed: () {},
child: widget.title,
);
}
OverlayEntry _createOverlayEntry() {
final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
maintainState: true,
builder: (context) => Positioned(
left: offset.dx - 300,
top: offset.dy + size.height,
width: 300,
child: TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)),
splashFactory: NoSplash.splashFactory,
backgroundColor: Colors.transparent,
),
onPressed: () {},
child: ListView(
padding: EdgeInsets.zero,
shrinkWrap: true,
children: widget.items)),
),
);
}
}