From e72aeba466f68749d3164fa06015366035a0024d Mon Sep 17 00:00:00 2001 From: v3DJG6GL <72495210+v3DJG6GL@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:03:26 +0100 Subject: [PATCH 01/13] feat: add hidePasswordLogin property to FladderConfig --- lib/util/fladder_config.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/util/fladder_config.dart b/lib/util/fladder_config.dart index 0289ef89d..9451b6f3a 100644 --- a/lib/util/fladder_config.dart +++ b/lib/util/fladder_config.dart @@ -10,6 +10,10 @@ class FladderConfig { static set seerrBaseUrl(String? value) => _instance._seerrBaseUrl = value; String? _seerrBaseUrl; + static bool? get hidePasswordLogin => _instance._hidePasswordLogin; + static set hidePasswordLogin(bool? value) => _instance._hidePasswordLogin = value; + bool? _hidePasswordLogin; + static void fromJson(Map json) => _instance = FladderConfig._fromJson(json); factory FladderConfig._fromJson(Map json) { @@ -19,6 +23,7 @@ class FladderConfig { config._baseUrl = newUrl?.isEmpty == true ? null : newUrl; config._seerrBaseUrl = newSeerrUrl?.isEmpty == true ? null : newSeerrUrl; + config._hidePasswordLogin = json['hidePasswordLogin'] as bool?; return config; } From ac377925b18e6d65c95ac4bda80b0fdd1453e7ff Mon Sep 17 00:00:00 2001 From: v3DJG6GL <72495210+v3DJG6GL@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:08:45 +0100 Subject: [PATCH 02/13] feat: add hidePasswordLogin field to ClientSettingsModel --- lib/models/settings/client_settings_model.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/models/settings/client_settings_model.dart b/lib/models/settings/client_settings_model.dart index 224cfdf7d..95b0d8043 100644 --- a/lib/models/settings/client_settings_model.dart +++ b/lib/models/settings/client_settings_model.dart @@ -91,6 +91,7 @@ abstract class ClientSettingsModel with _$ClientSettingsModel { @Default(false) bool usePosterForLibrary, @Default(false) bool useSystemIME, @Default(false) bool useTVExpandedLayout, + @Default(false) bool hidePasswordLogin, String? lastViewedUpdate, int? libraryPageSize, @Default({}) Map shortcuts, From 95d2ff27eca614e6e566989e44ec58c1cceb01c5 Mon Sep 17 00:00:00 2001 From: v3DJG6GL <72495210+v3DJG6GL@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:12:16 +0100 Subject: [PATCH 03/13] feat: add hidePasswordLogin toggle to advanced client settings --- .../client_settings_advanced.dart | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/screens/settings/client_sections/client_settings_advanced.dart b/lib/screens/settings/client_sections/client_settings_advanced.dart index b0e04ed08..1e59b9567 100644 --- a/lib/screens/settings/client_sections/client_settings_advanced.dart +++ b/lib/screens/settings/client_sections/client_settings_advanced.dart @@ -5,6 +5,7 @@ import 'package:iconsax_plus/iconsax_plus.dart'; import 'package:fladder/providers/settings/client_settings_provider.dart'; import 'package:fladder/providers/settings/home_settings_provider.dart'; +import 'package:fladder/util/fladder_config.dart'; import 'package:fladder/screens/settings/settings_list_tile.dart'; import 'package:fladder/screens/settings/widgets/settings_label_divider.dart'; import 'package:fladder/screens/settings/widgets/settings_list_group.dart'; @@ -115,6 +116,24 @@ List buildClientSettingsAdvanced(BuildContext context, WidgetRef ref) { onChanged: (value) => ref.read(clientSettingsProvider.notifier).useSystemIME(value), ), ), + SettingsListTile( + label: Text(context.localized.hidePasswordLogin), + subLabel: Text(context.localized.hidePasswordLoginDescription), + onTap: FladderConfig.hidePasswordLogin != null + ? null + : () => ref + .read(clientSettingsProvider.notifier) + .update((current) => current.copyWith(hidePasswordLogin: !current.hidePasswordLogin)), + trailing: Switch( + value: FladderConfig.hidePasswordLogin ?? + ref.watch(clientSettingsProvider.select((value) => value.hidePasswordLogin)), + onChanged: FladderConfig.hidePasswordLogin != null + ? null + : (value) => ref + .read(clientSettingsProvider.notifier) + .update((current) => current.copyWith(hidePasswordLogin: value)), + ), + ), ], ); } From 3126b303a93284ba50286df0ba945bf14c6dea16 Mon Sep 17 00:00:00 2001 From: v3DJG6GL <72495210+v3DJG6GL@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:14:26 +0100 Subject: [PATCH 04/13] feat: conditionally hide password login UI in login screen --- .../login/login_screen_credentials.dart | 145 +++++++++++------- 1 file changed, 86 insertions(+), 59 deletions(-) diff --git a/lib/screens/login/login_screen_credentials.dart b/lib/screens/login/login_screen_credentials.dart index 1349a34a2..4ed11de07 100644 --- a/lib/screens/login/login_screen_credentials.dart +++ b/lib/screens/login/login_screen_credentials.dart @@ -11,6 +11,7 @@ import 'package:fladder/models/account_model.dart'; import 'package:fladder/providers/api_provider.dart'; import 'package:fladder/providers/auth_provider.dart'; import 'package:fladder/providers/seerr_api_provider.dart'; +import 'package:fladder/providers/settings/client_settings_provider.dart'; import 'package:fladder/providers/shared_provider.dart'; import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/routes/auto_router.gr.dart'; @@ -103,6 +104,10 @@ class _LoginScreenCredentialsState extends ConsumerState final hasBaseUrl = ref.watch(authProvider.select((value) => value.hasBaseUrl)); final urlError = ref.watch(authProvider.select((value) => value.errorMessage)); final hasQuickConnect = ref.watch(authProvider.select((value) => value.serverLoginModel?.hasQuickConnect ?? false)); + // Note: hidePasswordLogin is a UI preference, not a security control. + // It hides the password fields but does not disable password-based authentication on the server. + final hidePasswordLogin = FladderConfig.hidePasswordLogin == true || + ref.watch(clientSettingsProvider.select((value) => value.hidePasswordLogin)); ref.listen( authProvider.select((value) => value.serverLoginModel), @@ -214,68 +219,90 @@ class _LoginScreenCredentialsState extends ConsumerState crossAxisAlignment: CrossAxisAlignment.stretch, spacing: 8, children: [ - Flexible( - child: AutofillGroup( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - spacing: 8, - children: [ - Flexible( - child: OutlinedTextField( - controller: usernameController, - autoFillHints: const [AutofillHints.username], - textInputAction: TextInputAction.next, - autocorrect: false, - onChanged: (value) => setState(() {}), - label: context.localized.userName, + if (!hidePasswordLogin) + Flexible( + child: AutofillGroup( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + spacing: 8, + children: [ + Flexible( + child: OutlinedTextField( + controller: usernameController, + autoFillHints: const [AutofillHints.username], + textInputAction: TextInputAction.next, + autocorrect: false, + onChanged: (value) => setState(() {}), + label: context.localized.userName, + ), ), - ), - Flexible( - child: OutlinedTextField( - controller: passwordController, - autoFillHints: const [AutofillHints.password], - keyboardType: TextInputType.visiblePassword, - focusNode: focusNode, - autocorrect: false, - textInputAction: TextInputAction.send, - onSubmitted: (value) => enterCredentialsTryLogin?.call(), - onChanged: (value) => setState(() {}), - label: context.localized.password, + Flexible( + child: OutlinedTextField( + controller: passwordController, + autoFillHints: const [AutofillHints.password], + keyboardType: TextInputType.visiblePassword, + focusNode: focusNode, + autocorrect: false, + textInputAction: TextInputAction.send, + onSubmitted: (value) => enterCredentialsTryLogin?.call(), + onChanged: (value) => setState(() {}), + label: context.localized.password, + ), ), - ), - ], + ], + ), ), ), - ), - const Divider( - indent: 32, - endIndent: 32, - ), - Row( - spacing: 8, - children: [ - Expanded( - child: FilledButton( - onPressed: enterCredentialsTryLogin, - child: loggingIn - ? SizedBox( - width: 18, - height: 18, - child: CircularProgressIndicator( - color: Theme.of(context).colorScheme.inversePrimary, strokeCap: StrokeCap.round), - ) - : Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(context.localized.login), - const SizedBox(width: 8), - const Icon(IconsaxPlusBold.send_1), - ], - ), + if (!hidePasswordLogin) ...[ + const Divider( + indent: 32, + endIndent: 32, + ), + Row( + spacing: 8, + children: [ + Expanded( + child: FilledButton( + onPressed: enterCredentialsTryLogin, + child: loggingIn + ? SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator( + color: Theme.of(context).colorScheme.inversePrimary, strokeCap: StrokeCap.round), + ) + : Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(context.localized.login), + const SizedBox(width: 8), + const Icon(IconsaxPlusBold.send_1), + ], + ), + ), ), - ), - if (FladderConfig.seerrBaseUrl?.isNotEmpty != true) + if (FladderConfig.seerrBaseUrl?.isNotEmpty != true) + IconButton.filledTonal( + onPressed: () async { + final tempSeerrUrl = ref.read(authProvider.select((value) => value.tempSeerrUrl)); + final result = await showAdvancedLoginOptionsDialog( + context, + initialSeerrUrl: tempSeerrUrl, + ); + if (result != null) { + ref.read(authProvider.notifier).setTempSeerrUrl(result); + } + }, + icon: const Icon(IconsaxPlusLinear.setting_3), + ), + ], + ), + ], + if (hidePasswordLogin && FladderConfig.seerrBaseUrl?.isNotEmpty != true) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ IconButton.filledTonal( onPressed: () async { final tempSeerrUrl = ref.read(authProvider.select((value) => value.tempSeerrUrl)); @@ -289,8 +316,8 @@ class _LoginScreenCredentialsState extends ConsumerState }, icon: const Icon(IconsaxPlusLinear.setting_3), ), - ], - ), + ], + ), if (hasQuickConnect) FilledButton( onPressed: () async { From 4d059f3ad135239dba5e02401e4fb5d52adfa28e Mon Sep 17 00:00:00 2001 From: v3DJG6GL <72495210+v3DJG6GL@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:16:28 +0100 Subject: [PATCH 05/13] feat: propagate hidePasswordLogin from FladderConfig through auth provider --- lib/models/login_screen_model.dart | 1 + lib/providers/auth_provider.dart | 3 +++ lib/screens/login/login_screen_credentials.dart | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/models/login_screen_model.dart b/lib/models/login_screen_model.dart index b2f2f4e41..272cdc7b0 100644 --- a/lib/models/login_screen_model.dart +++ b/lib/models/login_screen_model.dart @@ -22,6 +22,7 @@ abstract class LoginScreenModel with _$LoginScreenModel { @Default(false) bool loading, String? tempSeerrUrl, String? tempSeerrSessionCookie, + @Default(false) bool hidePasswordLogin, }) = _LoginScreenModel; } diff --git a/lib/providers/auth_provider.dart b/lib/providers/auth_provider.dart index 9b479c8f1..dd9c3329c 100644 --- a/lib/providers/auth_provider.dart +++ b/lib/providers/auth_provider.dart @@ -50,6 +50,9 @@ class AuthNotifier extends StateNotifier { await setServer(url); } } + if (FladderConfig.hidePasswordLogin == true) { + state = state.copyWith(hidePasswordLogin: true); + } state = state.copyWith( accounts: currentAccounts, screen: currentAccounts.isEmpty ? LoginScreenType.login : LoginScreenType.users, diff --git a/lib/screens/login/login_screen_credentials.dart b/lib/screens/login/login_screen_credentials.dart index 4ed11de07..7ad98c332 100644 --- a/lib/screens/login/login_screen_credentials.dart +++ b/lib/screens/login/login_screen_credentials.dart @@ -106,7 +106,7 @@ class _LoginScreenCredentialsState extends ConsumerState final hasQuickConnect = ref.watch(authProvider.select((value) => value.serverLoginModel?.hasQuickConnect ?? false)); // Note: hidePasswordLogin is a UI preference, not a security control. // It hides the password fields but does not disable password-based authentication on the server. - final hidePasswordLogin = FladderConfig.hidePasswordLogin == true || + final hidePasswordLogin = ref.watch(authProvider.select((value) => value.hidePasswordLogin)) || ref.watch(clientSettingsProvider.select((value) => value.hidePasswordLogin)); ref.listen( From 320a67e32e4b120e32b2963dfe57b6c264a31a4d Mon Sep 17 00:00:00 2001 From: v3DJG6GL <72495210+v3DJG6GL@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:17:15 +0100 Subject: [PATCH 06/13] feat: add hidePasswordLogin localization strings --- lib/l10n/app_en.arb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 2b3b7344b..8f5809383 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -2168,6 +2168,10 @@ "@seerrAuthLocal": {}, "seerrAuthJellyfin": "Jellyfin", "@seerrAuthJellyfin": {}, + "hidePasswordLogin": "Hide password login", + "@hidePasswordLogin": {}, + "hidePasswordLoginDescription": "Only show QuickConnect on the login screen", + "@hidePasswordLoginDescription": {}, "seerrUserFetchFailed": "Failed to fetch user from Seerr", "@seerrUserFetchFailed": {}, "seerrEnterServerUrlFirst": "Enter a Seerr server URL first", From 8bfaa21fa53acd50e60446b55969375097554580 Mon Sep 17 00:00:00 2001 From: v3DJG6GL <72495210+v3DJG6GL@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:18:50 +0100 Subject: [PATCH 07/13] feat: add HIDE_PASSWORD_LOGIN Docker env var --- Dockerfile | 1 + Dockerfile-rootless | 1 + docker-compose.yml | 1 + docker-entrypoint.sh | 5 ++++- 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 23c79b92d..c84c17cd0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,7 @@ EXPOSE 80 ENV BASE_URL="" ENV SEERR_BASE_URL="" ENV SEERR_HEADER="null" +ENV HIDE_PASSWORD_LOGIN="" COPY build/web /usr/share/nginx/html COPY docker-entrypoint.sh /docker-entrypoint.sh diff --git a/Dockerfile-rootless b/Dockerfile-rootless index 8a77ed666..d17bd79e8 100644 --- a/Dockerfile-rootless +++ b/Dockerfile-rootless @@ -5,6 +5,7 @@ EXPOSE 8080 ENV BASE_URL="" ENV SEERR_BASE_URL="" ENV SEERR_HEADER="null" +ENV HIDE_PASSWORD_LOGIN="" USER root COPY build/web /usr/share/nginx/html diff --git a/docker-compose.yml b/docker-compose.yml index d321b3857..c80029976 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,3 +8,4 @@ services: - BASE_URL=https://server-url #OPTIONAL: Locks the Fladder front-end to a certain jellyfin server - SEERR_BASE_URL=https://seerr-url #OPTIONAL: Presets Seerr base URL - SEERR_HEADER={"key":"value"} #OPTIONAL: JSON object string of Seerr headers + - HIDE_PASSWORD_LOGIN= #OPTIONAL: Set to "true" to hide password fields and only show QuickConnect diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 843020f6c..230156cfe 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -1,11 +1,14 @@ #!/bin/sh set -e +HIDE_PW_VAL=$([ "$HIDE_PASSWORD_LOGIN" = "true" ] && echo true || echo null) + # Generate config.json from environment variables cat > /usr/share/nginx/html/assets/config/config.json < Date: Mon, 9 Mar 2026 18:21:51 +0100 Subject: [PATCH 08/13] chore: regenerate freezed/json_serializable code for hidePasswordLogin --- lib/models/login_screen_model.freezed.dart | 45 ++++++++++++++----- .../client_settings_model.freezed.dart | 29 +++++++++++- .../settings/client_settings_model.g.dart | 2 + 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/lib/models/login_screen_model.freezed.dart b/lib/models/login_screen_model.freezed.dart index 93d679b02..fc292e92d 100644 --- a/lib/models/login_screen_model.freezed.dart +++ b/lib/models/login_screen_model.freezed.dart @@ -22,6 +22,7 @@ mixin _$LoginScreenModel { bool get loading; String? get tempSeerrUrl; String? get tempSeerrSessionCookie; + bool get hidePasswordLogin; /// Create a copy of LoginScreenModel /// with the given fields replaced by the non-null parameter values. @@ -33,7 +34,7 @@ mixin _$LoginScreenModel { @override String toString() { - return 'LoginScreenModel(accounts: $accounts, screen: $screen, serverLoginModel: $serverLoginModel, errorMessage: $errorMessage, hasBaseUrl: $hasBaseUrl, loading: $loading, tempSeerrUrl: $tempSeerrUrl, tempSeerrSessionCookie: $tempSeerrSessionCookie)'; + return 'LoginScreenModel(accounts: $accounts, screen: $screen, serverLoginModel: $serverLoginModel, errorMessage: $errorMessage, hasBaseUrl: $hasBaseUrl, loading: $loading, tempSeerrUrl: $tempSeerrUrl, tempSeerrSessionCookie: $tempSeerrSessionCookie, hidePasswordLogin: $hidePasswordLogin)'; } } @@ -51,7 +52,8 @@ abstract mixin class $LoginScreenModelCopyWith<$Res> { bool hasBaseUrl, bool loading, String? tempSeerrUrl, - String? tempSeerrSessionCookie}); + String? tempSeerrSessionCookie, + bool hidePasswordLogin}); $ServerLoginModelCopyWith<$Res>? get serverLoginModel; } @@ -77,6 +79,7 @@ class _$LoginScreenModelCopyWithImpl<$Res> Object? loading = null, Object? tempSeerrUrl = freezed, Object? tempSeerrSessionCookie = freezed, + Object? hidePasswordLogin = null, }) { return _then(_self.copyWith( accounts: null == accounts @@ -111,6 +114,10 @@ class _$LoginScreenModelCopyWithImpl<$Res> ? _self.tempSeerrSessionCookie : tempSeerrSessionCookie // ignore: cast_nullable_to_non_nullable as String?, + hidePasswordLogin: null == hidePasswordLogin + ? _self.hidePasswordLogin + : hidePasswordLogin // ignore: cast_nullable_to_non_nullable + as bool, )); } @@ -230,7 +237,8 @@ extension LoginScreenModelPatterns on LoginScreenModel { bool hasBaseUrl, bool loading, String? tempSeerrUrl, - String? tempSeerrSessionCookie)? + String? tempSeerrSessionCookie, + bool hidePasswordLogin)? $default, { required TResult orElse(), }) { @@ -245,7 +253,8 @@ extension LoginScreenModelPatterns on LoginScreenModel { _that.hasBaseUrl, _that.loading, _that.tempSeerrUrl, - _that.tempSeerrSessionCookie); + _that.tempSeerrSessionCookie, + _that.hidePasswordLogin); case _: return orElse(); } @@ -274,7 +283,8 @@ extension LoginScreenModelPatterns on LoginScreenModel { bool hasBaseUrl, bool loading, String? tempSeerrUrl, - String? tempSeerrSessionCookie) + String? tempSeerrSessionCookie, + bool hidePasswordLogin) $default, ) { final _that = this; @@ -288,7 +298,8 @@ extension LoginScreenModelPatterns on LoginScreenModel { _that.hasBaseUrl, _that.loading, _that.tempSeerrUrl, - _that.tempSeerrSessionCookie); + _that.tempSeerrSessionCookie, + _that.hidePasswordLogin); case _: throw StateError('Unexpected subclass'); } @@ -316,7 +327,8 @@ extension LoginScreenModelPatterns on LoginScreenModel { bool hasBaseUrl, bool loading, String? tempSeerrUrl, - String? tempSeerrSessionCookie)? + String? tempSeerrSessionCookie, + bool hidePasswordLogin)? $default, ) { final _that = this; @@ -330,7 +342,8 @@ extension LoginScreenModelPatterns on LoginScreenModel { _that.hasBaseUrl, _that.loading, _that.tempSeerrUrl, - _that.tempSeerrSessionCookie); + _that.tempSeerrSessionCookie, + _that.hidePasswordLogin); case _: return null; } @@ -348,7 +361,8 @@ class _LoginScreenModel implements LoginScreenModel { this.hasBaseUrl = false, this.loading = false, this.tempSeerrUrl, - this.tempSeerrSessionCookie}) + this.tempSeerrSessionCookie, + this.hidePasswordLogin = false}) : _accounts = accounts; final List _accounts; @@ -377,6 +391,9 @@ class _LoginScreenModel implements LoginScreenModel { final String? tempSeerrUrl; @override final String? tempSeerrSessionCookie; + @override + @JsonKey() + final bool hidePasswordLogin; /// Create a copy of LoginScreenModel /// with the given fields replaced by the non-null parameter values. @@ -388,7 +405,7 @@ class _LoginScreenModel implements LoginScreenModel { @override String toString() { - return 'LoginScreenModel(accounts: $accounts, screen: $screen, serverLoginModel: $serverLoginModel, errorMessage: $errorMessage, hasBaseUrl: $hasBaseUrl, loading: $loading, tempSeerrUrl: $tempSeerrUrl, tempSeerrSessionCookie: $tempSeerrSessionCookie)'; + return 'LoginScreenModel(accounts: $accounts, screen: $screen, serverLoginModel: $serverLoginModel, errorMessage: $errorMessage, hasBaseUrl: $hasBaseUrl, loading: $loading, tempSeerrUrl: $tempSeerrUrl, tempSeerrSessionCookie: $tempSeerrSessionCookie, hidePasswordLogin: $hidePasswordLogin)'; } } @@ -408,7 +425,8 @@ abstract mixin class _$LoginScreenModelCopyWith<$Res> bool hasBaseUrl, bool loading, String? tempSeerrUrl, - String? tempSeerrSessionCookie}); + String? tempSeerrSessionCookie, + bool hidePasswordLogin}); @override $ServerLoginModelCopyWith<$Res>? get serverLoginModel; @@ -435,6 +453,7 @@ class __$LoginScreenModelCopyWithImpl<$Res> Object? loading = null, Object? tempSeerrUrl = freezed, Object? tempSeerrSessionCookie = freezed, + Object? hidePasswordLogin = null, }) { return _then(_LoginScreenModel( accounts: null == accounts @@ -469,6 +488,10 @@ class __$LoginScreenModelCopyWithImpl<$Res> ? _self.tempSeerrSessionCookie : tempSeerrSessionCookie // ignore: cast_nullable_to_non_nullable as String?, + hidePasswordLogin: null == hidePasswordLogin + ? _self.hidePasswordLogin + : hidePasswordLogin // ignore: cast_nullable_to_non_nullable + as bool, )); } diff --git a/lib/models/settings/client_settings_model.freezed.dart b/lib/models/settings/client_settings_model.freezed.dart index a7af8642f..0dc53f58d 100644 --- a/lib/models/settings/client_settings_model.freezed.dart +++ b/lib/models/settings/client_settings_model.freezed.dart @@ -44,6 +44,7 @@ mixin _$ClientSettingsModel implements DiagnosticableTreeMixin { bool get usePosterForLibrary; bool get useSystemIME; bool get useTVExpandedLayout; + bool get hidePasswordLogin; String? get lastViewedUpdate; int? get libraryPageSize; Map get shortcuts; @@ -96,6 +97,7 @@ mixin _$ClientSettingsModel implements DiagnosticableTreeMixin { ..add(DiagnosticsProperty('usePosterForLibrary', usePosterForLibrary)) ..add(DiagnosticsProperty('useSystemIME', useSystemIME)) ..add(DiagnosticsProperty('useTVExpandedLayout', useTVExpandedLayout)) + ..add(DiagnosticsProperty('hidePasswordLogin', hidePasswordLogin)) ..add(DiagnosticsProperty('lastViewedUpdate', lastViewedUpdate)) ..add(DiagnosticsProperty('libraryPageSize', libraryPageSize)) ..add(DiagnosticsProperty('shortcuts', shortcuts)); @@ -103,7 +105,7 @@ mixin _$ClientSettingsModel implements DiagnosticableTreeMixin { @override String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'ClientSettingsModel(syncPath: $syncPath, transcodeDownloadModel: $transcodeDownloadModel, position: $position, size: $size, timeOut: $timeOut, nextUpDateCutoff: $nextUpDateCutoff, updateNotificationsInterval: $updateNotificationsInterval, themeMode: $themeMode, themeColor: $themeColor, deriveColorsFromItem: $deriveColorsFromItem, amoledBlack: $amoledBlack, blurPlaceHolders: $blurPlaceHolders, blurUpcomingEpisodes: $blurUpcomingEpisodes, selectedLocale: $selectedLocale, enableMediaKeys: $enableMediaKeys, posterSize: $posterSize, pinchPosterZoom: $pinchPosterZoom, mouseDragSupport: $mouseDragSupport, requireWifi: $requireWifi, expandSideBar: $expandSideBar, showAllCollectionTypes: $showAllCollectionTypes, maxConcurrentDownloads: $maxConcurrentDownloads, schemeVariant: $schemeVariant, backgroundImage: $backgroundImage, enableBlurEffects: $enableBlurEffects, checkForUpdates: $checkForUpdates, usePosterForLibrary: $usePosterForLibrary, useSystemIME: $useSystemIME, useTVExpandedLayout: $useTVExpandedLayout, lastViewedUpdate: $lastViewedUpdate, libraryPageSize: $libraryPageSize, shortcuts: $shortcuts)'; + return 'ClientSettingsModel(syncPath: $syncPath, transcodeDownloadModel: $transcodeDownloadModel, position: $position, size: $size, timeOut: $timeOut, nextUpDateCutoff: $nextUpDateCutoff, updateNotificationsInterval: $updateNotificationsInterval, themeMode: $themeMode, themeColor: $themeColor, deriveColorsFromItem: $deriveColorsFromItem, amoledBlack: $amoledBlack, blurPlaceHolders: $blurPlaceHolders, blurUpcomingEpisodes: $blurUpcomingEpisodes, selectedLocale: $selectedLocale, enableMediaKeys: $enableMediaKeys, posterSize: $posterSize, pinchPosterZoom: $pinchPosterZoom, mouseDragSupport: $mouseDragSupport, requireWifi: $requireWifi, expandSideBar: $expandSideBar, showAllCollectionTypes: $showAllCollectionTypes, maxConcurrentDownloads: $maxConcurrentDownloads, schemeVariant: $schemeVariant, backgroundImage: $backgroundImage, enableBlurEffects: $enableBlurEffects, checkForUpdates: $checkForUpdates, usePosterForLibrary: $usePosterForLibrary, useSystemIME: $useSystemIME, useTVExpandedLayout: $useTVExpandedLayout, hidePasswordLogin: $hidePasswordLogin, lastViewedUpdate: $lastViewedUpdate, libraryPageSize: $libraryPageSize, shortcuts: $shortcuts)'; } } @@ -143,6 +145,7 @@ abstract mixin class $ClientSettingsModelCopyWith<$Res> { bool usePosterForLibrary, bool useSystemIME, bool useTVExpandedLayout, + bool hidePasswordLogin, String? lastViewedUpdate, int? libraryPageSize, Map shortcuts}); @@ -192,6 +195,7 @@ class _$ClientSettingsModelCopyWithImpl<$Res> Object? usePosterForLibrary = null, Object? useSystemIME = null, Object? useTVExpandedLayout = null, + Object? hidePasswordLogin = null, Object? lastViewedUpdate = freezed, Object? libraryPageSize = freezed, Object? shortcuts = null, @@ -313,6 +317,10 @@ class _$ClientSettingsModelCopyWithImpl<$Res> ? _self.useTVExpandedLayout : useTVExpandedLayout // ignore: cast_nullable_to_non_nullable as bool, + hidePasswordLogin: null == hidePasswordLogin + ? _self.hidePasswordLogin + : hidePasswordLogin // ignore: cast_nullable_to_non_nullable + as bool, lastViewedUpdate: freezed == lastViewedUpdate ? _self.lastViewedUpdate : lastViewedUpdate // ignore: cast_nullable_to_non_nullable @@ -463,6 +471,7 @@ extension ClientSettingsModelPatterns on ClientSettingsModel { bool usePosterForLibrary, bool useSystemIME, bool useTVExpandedLayout, + bool hidePasswordLogin, String? lastViewedUpdate, int? libraryPageSize, Map shortcuts)? @@ -502,6 +511,7 @@ extension ClientSettingsModelPatterns on ClientSettingsModel { _that.usePosterForLibrary, _that.useSystemIME, _that.useTVExpandedLayout, + _that.hidePasswordLogin, _that.lastViewedUpdate, _that.libraryPageSize, _that.shortcuts); @@ -555,6 +565,7 @@ extension ClientSettingsModelPatterns on ClientSettingsModel { bool usePosterForLibrary, bool useSystemIME, bool useTVExpandedLayout, + bool hidePasswordLogin, String? lastViewedUpdate, int? libraryPageSize, Map shortcuts) @@ -593,6 +604,7 @@ extension ClientSettingsModelPatterns on ClientSettingsModel { _that.usePosterForLibrary, _that.useSystemIME, _that.useTVExpandedLayout, + _that.hidePasswordLogin, _that.lastViewedUpdate, _that.libraryPageSize, _that.shortcuts); @@ -645,6 +657,7 @@ extension ClientSettingsModelPatterns on ClientSettingsModel { bool usePosterForLibrary, bool useSystemIME, bool useTVExpandedLayout, + bool hidePasswordLogin, String? lastViewedUpdate, int? libraryPageSize, Map shortcuts)? @@ -683,6 +696,7 @@ extension ClientSettingsModelPatterns on ClientSettingsModel { _that.usePosterForLibrary, _that.useSystemIME, _that.useTVExpandedLayout, + _that.hidePasswordLogin, _that.lastViewedUpdate, _that.libraryPageSize, _that.shortcuts); @@ -726,6 +740,7 @@ class _ClientSettingsModel extends ClientSettingsModel this.usePosterForLibrary = false, this.useSystemIME = false, this.useTVExpandedLayout = false, + this.hidePasswordLogin = false, this.lastViewedUpdate, this.libraryPageSize, final Map shortcuts = const {}}) @@ -818,6 +833,9 @@ class _ClientSettingsModel extends ClientSettingsModel @JsonKey() final bool useTVExpandedLayout; @override + @JsonKey() + final bool hidePasswordLogin; + @override final String? lastViewedUpdate; @override final int? libraryPageSize; @@ -883,6 +901,7 @@ class _ClientSettingsModel extends ClientSettingsModel ..add(DiagnosticsProperty('usePosterForLibrary', usePosterForLibrary)) ..add(DiagnosticsProperty('useSystemIME', useSystemIME)) ..add(DiagnosticsProperty('useTVExpandedLayout', useTVExpandedLayout)) + ..add(DiagnosticsProperty('hidePasswordLogin', hidePasswordLogin)) ..add(DiagnosticsProperty('lastViewedUpdate', lastViewedUpdate)) ..add(DiagnosticsProperty('libraryPageSize', libraryPageSize)) ..add(DiagnosticsProperty('shortcuts', shortcuts)); @@ -890,7 +909,7 @@ class _ClientSettingsModel extends ClientSettingsModel @override String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'ClientSettingsModel.internal(syncPath: $syncPath, transcodeDownloadModel: $transcodeDownloadModel, position: $position, size: $size, timeOut: $timeOut, nextUpDateCutoff: $nextUpDateCutoff, updateNotificationsInterval: $updateNotificationsInterval, themeMode: $themeMode, themeColor: $themeColor, deriveColorsFromItem: $deriveColorsFromItem, amoledBlack: $amoledBlack, blurPlaceHolders: $blurPlaceHolders, blurUpcomingEpisodes: $blurUpcomingEpisodes, selectedLocale: $selectedLocale, enableMediaKeys: $enableMediaKeys, posterSize: $posterSize, pinchPosterZoom: $pinchPosterZoom, mouseDragSupport: $mouseDragSupport, requireWifi: $requireWifi, expandSideBar: $expandSideBar, showAllCollectionTypes: $showAllCollectionTypes, maxConcurrentDownloads: $maxConcurrentDownloads, schemeVariant: $schemeVariant, backgroundImage: $backgroundImage, enableBlurEffects: $enableBlurEffects, checkForUpdates: $checkForUpdates, usePosterForLibrary: $usePosterForLibrary, useSystemIME: $useSystemIME, useTVExpandedLayout: $useTVExpandedLayout, lastViewedUpdate: $lastViewedUpdate, libraryPageSize: $libraryPageSize, shortcuts: $shortcuts)'; + return 'ClientSettingsModel.internal(syncPath: $syncPath, transcodeDownloadModel: $transcodeDownloadModel, position: $position, size: $size, timeOut: $timeOut, nextUpDateCutoff: $nextUpDateCutoff, updateNotificationsInterval: $updateNotificationsInterval, themeMode: $themeMode, themeColor: $themeColor, deriveColorsFromItem: $deriveColorsFromItem, amoledBlack: $amoledBlack, blurPlaceHolders: $blurPlaceHolders, blurUpcomingEpisodes: $blurUpcomingEpisodes, selectedLocale: $selectedLocale, enableMediaKeys: $enableMediaKeys, posterSize: $posterSize, pinchPosterZoom: $pinchPosterZoom, mouseDragSupport: $mouseDragSupport, requireWifi: $requireWifi, expandSideBar: $expandSideBar, showAllCollectionTypes: $showAllCollectionTypes, maxConcurrentDownloads: $maxConcurrentDownloads, schemeVariant: $schemeVariant, backgroundImage: $backgroundImage, enableBlurEffects: $enableBlurEffects, checkForUpdates: $checkForUpdates, usePosterForLibrary: $usePosterForLibrary, useSystemIME: $useSystemIME, useTVExpandedLayout: $useTVExpandedLayout, hidePasswordLogin: $hidePasswordLogin, lastViewedUpdate: $lastViewedUpdate, libraryPageSize: $libraryPageSize, shortcuts: $shortcuts)'; } } @@ -932,6 +951,7 @@ abstract mixin class _$ClientSettingsModelCopyWith<$Res> bool usePosterForLibrary, bool useSystemIME, bool useTVExpandedLayout, + bool hidePasswordLogin, String? lastViewedUpdate, int? libraryPageSize, Map shortcuts}); @@ -982,6 +1002,7 @@ class __$ClientSettingsModelCopyWithImpl<$Res> Object? usePosterForLibrary = null, Object? useSystemIME = null, Object? useTVExpandedLayout = null, + Object? hidePasswordLogin = null, Object? lastViewedUpdate = freezed, Object? libraryPageSize = freezed, Object? shortcuts = null, @@ -1103,6 +1124,10 @@ class __$ClientSettingsModelCopyWithImpl<$Res> ? _self.useTVExpandedLayout : useTVExpandedLayout // ignore: cast_nullable_to_non_nullable as bool, + hidePasswordLogin: null == hidePasswordLogin + ? _self.hidePasswordLogin + : hidePasswordLogin // ignore: cast_nullable_to_non_nullable + as bool, lastViewedUpdate: freezed == lastViewedUpdate ? _self.lastViewedUpdate : lastViewedUpdate // ignore: cast_nullable_to_non_nullable diff --git a/lib/models/settings/client_settings_model.g.dart b/lib/models/settings/client_settings_model.g.dart index 613f3c3db..bb5a81e3e 100644 --- a/lib/models/settings/client_settings_model.g.dart +++ b/lib/models/settings/client_settings_model.g.dart @@ -57,6 +57,7 @@ _ClientSettingsModel _$ClientSettingsModelFromJson(Map json) => usePosterForLibrary: json['usePosterForLibrary'] as bool? ?? false, useSystemIME: json['useSystemIME'] as bool? ?? false, useTVExpandedLayout: json['useTVExpandedLayout'] as bool? ?? false, + hidePasswordLogin: json['hidePasswordLogin'] as bool? ?? false, lastViewedUpdate: json['lastViewedUpdate'] as String?, libraryPageSize: (json['libraryPageSize'] as num?)?.toInt(), shortcuts: (json['shortcuts'] as Map?)?.map( @@ -99,6 +100,7 @@ Map _$ClientSettingsModelToJson( 'usePosterForLibrary': instance.usePosterForLibrary, 'useSystemIME': instance.useSystemIME, 'useTVExpandedLayout': instance.useTVExpandedLayout, + 'hidePasswordLogin': instance.hidePasswordLogin, 'lastViewedUpdate': instance.lastViewedUpdate, 'libraryPageSize': instance.libraryPageSize, 'shortcuts': instance.shortcuts From 9f96ef2f874fc36dad16697b383deb83dfc46ccc Mon Sep 17 00:00:00 2001 From: v3DJG6GL <72495210+v3DJG6GL@users.noreply.github.com> Date: Thu, 23 Apr 2026 00:42:48 +0200 Subject: [PATCH 09/13] refactor(login): extract duplicated advanced-options button to local --- .../login/login_screen_credentials.dart | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/lib/screens/login/login_screen_credentials.dart b/lib/screens/login/login_screen_credentials.dart index 7ad98c332..34524f3a3 100644 --- a/lib/screens/login/login_screen_credentials.dart +++ b/lib/screens/login/login_screen_credentials.dart @@ -109,6 +109,20 @@ class _LoginScreenCredentialsState extends ConsumerState final hidePasswordLogin = ref.watch(authProvider.select((value) => value.hidePasswordLogin)) || ref.watch(clientSettingsProvider.select((value) => value.hidePasswordLogin)); + final advancedOptionsButton = IconButton.filledTonal( + onPressed: () async { + final tempSeerrUrl = ref.read(authProvider.select((value) => value.tempSeerrUrl)); + final result = await showAdvancedLoginOptionsDialog( + context, + initialSeerrUrl: tempSeerrUrl, + ); + if (result != null) { + ref.read(authProvider.notifier).setTempSeerrUrl(result); + } + }, + icon: const Icon(IconsaxPlusLinear.setting_3), + ); + ref.listen( authProvider.select((value) => value.serverLoginModel), (previous, next) { @@ -270,7 +284,8 @@ class _LoginScreenCredentialsState extends ConsumerState width: 18, height: 18, child: CircularProgressIndicator( - color: Theme.of(context).colorScheme.inversePrimary, strokeCap: StrokeCap.round), + color: Theme.of(context).colorScheme.inversePrimary, + strokeCap: StrokeCap.round), ) : Row( mainAxisSize: MainAxisSize.min, @@ -282,41 +297,14 @@ class _LoginScreenCredentialsState extends ConsumerState ), ), ), - if (FladderConfig.seerrBaseUrl?.isNotEmpty != true) - IconButton.filledTonal( - onPressed: () async { - final tempSeerrUrl = ref.read(authProvider.select((value) => value.tempSeerrUrl)); - final result = await showAdvancedLoginOptionsDialog( - context, - initialSeerrUrl: tempSeerrUrl, - ); - if (result != null) { - ref.read(authProvider.notifier).setTempSeerrUrl(result); - } - }, - icon: const Icon(IconsaxPlusLinear.setting_3), - ), + if (FladderConfig.seerrBaseUrl?.isNotEmpty != true) advancedOptionsButton, ], ), ], if (hidePasswordLogin && FladderConfig.seerrBaseUrl?.isNotEmpty != true) Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton.filledTonal( - onPressed: () async { - final tempSeerrUrl = ref.read(authProvider.select((value) => value.tempSeerrUrl)); - final result = await showAdvancedLoginOptionsDialog( - context, - initialSeerrUrl: tempSeerrUrl, - ); - if (result != null) { - ref.read(authProvider.notifier).setTempSeerrUrl(result); - } - }, - icon: const Icon(IconsaxPlusLinear.setting_3), - ), - ], + children: [advancedOptionsButton], ), if (hasQuickConnect) FilledButton( From 1af55007282e1c364bbb4eec4412f0c8ed67260d Mon Sep 17 00:00:00 2001 From: v3DJG6GL <72495210+v3DJG6GL@users.noreply.github.com> Date: Thu, 23 Apr 2026 04:09:24 +0200 Subject: [PATCH 10/13] refactor(login): unify secondary controls into centered row when URL field is hidden --- .../login/login_screen_credentials.dart | 71 ++++++++++--------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/lib/screens/login/login_screen_credentials.dart b/lib/screens/login/login_screen_credentials.dart index 34524f3a3..9f8403666 100644 --- a/lib/screens/login/login_screen_credentials.dart +++ b/lib/screens/login/login_screen_credentials.dart @@ -123,6 +123,26 @@ class _LoginScreenCredentialsState extends ConsumerState icon: const Icon(IconsaxPlusLinear.setting_3), ); + final backButton = AspectRatio( + aspectRatio: 1, + child: IconButton.filledTonal( + onPressed: () => provider.goUserSelect(), + icon: const Icon(IconsaxPlusLinear.arrow_left_2), + ), + ); + + final refreshButton = AspectRatio( + aspectRatio: 1, + child: Tooltip( + message: context.localized.retrievePublicListOfUsers, + waitDuration: const Duration(seconds: 1), + child: IconButton.filled( + onPressed: () => provider.setServer(serverTextController.text), + icon: const Icon(IconsaxPlusLinear.refresh), + ), + ), + ); + ref.listen( authProvider.select((value) => value.serverLoginModel), (previous, next) { @@ -137,22 +157,14 @@ class _LoginScreenCredentialsState extends ConsumerState crossAxisAlignment: CrossAxisAlignment.center, spacing: 16, children: [ - IntrinsicHeight( - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - spacing: 8, - children: [ - AspectRatio( - aspectRatio: 1, - child: IconButton.filledTonal( - onPressed: () => provider.goUserSelect(), - icon: const Icon( - IconsaxPlusLinear.arrow_left_2, - ), - ), - ), - if (!hasBaseUrl) + if (!hasBaseUrl) + IntrinsicHeight( + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + spacing: 8, + children: [ + if (existingUsers.isNotEmpty) backButton, Expanded( child: OutlinedTextField( controller: serverTextController, @@ -165,22 +177,10 @@ class _LoginScreenCredentialsState extends ConsumerState errorText: urlError, ), ), - AspectRatio( - aspectRatio: 1, - child: Tooltip( - message: context.localized.retrievePublicListOfUsers, - waitDuration: const Duration(seconds: 1), - child: IconButton.filled( - onPressed: () => provider.setServer(serverTextController.text), - icon: const Icon( - IconsaxPlusLinear.refresh, - ), - ), - ), - ), - ], + refreshButton, + ], + ), ), - ), if (serverCredentials == null) Column( mainAxisSize: MainAxisSize.max, @@ -301,10 +301,15 @@ class _LoginScreenCredentialsState extends ConsumerState ], ), ], - if (hidePasswordLogin && FladderConfig.seerrBaseUrl?.isNotEmpty != true) + if (hasBaseUrl) Row( mainAxisAlignment: MainAxisAlignment.center, - children: [advancedOptionsButton], + spacing: 8, + children: [ + if (existingUsers.isNotEmpty) backButton, + refreshButton, + if (hidePasswordLogin && FladderConfig.seerrBaseUrl?.isNotEmpty != true) advancedOptionsButton, + ], ), if (hasQuickConnect) FilledButton( From cd00524b26b37ea867ca4d7fd9e7b4fac8c90eaa Mon Sep 17 00:00:00 2001 From: v3DJG6GL <72495210+v3DJG6GL@users.noreply.github.com> Date: Thu, 23 Apr 2026 04:33:14 +0200 Subject: [PATCH 11/13] fix(login): render secondary-controls row correctly without AspectRatio layout error --- .../login/login_screen_credentials.dart | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/lib/screens/login/login_screen_credentials.dart b/lib/screens/login/login_screen_credentials.dart index 9f8403666..9bc7d684f 100644 --- a/lib/screens/login/login_screen_credentials.dart +++ b/lib/screens/login/login_screen_credentials.dart @@ -123,23 +123,17 @@ class _LoginScreenCredentialsState extends ConsumerState icon: const Icon(IconsaxPlusLinear.setting_3), ); - final backButton = AspectRatio( - aspectRatio: 1, - child: IconButton.filledTonal( - onPressed: () => provider.goUserSelect(), - icon: const Icon(IconsaxPlusLinear.arrow_left_2), - ), + final backButton = IconButton.filledTonal( + onPressed: () => provider.goUserSelect(), + icon: const Icon(IconsaxPlusLinear.arrow_left_2), ); - final refreshButton = AspectRatio( - aspectRatio: 1, - child: Tooltip( - message: context.localized.retrievePublicListOfUsers, - waitDuration: const Duration(seconds: 1), - child: IconButton.filled( - onPressed: () => provider.setServer(serverTextController.text), - icon: const Icon(IconsaxPlusLinear.refresh), - ), + final refreshButton = Tooltip( + message: context.localized.retrievePublicListOfUsers, + waitDuration: const Duration(seconds: 1), + child: IconButton.filled( + onPressed: () => provider.setServer(serverTextController.text), + icon: const Icon(IconsaxPlusLinear.refresh), ), ); @@ -164,7 +158,7 @@ class _LoginScreenCredentialsState extends ConsumerState crossAxisAlignment: CrossAxisAlignment.stretch, spacing: 8, children: [ - if (existingUsers.isNotEmpty) backButton, + if (existingUsers.isNotEmpty) AspectRatio(aspectRatio: 1, child: backButton), Expanded( child: OutlinedTextField( controller: serverTextController, @@ -177,7 +171,7 @@ class _LoginScreenCredentialsState extends ConsumerState errorText: urlError, ), ), - refreshButton, + AspectRatio(aspectRatio: 1, child: refreshButton), ], ), ), From 1d6cb91654e622631a78d9413a41a5f70ee1789d Mon Sep 17 00:00:00 2001 From: v3DJG6GL <72495210+v3DJG6GL@users.noreply.github.com> Date: Thu, 23 Apr 2026 05:02:00 +0200 Subject: [PATCH 12/13] =?UTF-8?q?fix(login):=20keep=20back/refresh/?= =?UTF-8?q?=E2=9A=99=20reachable=20across=20all=20BASE=5FURL=20=C3=97=20HI?= =?UTF-8?q?DE=5FPASSWORD=5FLOGIN=20combos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/login/login_screen_credentials.dart | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/screens/login/login_screen_credentials.dart b/lib/screens/login/login_screen_credentials.dart index 9bc7d684f..29efc52d8 100644 --- a/lib/screens/login/login_screen_credentials.dart +++ b/lib/screens/login/login_screen_credentials.dart @@ -151,7 +151,7 @@ class _LoginScreenCredentialsState extends ConsumerState crossAxisAlignment: CrossAxisAlignment.center, spacing: 16, children: [ - if (!hasBaseUrl) + if (!(hasBaseUrl && hidePasswordLogin)) IntrinsicHeight( child: Row( mainAxisAlignment: MainAxisAlignment.start, @@ -295,16 +295,21 @@ class _LoginScreenCredentialsState extends ConsumerState ], ), ], - if (hasBaseUrl) + if (hasBaseUrl && hidePasswordLogin) Row( mainAxisAlignment: MainAxisAlignment.center, spacing: 8, children: [ if (existingUsers.isNotEmpty) backButton, refreshButton, - if (hidePasswordLogin && FladderConfig.seerrBaseUrl?.isNotEmpty != true) advancedOptionsButton, + if (FladderConfig.seerrBaseUrl?.isNotEmpty != true) advancedOptionsButton, ], ), + if (!hasBaseUrl && hidePasswordLogin && FladderConfig.seerrBaseUrl?.isNotEmpty != true) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [advancedOptionsButton], + ), if (hasQuickConnect) FilledButton( onPressed: () async { From f9d0d448ce485a4e6657a3961cf031289127058a Mon Sep 17 00:00:00 2001 From: v3DJG6GL <72495210+v3DJG6GL@users.noreply.github.com> Date: Thu, 23 Apr 2026 05:39:36 +0200 Subject: [PATCH 13/13] fix(login): hide URL field whenever BASE_URL is set, not only when HIDE_PASSWORD_LOGIN is also set --- .../login/login_screen_credentials.dart | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/screens/login/login_screen_credentials.dart b/lib/screens/login/login_screen_credentials.dart index 29efc52d8..885494dd6 100644 --- a/lib/screens/login/login_screen_credentials.dart +++ b/lib/screens/login/login_screen_credentials.dart @@ -159,18 +159,19 @@ class _LoginScreenCredentialsState extends ConsumerState spacing: 8, children: [ if (existingUsers.isNotEmpty) AspectRatio(aspectRatio: 1, child: backButton), - Expanded( - child: OutlinedTextField( - controller: serverTextController, - onSubmitted: (value) => provider.setServer(value), - autoFillHints: const [AutofillHints.url], - keyboardType: TextInputType.url, - autocorrect: false, - textInputAction: TextInputAction.go, - label: context.localized.server, - errorText: urlError, + if (!hasBaseUrl) + Expanded( + child: OutlinedTextField( + controller: serverTextController, + onSubmitted: (value) => provider.setServer(value), + autoFillHints: const [AutofillHints.url], + keyboardType: TextInputType.url, + autocorrect: false, + textInputAction: TextInputAction.go, + label: context.localized.server, + errorText: urlError, + ), ), - ), AspectRatio(aspectRatio: 1, child: refreshButton), ], ),