Files
fin-tracker/app/lib/features/settings/api_base_url_dialog.dart
T
Dmitry a559d6de3e feat(app): смена сервера API и очистка данных устройства при выходе
Адрес бэкенда хранится в shared_preferences и читается до runApp; switchApiBaseUrl сбрасывает токены и кэш старого сервера. Выход стирает refresh-токен и sqlite-кэш ответов. Экран настроек переделан под разделы.
2026-09-19 22:14:14 +03:00

90 lines
2.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../core/auth/switch_server.dart';
import '../../core/config.dart';
/// Asks for a new backend address and applies it. Shared by Настройки and the login screen
/// (a wrong address there means you can never reach Настройки).
Future<void> showApiBaseUrlDialog(BuildContext context) async {
final container = ProviderScope.containerOf(context);
final result = await showDialog<_Choice>(
context: context,
builder: (_) =>
_ApiBaseUrlDialog(current: container.read(apiBaseUrlProvider)),
);
if (result == null) return;
await switchApiBaseUrl(container, result.url);
}
class _Choice {
const _Choice(this.url);
/// null = back to the default address.
final String? url;
}
class _ApiBaseUrlDialog extends StatefulWidget {
const _ApiBaseUrlDialog({required this.current});
final String current;
@override
State<_ApiBaseUrlDialog> createState() => _ApiBaseUrlDialogState();
}
class _ApiBaseUrlDialogState extends State<_ApiBaseUrlDialog> {
late final _controller = TextEditingController(text: widget.current);
String? _error;
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _save() {
final url = ApiBaseUrlController.normalize(_controller.text);
if (url == null) {
setState(() => _error = 'Нужен адрес вида https://fin.example.com');
return;
}
Navigator.pop(context, _Choice(url));
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Адрес API'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: _controller,
autofocus: true,
keyboardType: TextInputType.url,
decoration: InputDecoration(errorText: _error),
onSubmitted: (_) => _save(),
),
const SizedBox(height: 12),
Text(
'Смена адреса завершит сеанс и очистит локальный кэш.',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Отмена'),
),
TextButton(
onPressed: () => Navigator.pop(context, const _Choice(null)),
child: const Text('По умолчанию'),
),
FilledButton(onPressed: _save, child: const Text('Сохранить')),
],
);
}
}