How to pass user's TextFormField input to a button in a different class in flutter

0

I am creating a flutter app. For the code reusability, I need to differentiate Email and password forms and Login Button, I am not sure how to properly to pass the input from textformfield to the button for the form to be validated, when clicking it. Here's my code. Note that im a beginner in flutter.

//This is my EmailTextForm class:

    class EmailTextForm extends StatelessWidget {
    String email;

   EmailTextForm({Key key, this.email}) : super(key: key);

  Widget build(BuildContext context) {

return Container(
    width: 370.0,
    height: 54.0,
    child: TextFormField(
      decoration: InputDecoration(
        enabledBorder: OutlineInputBorder(
            //DEFAULT STATE OF THE BORDER(FOCUSED BORDER DOWN BELOW TO HAVE MORE CONTROL OF THE FORM)
            borderSide: BorderSide(
                width: 1.0, color: Color.fromRGBO(16, 25, 53, 0.1)),
            borderRadius: BorderRadius.circular(12.0)),
        focusedBorder: OutlineInputBorder(
          //ON FOCUSED BORDER TO NOT CHANGE STATE WHILE BEING PRESSED ON
          borderSide: BorderSide(
              width: 1.0, color: Color.fromRGBO(16, 25, 53, 0.1)),
          borderRadius: BorderRadius.circular(12.0),
        ),
        prefixIcon: Icon(Icons.mail, color: Color(0xFF9FA3AE)),
        hintText: 'El.Paštas',
        hintStyle: TextStyle(
          fontFamily: 'Sora',
          fontSize: 16.0,
          color: Color(0xFF9FA3AE),
        ),
      ),
      validator: (input) =>
          !input.contains('@') ? 'Please enter a valid email' : null,
      onSaved: (input) => email = input,
    ));
 }
}

//This is the button class.

import 'package:flutter/material.dart';
import 'dart:math' as math;

class LoginButton extends StatelessWidget {
  final _formKey = GlobalKey<FormState>();
  String email;
  String password;

  _submit() {
    if (_formKey.currentState.validate()) {
      _formKey.currentState.save();
      print('validated');
      //logging in the user
    }
  }

  @override
  Widget build(BuildContext context) {
    //Container to manipulate button design
    return Container(
        width: 370.0,
        height: 54.0,
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular((12.0)),
          gradient: LinearGradient(
            //change gradient, wrong value, maybe something in AdobeXD.
            colors: <Color>[Color(0xFF00BAFF), Color(0xFF448CFA)],
            stops: [0.0, 1.0],
            begin: Alignment(-1.0, 0.0),
            end: Alignment(1.0, 0.0),
            transform: GradientRotation(math.pi / 2),
          ),
          boxShadow: [
            BoxShadow(
              color: Color.fromRGBO(48, 183, 241, 1.0),
              offset: Offset(0.0, 4.0),
              blurRadius: 12.0,
            ),
          ],
        ),

        //@@@@ WHEN BUTTON IS PRESSED @@@@
        child: ElevatedButton(
          onPressed: _submit,
          child: Text(
            'Prisijungti',
          ),
          style: ElevatedButton.styleFrom(
              //COLOR OF THE TEXT INSIDE THE BUTTON
              onPrimary: Color(0xFFFFFFFF),
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(12.0),
              ),
              primary: Colors.transparent,
              textStyle: TextStyle(
                //Text inside button style
                fontSize: 16.0,
                fontWeight: FontWeight.w600,
                fontFamily: 'Sora',
              )),
        ));
  }
}
flutter
dart
asked on Stack Overflow May 6, 2021 by ungurys8

3 Answers

0

You need to wrap them in a Form widget and pass the key, for example:

Form(
  key: _keyForm
  child: Column(
    children: <Widget>[
     EmailTextFieldForm(),
     PasswordTextFieldForm(),
     FormButton(),
   ],
 )
)
answered on Stack Overflow May 6, 2021 by José David Ortega
0

you need to wrap all the TextFormFields in a form to get it like this

