reactive_forms 5.0.1 copy "reactive_forms: ^5.0.1" to clipboard
reactive_forms: ^5.0.1 copied to clipboard

outdated

This is a model-driven approach to handling form inputs and validations, heavily inspired in Angular Reactive Forms.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:reactive_forms/reactive_forms.dart';

void main() {
  runApp(ReactiveFormsApp());
}

class ReactiveFormsApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: customTheme,
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  // We recommend to use Reactive Form with Provider plugin or any other
  // state management library and not declare FormGroup directly in a
  // stateless widget.
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final form = FormGroup({
    'email': FormControl(
      validators: [
        Validators.required,
        Validators.email,
      ],
      asyncValidators: [_uniqueEmail],
    ),
    'password': FormControl(validators: [
      Validators.required,
      Validators.minLength(8),
    ]),
    'passwordConfirmation': FormControl(),
    'rememberMe': FormControl(value: false),
    'progress': FormControl<double>(
      value: 50.0,
      validators: [Validators.min(50.0)],
    ),
    'dateTime': FormControl<DateTime>(value: DateTime.now()),
    'time': FormControl<TimeOfDay>(value: TimeOfDay.now()),
  }, validators: [
    Validators.mustMatch('password', 'passwordConfirmation')
  ]);

  FormControl get password => this.form.control('password');

  FormControl get passwordConfirmation =>
      this.form.control('passwordConfirmation');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Reactive Forms'),
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0),
          child: ReactiveForm(
            formGroup: this.form,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                ReactiveTextField(
                  formControlName: 'email',
                  decoration: InputDecoration(
                    labelText: 'Email',
                    suffixIcon: ReactiveStatusListenableBuilder(
                      formControlName: 'email',
                      builder: (context, control, child) {
                        return control.pending
                            ? Padding(
                                padding: const EdgeInsets.all(8.0),
                                child: CircularProgressIndicator(
                                  strokeWidth: 2,
                                ),
                              )
                            : Container(width: 0);
                      },
                    ),
                  ),
                  validationMessages: {
                    ValidationMessage.required: 'The email must not be empty',
                    ValidationMessage.email:
                        'The email value must be a valid email',
                    'unique': 'This email is already in use',
                  },
                  textInputAction: TextInputAction.next,
                  onSubmitted: () => this.password.focus(),
                ),
                SizedBox(height: 24.0),
                ReactiveTextField(
                  formControlName: 'password',
                  decoration: InputDecoration(
                    labelText: 'Password',
                  ),
                  obscureText: true,
                  validationMessages: {
                    ValidationMessage.required:
                        'The password must not be empty',
                    ValidationMessage.minLength:
                        'The password must be at least 8 characters',
                  },
                  textInputAction: TextInputAction.next,
                  onSubmitted: () => this.passwordConfirmation.focus(),
                ),
                SizedBox(height: 24.0),
                ReactiveTextField(
                  formControlName: 'passwordConfirmation',
                  decoration: InputDecoration(
                    labelText: 'Confirm Password',
                  ),
                  obscureText: true,
                  validationMessages: {
                    ValidationMessage.mustMatch:
                        'Password confirmation must match',
                  },
                ),
                SizedBox(height: 24.0),
                ReactiveFormConsumer(
                  builder: (context, form, child) {
                    return RaisedButton(
                      child: Text('Sign Up'),
                      onPressed: form.valid
                          ? () {
                              print(form.value);
                            }
                          : null,
                    );
                  },
                ),
                RaisedButton(
                  child: Text('Reset all'),
                  onPressed: () => form.resetState({
                    'email': ControlState(value: 'johnDoe', disabled: true),
                    'progress': ControlState(value: 50.0),
                    'rememberMe': ControlState(value: false),
                  }),
                ),
                ReactiveSwitch(formControlName: 'rememberMe'),
                ReactiveCheckbox(formControlName: 'rememberMe'),
                ReactiveDropdownField<bool>(
                  formControlName: 'rememberMe',
                  hint: Text('Want to stay logged in?'),
                  decoration: InputDecoration(labelText: 'Remember me'),
                  items: [
                    DropdownMenuItem(
                      value: true,
                      child: Text('Yes'),
                    ),
                    DropdownMenuItem(
                      value: false,
                      child: Text('No'),
                    ),
                  ],
                ),
                ListTile(
                  title: Text('Remember me'),
                  trailing: ReactiveRadio(
                    formControlName: 'rememberMe',
                    value: true,
                  ),
                ),
                ListTile(
                  title: Text('Don\'t Remember me'),
                  trailing: ReactiveRadio(
                    formControlName: 'rememberMe',
                    value: false,
                  ),
                ),
                SizedBox(height: 24.0),
                ReactiveValueListenableBuilder<double>(
                  formControlName: 'progress',
                  builder: (context, control, child) {
                    return control.value == null
                        ? Text('Progress not set')
                        : Text(
                            'Progress set to ${control.value.toStringAsFixed(2)}%');
                  },
                ),
                ReactiveSlider(
                  formControlName: 'progress',
                  max: 100,
                  divisions: 100,
                  labelBuilder: (double value) =>
                      '${value.toStringAsFixed(2)}%',
                ),
                SizedBox(height: 24.0),
                ReactiveTextField(
                  formControlName: 'progress',
                  keyboardType: TextInputType.number,
                  validationMessages: {
                    ValidationMessage.min:
                        'A value lower than 50.00 is not accepted',
                  },
                ),
                SizedBox(height: 24.0),
                ReactiveTextField(
                  formControlName: 'dateTime',
                  readOnly: true,
                  decoration: InputDecoration(
                    labelText: 'Birthday',
                    suffixIcon: ReactiveDatePicker(
                      formControlName: 'dateTime',
                      firstDate: DateTime(1985),
                      lastDate: DateTime(2030),
                      builder: (context, picker, child) {
                        return IconButton(
                          onPressed: picker.showPicker,
                          icon: Icon(Icons.date_range),
                        );
                      },
                    ),
                  ),
                ),
                SizedBox(height: 24.0),
                ReactiveTextField(
                  formControlName: 'time',
                  readOnly: true,
                  decoration: InputDecoration(
                    labelText: 'Birthday time',
                    suffixIcon: ReactiveTimePicker(
                      formControlName: 'time',
                      builder: (context, picker, child) {
                        return IconButton(
                          onPressed: picker.showPicker,
                          icon: Icon(Icons.access_time),
                        );
                      },
                    ),
                  ),
                ),
                SizedBox(height: 24.0),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

/// Async validator in use emails example
const inUseEmails = ['johndoe@email.com', 'john@email.com'];

/// Async validator example that simulates a request to a server
/// to validate if the email of the user is unique.
Future<Map<String, dynamic>> _uniqueEmail(AbstractControl control) async {
  final error = {'unique': false};

  final emailAlreadyInUse = await Future.delayed(
    Duration(seconds: 5), // a delay to simulate a time consuming operation
    () => inUseEmails.contains(control.value),
  );

  if (emailAlreadyInUse) {
    control.markAsTouched();
    return error;
  }

  return null;
}

final customTheme = ThemeData.light().copyWith(
  inputDecorationTheme: InputDecorationTheme(
    border: OutlineInputBorder(),
    floatingLabelBehavior: FloatingLabelBehavior.auto,
    alignLabelWithHint: true,
  ),
);
828
likes
0
pub points
98%
popularity

Publisher

unverified uploader

This is a model-driven approach to handling form inputs and validations, heavily inspired in Angular Reactive Forms.

Repository (GitHub)
View/report issues

License

unknown (LICENSE)

Dependencies

flutter

More

Packages that depend on reactive_forms