feat: type schedule and auth models

This commit is contained in:
Artem Kokos
2026-04-23 20:57:15 +07:00
parent fa403bfcce
commit 0fdaf0bac4
11 changed files with 531 additions and 243 deletions

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/home_config.dart';
import '../providers/providers.dart';
import '../widgets/build_info_text.dart';
import 'home_edit_screen.dart';
import 'remote_screen.dart';
@@ -41,135 +42,81 @@ class _HomesScreenState extends ConsumerState<HomesScreen> {
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;
body: Column(
children: [
Expanded(
child: homes.isEmpty
? const _EmptyHomesView()
: ListView.builder(
padding: const EdgeInsets.all(12),
itemCount: homes.length,
itemBuilder: (context, index) {
final home = homes[index];
final isActive = currentHome?.id == home.id;
final distKm = location.distanceToKm(
home.latitude,
home.longitude,
);
// Расстояние до дома (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,
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: ListTile(
leading: Icon(
Icons.home,
color: isActive
? Colors.deepOrange
: Colors.white38,
size: 28,
),
),
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,
),
),
],
title: Text(
home.name,
style: TextStyle(
fontWeight: isActive
? FontWeight.bold
: FontWeight.normal,
color: isActive
? Colors.deepOrange
: Colors.white,
),
)
else if (home.hasCoordinates && !location.hasPosition)
// Координаты заданы, но геолокация недоступна
Row(
),
subtitle: _HomeSubtitle(
home: home,
location: location,
distKm: distKm,
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
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,
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),
),
],
),
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
// Кнопка редактирования
IconButton(
icon: const Icon(
Icons.edit,
size: 20,
color: Colors.white38,
),
onPressed: () => _editHome(context, home),
onTap: () => _selectHome(context, home),
),
// Кнопка удаления
IconButton(
icon: const Icon(
Icons.delete_outline,
size: 20,
color: Colors.redAccent,
),
onPressed: () => _confirmDelete(context, home),
),
],
),
onTap: () => _selectHome(context, home),
);
},
),
);
},
),
),
const Padding(
padding: EdgeInsets.only(bottom: 10),
child: BuildInfoText(),
),
],
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.deepOrange,
onPressed: () => _addHome(context),
@@ -178,7 +125,6 @@ class _HomesScreenState extends ConsumerState<HomesScreen> {
);
}
/// Выбрать дом и перейти на пульт
void _selectHome(BuildContext context, HomeConfig home) async {
try {
await ref.read(currentHomeProvider.notifier).switchTo(home);
@@ -197,21 +143,18 @@ class _HomesScreenState extends ConsumerState<HomesScreen> {
}
}
/// Добавить новый дом
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,
@@ -227,7 +170,6 @@ class _HomesScreenState extends ConsumerState<HomesScreen> {
onPressed: () async {
Navigator.of(ctx).pop();
await ref.read(homesProvider.notifier).remove(home.id);
// Синхронизировать фоновый таск (мог быть удалён дом с геофенсом)
await syncGeofenceTask(ref.read(homesProvider));
},
child: const Text(
@@ -240,3 +182,79 @@ class _HomesScreenState extends ConsumerState<HomesScreen> {
);
}
}
class _EmptyHomesView extends StatelessWidget {
const _EmptyHomesView();
@override
Widget build(BuildContext context) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.home_outlined, size: 64, color: Colors.white24),
SizedBox(height: 16),
Text(
'Нет добавленных домов',
style: TextStyle(color: Colors.white54, fontSize: 16),
),
SizedBox(height: 8),
Text(
'Добавьте сервер Ignis',
style: TextStyle(color: Colors.white38, fontSize: 14),
),
],
),
);
}
}
class _HomeSubtitle extends StatelessWidget {
final HomeConfig home;
final UserLocation location;
final double? distKm;
const _HomeSubtitle({
required this.home,
required this.location,
required this.distKm,
});
@override
Widget build(BuildContext context) {
return 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),
),
],
),
],
);
}
}