GiancarloCode / form_bloc

🔥 Dart and Flutter Package 🔥 Easy Form State Management using BLoC pattern 🔥 Wizard/stepper forms, asynchronous validation, dynamic and conditional fields, submission progress, serialization and more! 🔥
https://GiancarloCode.github.io/form_bloc/
461 stars 197 forks source link

How to validate on unfocus? #298

Open woellij opened 2 years ago

woellij commented 2 years ago

I'm trying to validate on unfocus, but calling SingleFieldBloc.validate does not emit a new state. Also after input change, a state is emitted, saying it is not dirty. Of course autoValidate is deactivated, but that's the whole point (with it on the validation works)

How can i manually call validate on SingleFieldBlocs and have the error displayed in the TextFieldBlocBuilder?

I've been banging my head against this for hours now ...

the implementation of validate seems to be completely besides the point, what the method-name is conveying ...

saawhitelife commented 2 years ago

You may mind this example:

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(),
      home: BlocProvider<MyFormBloc>(
        create: (_) => MyFormBloc(),
        child: const MyHomePage(title: 'Flutter Demo Home Page'),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final emailFocusNode = FocusNode();

  @override
  void initState() {
    super.initState();
    emailFocusNode.addListener(() {
      if (emailFocusNode.hasFocus) {
        formBloc.removeEmailValidators();
      } else {
        formBloc.addEmailValidators();
      }
    });
  }

  MyFormBloc get formBloc => context.read<MyFormBloc>();

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        FocusScope.of(context).unfocus();
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              TextFieldBlocBuilder(
                obscureText: false,
                focusNode: emailFocusNode,
                textFieldBloc: formBloc.emailFieldBloc,
                suffixButton: SuffixButton.obscureText,
                decoration: const InputDecoration(
                  labelText: 'TextFieldBlocBuilder',
                  prefixIcon: Icon(Icons.text_fields),
                  errorStyle: TextStyle(color: Colors.red),
                  border: OutlineInputBorder(),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    emailFocusNode.dispose();
    super.dispose();
  }
}

class MyFormBloc extends FormBloc {
  MyFormBloc() {
    addFieldBloc(fieldBloc: emailFieldBloc);
  }

  final emailValidators = [
    (text) => 'error here',
  ];
  final emailFieldBloc = TextFieldBloc();

  void addEmailValidators() {
    emailFieldBloc.updateValidators(emailValidators);
  }

  void removeEmailValidators() {
    emailFieldBloc.updateValidators([]);
  }

  @override
  void submit() {
    addEmailValidators();
    super.submit();
  }

  @override
  FutureOr<void> onSubmitting() {
    throw UnimplementedError();
  }
}
woellij commented 2 years ago

Hi @saawhitelife ,

thanks for getting back to me.
I had this on my plate for a while and while trying to implement it i faced a different issue where with the docker built page, the validation stopped working alltogether (maybe something to do with different pub versions)

Because of that i switched over to reactive_forms (https://pub.dev/packages/reactive_forms) which has the notion of only validating "touched" fields by default. Of course without the utilities of form_bloc.

otto-dev commented 2 years ago

I am also interested in this. Having fields light up in red once the user starts typing is too agressive.

While not the same, as an alternative workaround, you can call field.updateValidators(...) on the submit button press, adding all your validators, to only validate once the user clicks "submit".

ingmferrer commented 1 year ago

My proposal: #331