Адрес бэкенда хранится в shared_preferences и читается до runApp; switchApiBaseUrl сбрасывает токены и кэш старого сервера. Выход стирает refresh-токен и sqlite-кэш ответов. Экран настроек переделан под разделы.
90 lines
2.6 KiB
Dart
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('Сохранить')),
|
|
],
|
|
);
|
|
}
|
|
}
|