From: Azharul Haque <haque@azharul.com>
To: pve-devel@lists.proxmox.com
Cc: haque@azharul.com
Subject: [PATCH login-manager v3 3/5] refactor: ui: split password form into its own widget
Date: Thu, 20 Aug 2026 23:41:42 -0400 [thread overview]
Message-ID: <20260821034147.30194-6-haque@azharul.com> (raw)
In-Reply-To: <20260821034147.30194-1-haque@azharul.com>
Group the username field together with the password field below the
realm selector (it previously sat above it), and extract the credential
fields -- username, password and the save-password checkbox -- into a
private _ProxmoxPasswordForm widget. No functional change.
This prepares for OpenID Connect realm support in the following commit:
an OpenID realm has no username/password to enter in the app, so the
credential fields need to be shown or replaced as one unit depending on
the selected realm's type.
Suggested-by: Shan Shaji <s.shaji@proxmox.com>
Signed-off-by: Azharul Haque <haque@azharul.com>
---
lib/proxmox_login_form.dart | 169 ++++++++++++++++++++++--------------
1 file changed, 105 insertions(+), 64 deletions(-)
diff --git a/lib/proxmox_login_form.dart b/lib/proxmox_login_form.dart
index 6039407..8d8f2da 100644
--- a/lib/proxmox_login_form.dart
+++ b/lib/proxmox_login_form.dart
@@ -63,10 +63,6 @@ class ProxmoxLoginForm extends StatefulWidget {
}
class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
- bool _obscure = true;
- bool? _savePwCheckbox;
- FocusNode? passwordFocusNode;
-
@override
Widget build(BuildContext context) {
if (widget.accessDomains == null) {
@@ -97,20 +93,6 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
controller: widget.originController,
enabled: false,
),
- TextFormField(
- decoration: const InputDecoration(
- icon: Icon(Icons.person),
- labelText: 'Username',
- ),
- controller: widget.usernameController,
- validator: (value) {
- if (value!.isEmpty) {
- return 'Please enter username';
- }
- return null;
- },
- autofillHints: const [AutofillHints.username],
- ),
DropdownButtonFormField(
decoration: const InputDecoration(icon: Icon(Icons.domain)),
items: widget.accessDomains!
@@ -127,56 +109,115 @@ class _ProxmoxLoginFormState extends State<ProxmoxLoginForm> {
widget.accessDomains!.map((e) => Text(e!.realm)).toList(),
initialValue: widget.selectedDomain,
),
- Stack(
- children: [
- TextFormField(
- decoration: const InputDecoration(
- icon: Icon(Icons.lock),
- labelText: 'Password',
- ),
- controller: widget.passwordController,
- obscureText: _obscure,
- autocorrect: false,
- focusNode: passwordFocusNode,
- validator: (value) {
- if (value!.isEmpty) {
- return 'Please enter password';
- }
- return null;
- },
- onFieldSubmitted: (value) => widget.onPasswordSubmitted!(),
- autofillHints: const [AutofillHints.password],
- ),
- Align(
- alignment: Alignment.bottomRight,
- child: IconButton(
- constraints: BoxConstraints.tight(const Size(58, 58)),
- iconSize: 24,
- tooltip: _obscure ? "Show password" : "Hide password",
- icon:
- Icon(_obscure ? Icons.visibility : Icons.visibility_off),
- onPressed: () => setState(() {
- _obscure = !_obscure;
- }),
- ),
- )
- ],
+ _ProxmoxPasswordForm(
+ usernameController: widget.usernameController,
+ passwordController: widget.passwordController,
+ onPasswordSubmitted: widget.onPasswordSubmitted,
+ onSavePasswordChanged: widget.onSavePasswordChanged,
+ canSavePassword: widget.canSavePassword,
+ passwordSaved: widget.passwordSaved,
),
- if (widget.canSavePassword ?? false)
- CheckboxListTile(
- title: const Text('Save password in biometric storage'),
- value: _savePwCheckbox ?? widget.passwordSaved ?? false,
- onChanged: (value) {
- if (widget.onSavePasswordChanged != null) {
- widget.onSavePasswordChanged!(value!);
+ ],
+ ),
+ );
+ }
+}
+
+/// Username/password credential fields of the login form, including the
+/// optional "save password" checkbox.
+class _ProxmoxPasswordForm extends StatefulWidget {
+ final TextEditingController usernameController;
+ final TextEditingController passwordController;
+ final Function? onPasswordSubmitted;
+ final Function? onSavePasswordChanged;
+ final bool? canSavePassword;
+ final bool? passwordSaved;
+
+ const _ProxmoxPasswordForm({
+ required this.usernameController,
+ required this.passwordController,
+ this.onPasswordSubmitted,
+ this.onSavePasswordChanged,
+ this.canSavePassword,
+ this.passwordSaved,
+ });
+
+ @override
+ State<_ProxmoxPasswordForm> createState() => _ProxmoxPasswordFormState();
+}
+
+class _ProxmoxPasswordFormState extends State<_ProxmoxPasswordForm> {
+ bool _obscure = true;
+ bool? _savePwCheckbox;
+ FocusNode? passwordFocusNode;
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ TextFormField(
+ decoration: const InputDecoration(
+ icon: Icon(Icons.person),
+ labelText: 'Username',
+ ),
+ controller: widget.usernameController,
+ validator: (value) {
+ if (value!.isEmpty) {
+ return 'Please enter username';
+ }
+ return null;
+ },
+ autofillHints: const [AutofillHints.username],
+ ),
+ Stack(
+ children: [
+ TextFormField(
+ decoration: const InputDecoration(
+ icon: Icon(Icons.lock),
+ labelText: 'Password',
+ ),
+ controller: widget.passwordController,
+ obscureText: _obscure,
+ autocorrect: false,
+ focusNode: passwordFocusNode,
+ validator: (value) {
+ if (value!.isEmpty) {
+ return 'Please enter password';
}
- setState(() {
- _savePwCheckbox = value!;
- });
+ return null;
},
+ onFieldSubmitted: (value) => widget.onPasswordSubmitted!(),
+ autofillHints: const [AutofillHints.password],
+ ),
+ Align(
+ alignment: Alignment.bottomRight,
+ child: IconButton(
+ constraints: BoxConstraints.tight(const Size(58, 58)),
+ iconSize: 24,
+ tooltip: _obscure ? "Show password" : "Hide password",
+ icon: Icon(_obscure ? Icons.visibility : Icons.visibility_off),
+ onPressed: () => setState(() {
+ _obscure = !_obscure;
+ }),
+ ),
)
- ],
- ),
+ ],
+ ),
+ if (widget.canSavePassword ?? false)
+ CheckboxListTile(
+ title: const Text('Save password in biometric storage'),
+ value: _savePwCheckbox ?? widget.passwordSaved ?? false,
+ onChanged: (value) {
+ if (widget.onSavePasswordChanged != null) {
+ widget.onSavePasswordChanged!(value!);
+ }
+ setState(() {
+ _savePwCheckbox = value!;
+ });
+ },
+ )
+ ],
);
}
--
2.50.1 (Apple Git-155)
next prev parent reply other threads:[~2026-08-25 8:10 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-10 5:40 [PATCH 0/2] android: register OpenID Connect callback activity for #4281 Azharul Haque
2026-08-10 5:40 ` [PATCH 1/2] fix #4281: android: register OpenID Connect callback activity Azharul Haque
2026-08-10 5:40 ` [PATCH 2/2] fix #4281: android: match renamed OpenID callback scheme Azharul Haque
2026-08-10 13:59 ` [PATCH 0/2] android: register OpenID Connect callback activity for #4281 Shan Shaji
2026-08-10 14:47 ` [PATCH v2 0/7] app: implement OpenID Connect (OAuth) realm login (#4281) Azharul Haque
2026-08-10 14:47 ` [PATCH dart-api-client v2 1/2] fix #4281: access: add `type` property to `PveAccessDomainModel` Azharul Haque
2026-08-20 9:17 ` Shan Shaji
2026-08-20 13:37 ` Azharul Haque
2026-08-20 14:39 ` Shan Shaji
2026-08-21 2:56 ` Azharul Haque
2026-08-21 8:00 ` Shan Shaji
2026-08-10 14:47 ` [PATCH dart-api-client v2 2/2] fix #4281: access: add OpenID Connect auth-url/login helpers Azharul Haque
2026-08-20 9:48 ` Shan Shaji
2026-08-20 13:41 ` Azharul Haque
2026-08-10 14:47 ` [PATCH login-manager v2 1/3] fix #4281: ui: add OpenID Connect login flow to login form Azharul Haque
2026-08-20 12:31 ` Shan Shaji
2026-08-10 14:47 ` [PATCH login-manager v2 2/3] fix #4281: ui: fix stale Continue button state on realm switch Azharul Haque
2026-08-10 14:47 ` [PATCH login-manager v2 3/3] fix #4281: ui: use a namespaced OpenID callback scheme Azharul Haque
2026-08-20 14:28 ` Shan Shaji
2026-08-10 14:47 ` [PATCH flutter-frontend v2 1/2] fix #4281: android: register OpenID Connect callback activity Azharul Haque
2026-08-20 14:01 ` Shan Shaji
2026-08-10 14:47 ` [PATCH flutter-frontend v2 2/2] fix #4281: android: match renamed OpenID callback scheme Azharul Haque
2026-08-21 3:41 ` [PATCH v3 00/10] app: implement OpenID Connect (OAuth) realm login (#4281) Azharul Haque
2026-08-21 3:41 ` [PATCH dart-api-client v3 1/2] fix #4281: access: add `type` property to `PveAccessDomainModel` Azharul Haque
2026-08-21 3:41 ` [PATCH dart-api-client v3 2/2] fix #4281: access: add OpenID Connect auth-url/login helpers Azharul Haque
2026-08-21 3:41 ` [PATCH login-manager v3 1/5] fix #4281: deps: add flutter_web_auth_2 dependency Azharul Haque
2026-08-21 3:41 ` [PATCH login-manager v3 2/5] refactor: ui: factor out shared login tail into _finishLogin Azharul Haque
2026-08-21 3:41 ` Azharul Haque [this message]
2026-08-21 3:41 ` [PATCH login-manager v3 4/5] fix #4281: ui: add OpenID Connect login flow to login form Azharul Haque
2026-08-21 3:41 ` [PATCH login-manager v3 5/5] fix #4281: ui: fix stale Continue button state on realm switch Azharul Haque
2026-08-21 3:41 ` [PATCH flutter-frontend v3 1/3] chore: regenerate plugin registrant for flutter_web_auth_2 Azharul Haque
2026-08-21 3:41 ` [PATCH flutter-frontend v3 2/3] fix #4281: android: set taskAffinity="" on MainActivity Azharul Haque
2026-08-21 3:41 ` [PATCH flutter-frontend v3 3/3] fix #4281: android: register OpenID Connect callback activity Azharul Haque
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260821034147.30194-6-haque@azharul.com \
--to=haque@azharul.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox