test: expand client-side coverage and fix lifecycle issues

This commit is contained in:
Artem Kokos
2026-05-01 10:12:33 +07:00
parent 2fa89f6be0
commit 8f0753c1e2
10 changed files with 1116 additions and 43 deletions

View File

@@ -22,7 +22,7 @@ class _ApiKeysScreenState extends ConsumerState<ApiKeysScreen> {
@override
void initState() {
super.initState();
_load();
Future<void>.microtask(_load);
}
Future<void> _load() async {
@@ -132,44 +132,46 @@ class _ApiKeysScreenState extends ConsumerState<ApiKeysScreen> {
builder: (ctx) => StatefulBuilder(
builder: (ctx, setDialogState) => AlertDialog(
title: const Text('Новый API-ключ'),
content: Form(
key: formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: nameCtrl,
decoration: const InputDecoration(
labelText: 'Имя ключа',
hintText: 'Например: Гость',
content: SingleChildScrollView(
child: Form(
key: formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: nameCtrl,
decoration: const InputDecoration(
labelText: 'Имя ключа',
hintText: 'Например: Гость',
),
autofocus: true,
validator: (value) {
final normalized = value?.trim() ?? '';
if (normalized.isEmpty) {
return 'Укажите имя ключа';
}
if (normalized.length < 2) {
return 'Слишком короткое имя';
}
return null;
},
),
autofocus: true,
validator: (value) {
final normalized = value?.trim() ?? '';
if (normalized.isEmpty) {
return 'Укажите имя ключа';
}
if (normalized.length < 2) {
return 'Слишком короткое имя';
}
return null;
},
),
const SizedBox(height: 12),
SwitchListTile(
title: const Text('Дать права администратора'),
subtitle: const Text(
'Используйте только для доверенных людей',
const SizedBox(height: 12),
SwitchListTile(
title: const Text('Дать права администратора'),
subtitle: const Text(
'Используйте только для доверенных людей',
),
value: isAdmin,
activeThumbColor: Colors.deepOrange,
onChanged: isCreating
? null
: (value) => setDialogState(() => isAdmin = value),
contentPadding: EdgeInsets.zero,
),
value: isAdmin,
activeThumbColor: Colors.deepOrange,
onChanged: isCreating
? null
: (value) => setDialogState(() => isAdmin = value),
contentPadding: EdgeInsets.zero,
),
],
],
),
),
),
actions: [
@@ -232,7 +234,9 @@ class _ApiKeysScreenState extends ConsumerState<ApiKeysScreen> {
),
);
nameCtrl.dispose();
WidgetsBinding.instance.addPostFrameCallback((_) {
nameCtrl.dispose();
});
}
Future<void> _revokeKey(ApiKeyInfo data) async {

View File

@@ -22,15 +22,18 @@ class RemoteScreen extends ConsumerStatefulWidget {
}
class _RemoteScreenState extends ConsumerState<RemoteScreen> {
late final GroupsNotifier _groupsNotifier;
@override
void initState() {
super.initState();
Future.microtask(() => ref.read(groupsProvider.notifier).startPolling());
_groupsNotifier = ref.read(groupsProvider.notifier);
Future.microtask(_groupsNotifier.startPolling);
}
@override
void dispose() {
ref.read(groupsProvider.notifier).stopPolling();
_groupsNotifier.stopPolling(resetStatus: false);
super.dispose();
}

View File

@@ -21,7 +21,7 @@ class _SchedulesScreenState extends ConsumerState<SchedulesScreen> {
@override
void initState() {
super.initState();
_load();
Future<void>.microtask(_load);
}
Future<void> _load() async {