feat: secure home credentials

This commit is contained in:
Artem Kokos
2026-04-22 23:25:48 +07:00
parent 6a961209cc
commit 7c0a2675c6
22 changed files with 1782 additions and 397 deletions

View File

@@ -55,9 +55,9 @@ class _RemoteScreenState extends ConsumerState<RemoteScreen> {
IconButton(
icon: const Icon(Icons.add_circle_outline),
tooltip: 'Создать группу',
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(builder: (_) => const GroupEditScreen()),
),
onPressed: () => Navigator.of(
context,
).push(MaterialPageRoute(builder: (_) => const GroupEditScreen())),
),
// Меню
PopupMenuButton<String>(
@@ -139,64 +139,72 @@ class _RemoteScreenState extends ConsumerState<RemoteScreen> {
),
)
: groups.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.lightbulb_outline, size: 64, color: Colors.white24),
const SizedBox(height: 16),
const Text(
'Нет групп',
style: TextStyle(color: Colors.white54, fontSize: 16),
),
const SizedBox(height: 8),
TextButton.icon(
icon: const Icon(Icons.add),
label: const Text('Создать группу'),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => const GroupEditScreen()),
),
),
],
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.lightbulb_outline,
size: 64,
color: Colors.white24,
),
)
: RefreshIndicator(
color: Colors.deepOrange,
onRefresh: () =>
ref.read(groupsProvider.notifier).refresh(),
child: ListView.builder(
padding: const EdgeInsets.only(top: 8, bottom: 80),
itemCount: groups.length,
itemBuilder: (context, index) {
final g = Map<String, dynamic>.from(groups[index]);
return Dismissible(
key: Key(g['id'].toString()),
direction: DismissDirection.endToStart,
background: Container(
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
margin: const EdgeInsets.symmetric(
horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.redAccent.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(16),
),
child: const Icon(Icons.delete, color: Colors.redAccent),
),
confirmDismiss: (_) => _confirmDeleteGroup(context, g),
onDismissed: (_) => _deleteGroup(g['id'].toString()),
child: GroupCard(group: g),
);
},
const SizedBox(height: 16),
const Text(
'Нет групп',
style: TextStyle(color: Colors.white54, fontSize: 16),
),
),
const SizedBox(height: 8),
TextButton.icon(
icon: const Icon(Icons.add),
label: const Text('Создать группу'),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => const GroupEditScreen(),
),
),
),
],
),
)
: RefreshIndicator(
color: Colors.deepOrange,
onRefresh: () => ref.read(groupsProvider.notifier).refresh(),
child: ListView.builder(
padding: const EdgeInsets.only(top: 8, bottom: 80),
itemCount: groups.length,
itemBuilder: (context, index) {
final g = Map<String, dynamic>.from(groups[index]);
return Dismissible(
key: Key(g['id'].toString()),
direction: DismissDirection.endToStart,
background: Container(
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
margin: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: Colors.redAccent.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(16),
),
child: const Icon(Icons.delete, color: Colors.redAccent),
),
confirmDismiss: (_) => _confirmDeleteGroup(context, g),
onDismissed: (_) => _deleteGroup(g['id'].toString()),
child: GroupCard(group: g),
);
},
),
),
);
}
/// Подтверждение удаления группы свайпом
Future<bool> _confirmDeleteGroup(
BuildContext context, Map<String, dynamic> g) async {
BuildContext context,
Map<String, dynamic> g,
) async {
return await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
@@ -209,8 +217,10 @@ class _RemoteScreenState extends ConsumerState<RemoteScreen> {
),
TextButton(
onPressed: () => Navigator.of(ctx).pop(true),
child: const Text('Удалить',
style: TextStyle(color: Colors.redAccent)),
child: const Text(
'Удалить',
style: TextStyle(color: Colors.redAccent),
),
),
],
),
@@ -224,9 +234,9 @@ class _RemoteScreenState extends ConsumerState<RemoteScreen> {
await ref.read(groupsProvider.notifier).refresh();
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Ошибка удаления: $e')),
);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Ошибка удаления: $e')));
}
}
}