feat(app): смена сервера API и очистка данных устройства при выходе

Адрес бэкенда хранится в shared_preferences и читается до runApp; switchApiBaseUrl сбрасывает токены и кэш старого сервера. Выход стирает refresh-токен и sqlite-кэш ответов. Экран настроек переделан под разделы.
This commit is contained in:
Dmitry
2026-09-19 22:14:14 +03:00
parent bfe74ca47c
commit a559d6de3e
17 changed files with 887 additions and 135 deletions
+36 -5
View File
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../core/auth/auth_controller.dart';
import '../../core/config.dart';
import '../settings/api_base_url_dialog.dart';
class LoginPage extends ConsumerStatefulWidget {
const LoginPage({super.key});
@@ -55,9 +56,28 @@ class _LoginPageState extends ConsumerState<LoginPage> {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('fin-tracker', style: Theme.of(context).textTheme.headlineMedium),
Text(
'fin-tracker',
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 4),
Text(apiBaseUrl(), style: Theme.of(context).textTheme.bodySmall),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
child: Text(
ref.watch(apiBaseUrlProvider),
style: Theme.of(context).textTheme.bodySmall,
overflow: TextOverflow.ellipsis,
),
),
IconButton(
icon: const Icon(Icons.edit_outlined, size: 16),
tooltip: 'Изменить адрес',
onPressed: () => showApiBaseUrlDialog(context),
),
],
),
const SizedBox(height: 24),
TextField(
controller: _email,
@@ -71,18 +91,29 @@ class _LoginPageState extends ConsumerState<LoginPage> {
autofillHints: const [AutofillHints.password],
obscureText: true,
onSubmitted: (_) => _busy ? null : _submit(),
decoration: const InputDecoration(labelText: 'Пароль'),
decoration: const InputDecoration(
labelText: 'Пароль',
),
),
if (_error != null) ...[
const SizedBox(height: 12),
Text(_error!, style: TextStyle(color: Theme.of(context).colorScheme.error)),
Text(
_error!,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
),
),
],
const SizedBox(height: 24),
FilledButton(
onPressed: _busy ? null : _submit,
child: _busy
? const SizedBox.square(
dimension: 18, child: CircularProgressIndicator(strokeWidth: 2))
dimension: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
),
)
: const Text('Войти'),
),
],