feat: implement Flutter side of floor plan screen and widget
This commit is contained in:
@@ -3,10 +3,11 @@ import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
import '../../../domain/models/floor.dart';
|
||||
import '../../../domain/models/floor_plan_mode.dart';
|
||||
import '../../../domain/models/tag.dart';
|
||||
import '../../../domain/models/particle.dart';
|
||||
import '../../../domain/models/position.dart';
|
||||
import '../../../domain/models/sensor.dart';
|
||||
|
||||
class KonvaWebView extends StatefulWidget {
|
||||
const KonvaWebView({
|
||||
@@ -14,11 +15,15 @@ class KonvaWebView extends StatefulWidget {
|
||||
required this.mode,
|
||||
required this.onSensorTapped,
|
||||
required this.onSensorMoved,
|
||||
this.onRoomsUpdated,
|
||||
this.onRoomAdded,
|
||||
});
|
||||
|
||||
final FloorPlanMode mode;
|
||||
final void Function(String sensorId) onSensorTapped;
|
||||
final void Function(String sensorId, Position newPosition) onSensorMoved;
|
||||
final void Function(String sensorId, int roomId, double x, double y) onSensorMoved;
|
||||
final void Function(List<Room> rooms)? onRoomsUpdated;
|
||||
final void Function(double x, double y)? onRoomAdded;
|
||||
|
||||
@override
|
||||
State<KonvaWebView> createState() => KonvaWebViewState();
|
||||
@@ -26,6 +31,8 @@ class KonvaWebView extends StatefulWidget {
|
||||
|
||||
class KonvaWebViewState extends State<KonvaWebView> {
|
||||
late final WebViewController _controller;
|
||||
bool _ready = false;
|
||||
final _pending = <Future<void> Function()>[];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -33,9 +40,26 @@ class KonvaWebViewState extends State<KonvaWebView> {
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..addJavaScriptChannel('FlutterBridge', onMessageReceived: _onMessage)
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(onPageFinished: (_) => _onPageReady()),
|
||||
)
|
||||
..loadFlutterAsset('assets/konva/index.html');
|
||||
}
|
||||
|
||||
void _onPageReady() {
|
||||
_ready = true;
|
||||
for (final call in _pending) {
|
||||
call();
|
||||
}
|
||||
_pending.clear();
|
||||
}
|
||||
|
||||
Future<void> _run(Future<void> Function() call) {
|
||||
if (_ready) return call();
|
||||
_pending.add(call);
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
void _onMessage(JavaScriptMessage message) {
|
||||
final data = jsonDecode(message.message) as Map<String, dynamic>;
|
||||
switch (data['type'] as String?) {
|
||||
@@ -44,37 +68,99 @@ class KonvaWebViewState extends State<KonvaWebView> {
|
||||
case 'sensorMoved':
|
||||
widget.onSensorMoved(
|
||||
data['id'] as String,
|
||||
Position(
|
||||
x: (data['x'] as num).toDouble(),
|
||||
y: (data['y'] as num).toDouble(),
|
||||
),
|
||||
(data['roomId'] as num).toInt(),
|
||||
(data['x'] as num).toDouble(),
|
||||
(data['y'] as num).toDouble(),
|
||||
);
|
||||
case 'roomsUpdated':
|
||||
final rooms = (data['rooms'] as List)
|
||||
.map((r) => Room.fromJson(r as Map<String, dynamic>))
|
||||
.toList();
|
||||
widget.onRoomsUpdated?.call(rooms);
|
||||
case 'roomAdded':
|
||||
widget.onRoomAdded?.call(
|
||||
(data['x'] as num).toDouble(),
|
||||
(data['y'] as num).toDouble(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateTags(List<TagPosition> positions) async {
|
||||
final payload = jsonEncode(positions
|
||||
.map((p) => {'tagId': p.tagId, 'x': p.position.x, 'y': p.position.y})
|
||||
.toList());
|
||||
await _controller.runJavaScript('window.companion.updateTags($payload)');
|
||||
Future<void> loadFloorPlan(List<Room> rooms) {
|
||||
return _run(() async {
|
||||
final payload = jsonEncode(rooms
|
||||
.map((r) => {
|
||||
'id': r.id,
|
||||
'name': r.name,
|
||||
'x': r.x,
|
||||
'y': r.y,
|
||||
'width': r.width,
|
||||
'height': r.height,
|
||||
})
|
||||
.toList());
|
||||
await _controller
|
||||
.runJavaScript('window.companion.loadFloorPlan($payload)');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> updateParticleCloud(List<Particle> particles) async {
|
||||
final payload = jsonEncode(particles
|
||||
.map((p) => {'x': p.x, 'y': p.y, 'weight': p.weight})
|
||||
.toList());
|
||||
await _controller.runJavaScript('window.companion.updateCloud($payload)');
|
||||
Future<void> loadSensors(List<Sensor> sensors) {
|
||||
return _run(() async {
|
||||
final payload = jsonEncode(sensors
|
||||
.map((s) => {
|
||||
'id': s.id,
|
||||
'sensor_id': s.sensorId,
|
||||
'name': s.name,
|
||||
'floor_x': s.x,
|
||||
'floor_y': s.y,
|
||||
'room_id': s.roomId,
|
||||
})
|
||||
.toList());
|
||||
await _controller
|
||||
.runJavaScript('window.companion.loadSensors($payload)');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> highlightSensor(String? sensorId) async {
|
||||
final id = sensorId == null ? 'null' : '"$sensorId"';
|
||||
await _controller.runJavaScript('window.companion.highlightSensor($id)');
|
||||
Future<void> updateTags(List<TagPosition> positions) {
|
||||
return _run(() async {
|
||||
final payload = jsonEncode(positions
|
||||
.map((p) => {
|
||||
'tagId': p.tagId,
|
||||
'roomId': p.roomId,
|
||||
'name': p.name,
|
||||
'color': p.color,
|
||||
})
|
||||
.toList());
|
||||
await _controller.runJavaScript('window.companion.updateTags($payload)');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> setMode(FloorPlanMode mode) async {
|
||||
await _controller.runJavaScript(
|
||||
'window.companion.setMode("${mode.name}")',
|
||||
);
|
||||
Future<void> updateParticleCloud(List<Particle> particles) {
|
||||
return _run(() async {
|
||||
final payload = jsonEncode(particles
|
||||
.map((p) => {'x': p.x, 'y': p.y, 'weight': p.weight})
|
||||
.toList());
|
||||
await _controller.runJavaScript('window.companion.updateCloud($payload)');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> highlightSensor(String? sensorId) {
|
||||
return _run(() async {
|
||||
final id = sensorId == null ? 'null' : jsonEncode(sensorId);
|
||||
await _controller
|
||||
.runJavaScript('window.companion.highlightSensor($id)');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> setMode(FloorPlanMode mode) {
|
||||
return _run(() async {
|
||||
await _controller
|
||||
.runJavaScript('window.companion.setMode("${mode.name}")');
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> addRoom() {
|
||||
return _run(() async {
|
||||
await _controller.runJavaScript('window.companion.addRoom()');
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
Reference in New Issue
Block a user