Column(
  children: [
    TextFormField(),
    ....
    TextFormField(),
])

TextFormFields can be wrapped or moved to another widget, as long as it is a child of the Form.

if everything is in one widget

Form(
  child: 
     Column(
     children: [
       TextFormField(),
       ....
       TextFormField(),
       Button(
         onTap: () {},
       ),
])

you need to wrap the button in the Builder so that the context of the current element in the tree is available

Builder(
  builder: (context) => Button(
    onTap: () {},
  ),
),

and after that, you can do Form.of(context).validate(). This entry will find the first form higher in the tree and validate all text fields.

in this way you should get out like this

Builder(
  builder: (context) => Button(
    onTap: () {
      Form.of(context).validate()
    },
  ),
)

if the button is placed in a separate widget, then there is no need to wrap it in the Builder, you can simply call the validation, since the context below the form is available to you

Button(
  onTap: () {
    Form.of(context).validate()
  },
),

also, you can create GlobalKey

and use validation with a key. You can pass key, for example, through the constructor(if needed)

final _formKey = GlobalKey<FormState>();

Form(
  key: _formKey
  child: Column(
    children: [
      TextFormField(),
      ....
      Button(
        onTap: () {
          _formKey.currentState!.validate ()
        }
      )
    ],
  ),
 )
answered on Stack Overflow May 6, 2021 by Cr0manty • edited May 7, 2021 by Cr0manty
0

Accessing Field Data

The code sample at bottom shows two different ways to get access to the email field value using:

  • FormFieldState
  • TextEditingController

These two methods don't rely on having a Form wrapping your fields (although it's often convenient to do so, giving you more options for handling form data & showing validation errors.)

enter image description here

Why use Form?

A Form widget wrapping fields is useful for handling/manipulating several fields together as a group for things such as form resetting, validation, and submitting.

We access these Form functions via a GlobalKey<FormState> that we give to the Form when we declare it.

      child: Form(
        key: formKey, // declared above as a field in our State object

For example, TextFormField has a validator: argument (takes a function). If our field is inside a Form, we can ask the Form call all validator functions to "validate" our form:

formKey.currentState.validate();

The validator: will display any non-null String you return to it:

email validator

Code Sample

import 'package:flutter/material.dart';

class FormValuesPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Form Values'),
      ),
      body: FormValuesExample(),
    );
  }
}

class FormValuesExample extends StatefulWidget {
  @override
  _FormValuesExampleState createState() => _FormValuesExampleState();
}

class _FormValuesExampleState extends State<FormValuesExample> {
  GlobalKey<FormState> formKey = GlobalKey<FormState>();
  GlobalKey<FormFieldState> emailFieldKey = GlobalKey();
  TextEditingController emailController = TextEditingController();

  @override
  void dispose() {
    emailController.dispose();
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.symmetric(horizontal: 20),
      child: Form(
        key: formKey, // declared above as a field in our State object
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            TextFormField(
              key: emailFieldKey,
              controller: emailController,
              decoration: InputDecoration(
                labelText: 'Email'
              ),
              validator: (val) => validateEmail(val),
            ),
            LoginButton(formKey: formKey, fieldKey: emailFieldKey, controller: emailController,)
          ],
        ),
      ),
    );
  }

  String validateEmail(String email) {
    if (email == null || email.isEmpty)
      return 'Email cannot be empty';
    return null;
  }
}

class LoginButton extends StatelessWidget {
  final GlobalKey<FormState> formKey;
  final GlobalKey<FormFieldState> fieldKey;
  final TextEditingController controller;

  LoginButton({this.formKey, this.fieldKey, this.controller});

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
        onPressed: () {
          formKey.currentState.validate();
          print('from FormFieldState ${fieldKey.currentState.value}');
          print('from controller: ${controller.text}');
        },
        child: Text('Submit'));
  }
}
answered on Stack Overflow May 6, 2021 by Baker

User contributions licensed under CC BY-SA 3.0