I am developing a flutter application. I listed the requirements for the password on the registration page in the app. As the user types his password, I want to instantly listen to whether these requirements are met and update the text. First of all, my codes are as follows:
register_view.dart:
class RegisterView extends StatefulWidget { const RegisterView({super.key}); @override State<RegisterView> createState() => _RegisterViewState();}class _RegisterViewState extends State<RegisterView> with RegisterViewMixin { final RegisterViewModel _viewModel = RegisterViewModel(); final _formKey = GlobalKey<FormState>(); @override Widget build(BuildContext context) { return CustomScaffold( onPadding: true, padding: const EdgeInsets.symmetric(horizontal: 20), appBar: _buildAppBar(), body: _buildBody(), ); } Widget _buildBody() { return SingleChildScrollView( child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 50), const ToggLogoWidget(width: TextSizeEnum.xxlarge), const SizedBox(height: 50), const CustomText( text: "Tru.ID hesabınızı oluşturun", color: AppColors.skyBlueText, fontSize: TextSizeEnum.medium, fontWeight: FontWeight.bold, ), const SizedBox(height: 20), CustomTextFormField( hintText: "İsim", validator: (value) { if (value == null || value.isEmpty) { return 'İsim boş bırakılamaz'; } return null; }, ), _buildSizedBox(), CustomTextFormField( hintText: "Soyisim", validator: (value) { if (value == null || value.isEmpty) { return 'Soyisim boş bırakılamaz'; } return null; }, ), _buildSizedBox(), CustomTextFormField( hintText: "E-Posta", keyboardType: TextInputType.emailAddress, validator: (value) { if (value == null || value.isEmpty) { return 'E-Posta adresi boş bırakılamaz'; } else if (!StringUtil.isValidEmail(value)) { return 'Geçerli bir e-posta adresi giriniz'; } return null; }, ), const SizedBox(height: 6), const CustomText( icon: Icon(Icons.info, color: AppColors.grey), text: "Togg bu e-posta adresine bir doğrulama gönderecektir.", fontSize: TextSizeEnum.verySmall, ), _buildSizedBox(), CustomTextFormField( controller: _viewModel.passwordController, hintText: "Şifre", obscureText: true, validator: (value) { if (value == null || value.isEmpty) { return 'Şifre boş bırakılamaz'; } else if (!StringUtil.isPassword8Chars(value)) { return ''; } else if (!StringUtil.hasNumber(value)) { return ''; } else if (!StringUtil.hasUpper(value)) { return ''; } else if (!StringUtil.hasLower(value)) { return ''; } else if (!StringUtil.hasSpecialCharacter(value)) { return ''; } return null; }, ), const SizedBox(height: 10), PasswordValidationWidget( password: _viewModel.passwordController.text, confirmPassword: _viewModel.confirmPasswordController.text, ), _buildSizedBox(), CustomTextFormField( controller: _viewModel.confirmPasswordController, hintText: "Şifre Yeniden", obscureText: true, validator: (value) { if (value == null || value.isEmpty) { return 'Şifre boş bırakılamaz'; } else if (!StringUtil.isPassword8Chars(value)) { return ''; } else if (!StringUtil.hasNumber(value)) { return ''; } else if (!StringUtil.hasUpper(value)) { return ''; } else if (!StringUtil.hasLower(value)) { return ''; } else if (!StringUtil.hasSpecialCharacter(value)) { return ''; } return null; }, ), _buildSizedBox(), CustomDarkButton( text: "Hesap Oluştur", active: true, onPressed: () { if (_formKey.currentState!.validate()) { //basarili } else { // basarisiz } }, ) ], ), ), ); } PreferredSizeWidget _buildAppBar() { return const WelcomeAppBar(); } SizedBox _buildSizedBox() { return const SizedBox(height: 20); }}password_validation_widget.dart:
class PasswordValidationWidget extends StatefulWidget { final String password; final String confirmPassword; const PasswordValidationWidget({ Key? key, required this.password, required this.confirmPassword, }) : super(key: key); @override State<PasswordValidationWidget> createState() => _PasswordValidationWidgetState();}class _PasswordValidationWidgetState extends State<PasswordValidationWidget> { @override Widget build(BuildContext context) { return Column( children: [ _buildValidationRow("En az 8 karakter uzunluğunda", StringUtil.isPassword8Chars(widget.password)), _buildValidationRow("En az 1 özel karakter içermeli", StringUtil.hasSpecialCharacter(widget.password)), _buildValidationRow("En az 1 sayı içermeli", StringUtil.hasNumber(widget.password)), _buildValidationRow("En az 1 büyük harf içermeli", StringUtil.hasUpper(widget.password)), _buildValidationRow("En az 1 küçük harf içermeli", StringUtil.hasLower(widget.password)), _buildValidationRow("Şifreler eşleşmeli", StringUtil.arePasswordsMatching(widget.password, widget.confirmPassword)), ], ); } Widget _buildValidationRow(String text, bool isValid) { return Row( children: [ Icon( isValid ? Icons.check : Icons.close, color: isValid ? Colors.green : Colors.red, ), const SizedBox(width: 8), Text(text), ], ); }}strings_util.dart:
class StringUtil { static bool isPassword8Chars(String password) => password.length >= 8; static bool hasSpecialCharacter(String password) => password.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]')); static bool hasNumber(String password) => password.contains(RegExp(r'\d')); static bool hasUpper(String password) => password.contains(RegExp(r'[A-Z]')); static bool hasLower(String password) => password.contains(RegExp(r'[a-z]')); static bool arePasswordsMatching(String password, String confirmPassword) => confirmPassword == password;}register_view_model.dart:
class RegisterViewModel extends ChangeNotifier { final TextEditingController passwordController = TextEditingController(); final TextEditingController confirmPasswordController = TextEditingController();}register_view_mixin.dart:
mixin RegisterViewMixin on State<RegisterView> { final RegisterViewModel _viewModel = RegisterViewModel(); @override void initState() { super.initState(); _viewModel.passwordController.addListener(_updatePasswordValidation); _viewModel.confirmPasswordController.addListener(_updatePasswordValidation); } @override void dispose() { _viewModel.passwordController.removeListener(_updatePasswordValidation); _viewModel.confirmPasswordController.removeListener(_updatePasswordValidation); super.dispose(); } void _updatePasswordValidation() { print("test"); // not working setState(() {}); }}When I transferred the codes from the mixin side to the view side, I was able to achieve what I wanted, but I want these codes to remain in the mixin.