diff --git a/web/repertory/lib/helpers.dart b/web/repertory/lib/helpers.dart index d5498f5e..e1053ca6 100644 --- a/web/repertory/lib/helpers.dart +++ b/web/repertory/lib/helpers.dart @@ -468,13 +468,15 @@ Scaffold createCommonScaffold( InputDecoration createCommonDecoration( ColorScheme colorScheme, String label, { - IconData? icon, bool filled = true, + String? hintText, + IconData? icon, }) => InputDecoration( labelText: label, prefixIcon: icon == null ? null : Icon(icon), filled: filled, fillColor: colorScheme.primary.withValues(alpha: constants.primaryAlpha), + hintText: hintText, border: OutlineInputBorder( borderRadius: BorderRadius.circular(constants.borderRadiusSmall), borderSide: BorderSide.none, diff --git a/web/repertory/lib/screens/add_mount_screen.dart b/web/repertory/lib/screens/add_mount_screen.dart index 02718b3e..67705147 100644 --- a/web/repertory/lib/screens/add_mount_screen.dart +++ b/web/repertory/lib/screens/add_mount_screen.dart @@ -10,6 +10,7 @@ import 'package:repertory/models/auth.dart'; import 'package:repertory/models/mount.dart'; import 'package:repertory/models/mount_list.dart'; import 'package:repertory/types/mount_config.dart'; +import 'package:repertory/utils/safe_set_state_mixin.dart'; import 'package:repertory/widgets/app_dropdown.dart'; import 'package:repertory/widgets/mount_settings.dart'; @@ -21,7 +22,8 @@ class AddMountScreen extends StatefulWidget { State createState() => _AddMountScreenState(); } -class _AddMountScreenState extends State { +class _AddMountScreenState extends State + with SafeSetState { Mount? _mount; final _mountNameController = TextEditingController(); String _mountType = ""; @@ -161,8 +163,9 @@ class _AddMountScreenState extends State { decoration: createCommonDecoration( scheme, 'Configuration Name', + hintText: 'Enter a unique name', icon: Icons.drive_file_rename_outline, - ).copyWith(hintText: 'Enter a unique name'), + ), ), ], if (_mount != null) ...[ @@ -323,10 +326,4 @@ class _AddMountScreenState extends State { success ? "Success" : "Provider settings are invalid!", ); } - - @override - void setState(VoidCallback fn) { - if (!mounted) return; - super.setState(fn); - } } diff --git a/web/repertory/lib/screens/auth_screen.dart b/web/repertory/lib/screens/auth_screen.dart index 1344e7b6..daa8edb5 100644 --- a/web/repertory/lib/screens/auth_screen.dart +++ b/web/repertory/lib/screens/auth_screen.dart @@ -7,6 +7,7 @@ import 'package:repertory/constants.dart' as constants; import 'package:repertory/helpers.dart'; import 'package:repertory/models/auth.dart'; import 'package:repertory/models/settings.dart'; +import 'package:repertory/widgets/app_text_field.dart'; import 'package:repertory/widgets/aurora_sweep.dart'; class AuthScreen extends StatefulWidget { @@ -226,15 +227,12 @@ class _AuthScreenState extends State { const SizedBox( height: constants.padding * 2.0, ), - TextFormField( + AppTextField( autofocus: true, controller: _userController, + icon: Icons.person, + labelText: 'Username', textInputAction: TextInputAction.next, - decoration: createCommonDecoration( - scheme, - 'Username', - icon: Icons.person, - ), validator: (v) { if (v == null || v.trim().isEmpty) { return 'Enter your username'; @@ -246,33 +244,27 @@ class _AuthScreenState extends State { }, ), const SizedBox(height: constants.padding), - - TextFormField( + AppTextField( controller: _passwordController, + icon: Icons.lock, + labelText: 'Password', obscureText: _obscure, + suffixIcon: IconButton( + tooltip: _obscure + ? 'Show password' + : 'Hide password', + icon: Icon( + _obscure + ? Icons.visibility + : Icons.visibility_off, + ), + onPressed: () { + setState(() { + _obscure = !_obscure; + }); + }, + ), textInputAction: TextInputAction.go, - decoration: - createCommonDecoration( - scheme, - 'Password', - icon: Icons.lock, - ).copyWith( - suffixIcon: IconButton( - tooltip: _obscure - ? 'Show password' - : 'Hide password', - icon: Icon( - _obscure - ? Icons.visibility - : Icons.visibility_off, - ), - onPressed: () { - setState(() { - _obscure = !_obscure; - }); - }, - ), - ), validator: (v) { if (v == null || v.isEmpty) { return 'Enter your password'; diff --git a/web/repertory/lib/screens/edit_mount_screen.dart b/web/repertory/lib/screens/edit_mount_screen.dart index 9cf6be3a..f8a89158 100644 --- a/web/repertory/lib/screens/edit_mount_screen.dart +++ b/web/repertory/lib/screens/edit_mount_screen.dart @@ -8,6 +8,7 @@ import 'package:repertory/constants.dart' as constants; import 'package:repertory/helpers.dart'; import 'package:repertory/models/auth.dart'; import 'package:repertory/models/mount.dart'; +import 'package:repertory/utils/safe_set_state_mixin.dart'; import 'package:repertory/widgets/mount_settings.dart'; class EditMountScreen extends StatefulWidget { @@ -19,7 +20,8 @@ class EditMountScreen extends StatefulWidget { State createState() => _EditMountScreenState(); } -class _EditMountScreenState extends State { +class _EditMountScreenState extends State + with SafeSetState { bool _showAdvanced = false; @override @@ -186,10 +188,4 @@ class _EditMountScreenState extends State { ), ]); } - - @override - void setState(VoidCallback fn) { - if (!mounted) return; - super.setState(fn); - } } diff --git a/web/repertory/lib/screens/edit_settings_screen.dart b/web/repertory/lib/screens/edit_settings_screen.dart index 2e12e3dd..6197573f 100644 --- a/web/repertory/lib/screens/edit_settings_screen.dart +++ b/web/repertory/lib/screens/edit_settings_screen.dart @@ -9,6 +9,7 @@ import 'package:provider/provider.dart'; import 'package:repertory/constants.dart' as constants; import 'package:repertory/helpers.dart'; import 'package:repertory/models/auth.dart'; +import 'package:repertory/utils/safe_set_state_mixin.dart'; import 'package:repertory/widgets/ui_settings.dart'; class EditSettingsScreen extends StatefulWidget { @@ -19,7 +20,8 @@ class EditSettingsScreen extends StatefulWidget { State createState() => _EditSettingsScreenState(); } -class _EditSettingsScreenState extends State { +class _EditSettingsScreenState extends State + with SafeSetState { @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; @@ -174,12 +176,4 @@ class _EditSettingsScreenState extends State { return {}; } - - @override - void setState(VoidCallback fn) { - if (!mounted) { - return; - } - super.setState(fn); - } } diff --git a/web/repertory/lib/utils/safe_set_state_mixin.dart b/web/repertory/lib/utils/safe_set_state_mixin.dart new file mode 100644 index 00000000..d961949e --- /dev/null +++ b/web/repertory/lib/utils/safe_set_state_mixin.dart @@ -0,0 +1,14 @@ +// safe_set_state_mixin.dart + +import 'package:flutter/widgets.dart'; + +mixin SafeSetState on State { + @override + void setState(VoidCallback fn) { + if (!mounted) { + return; + } + + super.setState(fn); + } +} diff --git a/web/repertory/lib/widgets/app_text_field.dart b/web/repertory/lib/widgets/app_text_field.dart new file mode 100644 index 00000000..60b326ad --- /dev/null +++ b/web/repertory/lib/widgets/app_text_field.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:repertory/helpers.dart' as helpers; + +class AppTextField extends StatelessWidget { + const AppTextField({ + super.key, + this.autofocus = false, + this.controller, + this.enabled = true, + this.hintText, + this.icon, + this.keyboardType, + this.labelText, + this.maxLines = 1, + this.obscureText = false, + this.onChanged, + this.onFieldSubmitted, + this.suffixIcon, + this.textInputAction, + this.validator, + }); + + final bool autofocus; + final TextEditingController? controller; + final bool enabled; + final String? hintText; + final IconData? icon; + final TextInputType? keyboardType; + final String? labelText; + final int? maxLines; + final bool obscureText; + final ValueChanged? onChanged; + final ValueChanged? onFieldSubmitted; + final Widget? suffixIcon; + final TextInputAction? textInputAction; + final FormFieldValidator? validator; + + @override + Widget build(BuildContext context) { + final scheme = Theme.of(context).colorScheme; + + final decoration = helpers + .createCommonDecoration( + scheme, + filled: true, + hintText: hintText, + icon: icon, + labelText ?? '', + ) + .copyWith(suffixIcon: suffixIcon); + + return TextFormField( + autofocus: autofocus, + controller: controller, + decoration: decoration, + enabled: enabled, + keyboardType: keyboardType, + maxLines: maxLines, + obscureText: obscureText, + onChanged: onChanged, + onFieldSubmitted: onFieldSubmitted, + textInputAction: textInputAction, + validator: validator, + ); + } +} diff --git a/web/repertory/lib/widgets/mount_settings.dart b/web/repertory/lib/widgets/mount_settings.dart index d916cbc4..81d65751 100644 --- a/web/repertory/lib/widgets/mount_settings.dart +++ b/web/repertory/lib/widgets/mount_settings.dart @@ -1042,8 +1042,14 @@ class _MountSettingsWidgetState extends State { @override void setState(VoidCallback fn) { - if (!mounted) return; - if (widget.onChanged != null) widget.onChanged!(); + if (!mounted) { + return; + } + + if (widget.onChanged != null) { + widget.onChanged!(); + } + super.setState(fn); } } diff --git a/web/repertory/lib/widgets/mount_widget.dart b/web/repertory/lib/widgets/mount_widget.dart index f33c1333..49561dd9 100644 --- a/web/repertory/lib/widgets/mount_widget.dart +++ b/web/repertory/lib/widgets/mount_widget.dart @@ -6,6 +6,7 @@ import 'package:provider/provider.dart'; import 'package:repertory/constants.dart' as constants; import 'package:repertory/helpers.dart'; import 'package:repertory/models/mount.dart'; +import 'package:repertory/utils/safe_set_state_mixin.dart'; class MountWidget extends StatefulWidget { const MountWidget({super.key}); @@ -14,7 +15,8 @@ class MountWidget extends StatefulWidget { State createState() => _MountWidgetState(); } -class _MountWidgetState extends State { +class _MountWidgetState extends State + with SafeSetState { bool _enabled = true; bool _editEnabled = true; Timer? _timer; @@ -286,14 +288,6 @@ class _MountWidgetState extends State { _timer = null; super.dispose(); } - - @override - void setState(VoidCallback fn) { - if (!mounted) { - return; - } - super.setState(fn); - } } class _FramedBox extends StatelessWidget { diff --git a/web/repertory/lib/widgets/ui_settings.dart b/web/repertory/lib/widgets/ui_settings.dart index 09cdf29c..4b72f3a1 100644 --- a/web/repertory/lib/widgets/ui_settings.dart +++ b/web/repertory/lib/widgets/ui_settings.dart @@ -17,6 +17,7 @@ import 'package:repertory/helpers.dart' trimNotEmptyValidator; import 'package:repertory/models/auth.dart'; import 'package:repertory/settings.dart'; +import 'package:repertory/utils/safe_set_state_mixin.dart'; import 'package:settings_ui/settings_ui.dart'; class UISettingsWidget extends StatefulWidget { @@ -34,7 +35,8 @@ class UISettingsWidget extends StatefulWidget { State createState() => _UISettingsWidgetState(); } -class _UISettingsWidgetState extends State { +class _UISettingsWidgetState extends State + with SafeSetState { @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; @@ -164,12 +166,4 @@ class _UISettingsWidgetState extends State { super.dispose(); } - - @override - void setState(VoidCallback fn) { - if (!mounted) { - return; - } - super.setState(fn); - } }