1 import 'package:flutter/material.dart';
2 import 'package:proxmox_dart_api_client/proxmox_dart_api_client.dart';
3 import 'package:proxmox_login_manager/proxmox_login_form.dart';
5 class ProxmoxTfaForm extends StatefulWidget {
6 final ProxmoxApiClient? apiClient;
8 const ProxmoxTfaForm({super.key, this.apiClient});
11 State<ProxmoxTfaForm> createState() => _ProxmoxTfaFormState();
14 class _ProxmoxTfaFormState extends State<ProxmoxTfaForm> {
15 final TextEditingController _codeController = TextEditingController();
16 bool _isLoading = false;
17 List<String> _tfaKinds = [];
18 String _selectedTfaKind = "";
23 _tfaKinds = widget.apiClient!.credentials.tfa!.kinds().toList();
24 _selectedTfaKind = _tfaKinds[0];
28 Widget build(BuildContext context) {
30 data: ThemeData.dark().copyWith(
31 colorScheme: const ColorScheme.dark().copyWith(
32 secondary: ProxmoxColors.orange,
33 onSecondary: ProxmoxColors.supportGrey)),
35 backgroundColor: ProxmoxColors.supportBlue,
36 extendBodyBehindAppBar: true,
39 backgroundColor: Colors.transparent,
41 icon: const Icon(Icons.close),
42 onPressed: () => Navigator.of(context).pop(),
46 alignment: Alignment.center,
48 SingleChildScrollView(
49 child: ConstrainedBox(
50 constraints: BoxConstraints.tightFor(
51 height: MediaQuery.of(context).size.height),
54 padding: const EdgeInsets.all(8.0),
56 mainAxisAlignment: MainAxisAlignment.start,
57 crossAxisAlignment: CrossAxisAlignment.center,
60 padding: EdgeInsets.fromLTRB(0, 100.0, 0, 30.0),
71 fontWeight: FontWeight.bold),
74 'Check your second factor provider',
76 color: Colors.white38,
77 fontWeight: FontWeight.bold),
80 padding: const EdgeInsets.fromLTRB(0, 50.0, 0, 8.0),
85 DropdownButtonFormField(
86 decoration: const InputDecoration(
88 icon: Icon(Icons.input)),
90 .map((e) => DropdownMenuItem(
92 child: ListTile(title: Text(e)),
95 onChanged: (String? value) {
97 _selectedTfaKind = value!;
100 selectedItemBuilder: (context) =>
101 _tfaKinds.map((e) => Text(e)).toList(),
102 value: _selectedTfaKind,
105 controller: _codeController,
106 textAlign: TextAlign.center,
107 decoration: const InputDecoration(
109 icon: Icon(Icons.pin)),
110 keyboardType: _selectedTfaKind == 'totp'
111 ? TextInputType.number
112 : TextInputType.visiblePassword,
114 onSubmitted: (value) => _submitTfaCode()),
121 alignment: Alignment.bottomCenter,
123 width: MediaQuery.of(context).size.width,
125 style: TextButton.styleFrom(
126 foregroundColor: Colors.white,
127 backgroundColor: const Color(0xFFE47225),
128 disabledBackgroundColor: Colors.grey,
130 onPressed: () => _submitTfaCode(),
131 child: const Text('Continue'),
143 const ProxmoxProgressOverlay(
144 message: 'Verifying second-factor...',
152 Future<void> _submitTfaCode() async {
157 final client = await widget.apiClient!
158 .finishTfaChallenge(_selectedTfaKind, _codeController.text);
159 if (mounted) Navigator.of(context).pop(client);
160 } on ProxmoxApiException catch (e) {
164 builder: (context) => ProxmoxApiErrorDialog(
175 builder: (context) => ConnectionErrorDialog(