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',
)),
));
}
}
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(),
],
)
)
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 ()
}
)
],
),
)
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.)
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:
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'));
}
}
User contributions licensed under CC BY-SA 3.0