import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../domain/models/floor_plan_mode.dart'; import '../../../providers.dart'; import '../../floorplan/widgets/konva_web_view.dart'; class StepFloorPlan extends ConsumerStatefulWidget { const StepFloorPlan({super.key, required this.onComplete}); final VoidCallback onComplete; @override ConsumerState createState() => _StepFloorPlanState(); } class _StepFloorPlanState extends ConsumerState { final _konvaKey = GlobalKey(); @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { ref.read(roomsProvider.future).then( (rooms) => _konvaKey.currentState?.loadFloorPlan(rooms), ); _konvaKey.currentState?.setMode(FloorPlanMode.edit); }); } @override Widget build(BuildContext context) { ref.listen(roomsProvider, (_, next) { next.whenData((r) => _konvaKey.currentState?.loadFloorPlan(r)); }); return Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(24, 16, 24, 8), child: Text('Draw floor plan', style: Theme.of(context).textTheme.titleLarge), ), Expanded( child: KonvaWebView( key: _konvaKey, mode: FloorPlanMode.edit, onSensorTapped: (_) {}, onSensorMoved: (_, __, ___, ____) {}, onRoomsUpdated: (roomUpdates) async { final floor = ref.read(floorProvider).valueOrNull; if (floor == null) return; final repo = ref.read(floorRepositoryProvider); for (final r in roomUpdates) { await repo.updateRoom(floor.id, r.id, x: r.x, y: r.y); } ref.invalidate(roomsProvider); }, onRoomAdded: (x, y) => _showAddRoomDialog(x, y), ), ), Padding( padding: const EdgeInsets.all(24), child: FilledButton( onPressed: widget.onComplete, child: const Text('Continue'), ), ), ], ); } Future _showAddRoomDialog(double x, double y) async { final nameCtrl = TextEditingController(); final widthCtrl = TextEditingController(text: '5.0'); final heightCtrl = TextEditingController(text: '4.0'); final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('Add room'), content: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: nameCtrl, decoration: const InputDecoration(labelText: 'Name'), autofocus: true, onSubmitted: (_) => Navigator.of(ctx).pop(true), ), const SizedBox(height: 8), Row( children: [ Expanded( child: TextField( controller: widthCtrl, decoration: const InputDecoration( labelText: 'Width', suffixText: 'm'), keyboardType: const TextInputType.numberWithOptions( decimal: true), ), ), const SizedBox(width: 8), Expanded( child: TextField( controller: heightCtrl, decoration: const InputDecoration( labelText: 'Height', suffixText: 'm'), keyboardType: const TextInputType.numberWithOptions( decimal: true), ), ), ], ), ], ), actions: [ TextButton( onPressed: () => Navigator.of(ctx).pop(false), child: const Text('Cancel'), ), FilledButton( onPressed: () => Navigator.of(ctx).pop(true), child: const Text('Add'), ), ], ), ); final name = nameCtrl.text.trim(); final width = double.tryParse(widthCtrl.text) ?? 5.0; final height = double.tryParse(heightCtrl.text) ?? 4.0; nameCtrl.dispose(); widthCtrl.dispose(); heightCtrl.dispose(); if (confirmed != true || !mounted) return; if (name.isEmpty) return; var floor = ref.read(floorProvider).valueOrNull; if (floor == null) { floor = await ref .read(floorRepositoryProvider) .createFloor(name: 'Ground Floor'); ref.invalidate(floorProvider); } await ref.read(floorRepositoryProvider).createRoom( floor.id, name: name, width: width, height: height, x: x, y: y, ); ref.invalidate(roomsProvider); } }