Files
ignis_app/lib/screens/homes_screen.dart
Artem Kokos 8198ea09ae Geofence
2026-04-14 00:02:02 +07:00

203 lines
7.7 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/home_config.dart';
import '../providers/providers.dart';
import 'home_edit_screen.dart';
import 'remote_screen.dart';
/// Экран "Дома" -- список серверов Ignis.
/// Пользователь может добавить, удалить, переключить активный дом.
class HomesScreen extends ConsumerStatefulWidget {
const HomesScreen({super.key});
@override
ConsumerState<HomesScreen> createState() => _HomesScreenState();
}
class _HomesScreenState extends ConsumerState<HomesScreen> {
@override
void initState() {
super.initState();
Future.microtask(() => ref.read(userLocationProvider.notifier).startWatching());
}
@override
void dispose() {
ref.read(userLocationProvider.notifier).stopWatching();
super.dispose();
}
@override
Widget build(BuildContext context) {
final homes = ref.watch(homesProvider);
final currentHome = ref.watch(currentHomeProvider);
final location = ref.watch(userLocationProvider);
return Scaffold(
appBar: AppBar(
title: const Text('ДОМА'),
automaticallyImplyLeading: false,
),
body: homes.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.home_outlined, size: 64, color: Colors.white24),
const SizedBox(height: 16),
const Text(
'Нет добавленных домов',
style: TextStyle(color: Colors.white54, fontSize: 16),
),
const SizedBox(height: 8),
const Text(
'Добавьте сервер Ignis',
style: TextStyle(color: Colors.white38, fontSize: 14),
),
],
),
)
: ListView.builder(
padding: const EdgeInsets.all(12),
itemCount: homes.length,
itemBuilder: (context, index) {
final home = homes[index];
final isActive = currentHome?.id == home.id;
// Расстояние до дома (null если нет координат или геолокации)
final distKm = location.distanceToKm(
home.latitude, home.longitude,
);
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: ListTile(
leading: Icon(
Icons.home,
color: isActive ? Colors.deepOrange : Colors.white38,
size: 28,
),
title: Text(
home.name,
style: TextStyle(
fontWeight: isActive ? FontWeight.bold : FontWeight.normal,
color: isActive ? Colors.deepOrange : Colors.white,
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
home.url,
style: const TextStyle(color: Colors.white38, fontSize: 12),
),
if (distKm != null)
Padding(
padding: const EdgeInsets.only(top: 2),
child: Row(
children: [
const Icon(Icons.near_me, size: 11, color: Colors.white30),
const SizedBox(width: 4),
Text(
'~${formatDistance(distKm)}',
style: const TextStyle(
color: Colors.white30,
fontSize: 11,
),
),
],
),
)
else if (home.hasCoordinates && !location.hasPosition)
// Координаты заданы, но геолокация недоступна
Row(
children: [
const Icon(Icons.location_on, size: 12, color: Colors.white24),
const SizedBox(width: 4),
Text(
location.error ?? 'Координаты заданы',
style: const TextStyle(color: Colors.white24, fontSize: 11),
),
],
),
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
// Кнопка редактирования
IconButton(
icon: const Icon(Icons.edit, size: 20, color: Colors.white38),
onPressed: () => _editHome(context, home),
),
// Кнопка удаления
IconButton(
icon: const Icon(Icons.delete_outline, size: 20, color: Colors.redAccent),
onPressed: () => _confirmDelete(context, home),
),
],
),
onTap: () => _selectHome(context, home),
),
);
},
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.deepOrange,
onPressed: () => _addHome(context),
child: const Icon(Icons.add),
),
);
}
/// Выбрать дом и перейти на пульт
void _selectHome(BuildContext context, HomeConfig home) async {
await ref.read(currentHomeProvider.notifier).switchTo(home);
await ref.read(authInfoProvider.notifier).load();
if (context.mounted) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const RemoteScreen()),
);
}
}
/// Добавить новый дом
void _addHome(BuildContext context) {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => const HomeEditScreen()),
);
}
/// Редактировать дом
void _editHome(BuildContext context, HomeConfig home) {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => HomeEditScreen(home: home)),
);
}
/// Подтвердить удаление
void _confirmDelete(BuildContext context, HomeConfig home) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Удалить дом?'),
content: Text('Удалить "${home.name}" (${home.url})?'),
actions: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(),
child: const Text('Отмена'),
),
TextButton(
onPressed: () async {
Navigator.of(ctx).pop();
await ref.read(homesProvider.notifier).remove(home.id);
// Синхронизировать фоновый таск (мог быть удалён дом с геофенсом)
await syncGeofenceTask(ref.read(homesProvider));
},
child: const Text('Удалить', style: TextStyle(color: Colors.redAccent)),
),
],
),
);
}
}