Flutter/Dart: Pass Parameters to a Stateful Widget? - flutter

I need to call pass my title and oldtitle parameters to my EditPage Stateful widget. But if I do this;
class EditPage extends StatefulWidget {
String title;
String oldtitle;
EditPage({this.title, this.oldtitle})
The strings are not available to the build unless I call it them as widget.title and widget.oldtitle.
But I'm using a textfield within a form that doesn't seem to work right if I use these widgets.
Here's the form code:
Container(
child: TextField(
decoration: new InputDecoration(
hintText: widget.oldtitle,
contentPadding: new EdgeInsets.all(1.0),
border: InputBorder.none,
filled: true,
fillColor: Colors.grey[300],
),
keyboardType: TextInputType.text,
autocorrect: false,
onChanged: (titleText) {
setState(() {
widget.title= titleText;
});
},
),
),
But then if I do this;
class _EditPageState extends State<EditPage> {
String title;
String oldtitle;
EditPage({this.title, this.oldtitle})
I can't pass the title parameter to it from another screen. IE:
`EditPage(title:mytitle, oldtitle:myoldtitle);`
So What's the correct way to pass a parameter to a Stateful widget?

You should never pass the variables to the state directly, since it does not guarantee that widget will get rebuilt when state will be updated. You should accept parameters via your stateful widget and access them from the state itself via widget.variable.
Example:
class TestWidget extends StatefulWidget {
final String variable;
TestWidget({Key key, #required this.variable}) : super(key: key);
#override
_TestWidgetState createState() => _TestWidgetState();
}
class _TestWidgetState extends State<TestWidget> {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
// Accessing the variables passed into the StatefulWidget.
child: Text(widget.variable),
),
);
}
}

Looks like the solution was to separate the title from the oldtitle;
class EditPage extends StatefulWidget {
String oldtitle;
EditPage({this.oldtitle})
and then;
class _EditPageState extends State<EditPage> {
String title;
So now for the form;
Container(
child: TextField(
decoration: new InputDecoration(
hintText: widget.oldtitle,
contentPadding: new EdgeInsets.all(1.0),
border: InputBorder.none,
filled: true,
fillColor: Colors.grey[300],
),
keyboardType: TextInputType.text,
autocorrect: false,
onChanged: (titleText) {
setState(() {
title= titleText;
});
},
),
),

Related

Flutter checkbox inside textfield

I was wondering how I could recreate the Samsung Notes App. To be more precise I want to have the checkbox functionality within the textfield. See picture below:
Thanks for your support!
I've just tried it myself and this looks like how you want it to look:
TextField(
decoration: InputDecoration(
prefixIcon: Checkbox(value: value, onChanged: (value) {//bool magic here}),
hintText: 'TextField'
),
),
Check out https://api.flutter.dev/flutter/material/InputDecoration-class.html for more InputDecoration() options to prettify things.
if I understood you correctly you can use Textformfield. You can add the checkbox widget to the textformfield prefix parameter. I hope this gives you an idea and helps.
For Example:
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({
Key? key,
required this.title,
}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool isChecked = false;
TextEditingController textController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: TextField(
controller:textController,
style:TextStyle(decoration: isChecked ? TextDecoration.lineThrough : TextDecoration.none),
decoration: InputDecoration(
prefixIcon: Checkbox(value:isChecked, onChanged: (value) {
setState(() { isChecked=value!;});
}),
hintText: 'To Do'
),
),
),
);
}
}
Dartpad => https://dartpad.dev/?id=e75b493dae1287757c5e1d77a0dc73f1

How to extract TextFormField as reusable widget in flutter

I want to make my flutter project highly manageable, apply clean code and maintain DRY concept strictly. There are a lot of input elements in any flutter project. So I want to make this element as a separate widget so that if I want to change in future then I will change in one place. Here is my approach:
import 'package:flutter/material.dart';
import '../utility/validatation.dart';
class RegistrationPage extends StatefulWidget {
static const String routeName = '/registrationPage';
#override
State<RegistrationPage> createState() => _RegistrationPageState();
}
class _RegistrationPageState extends State<RegistrationPage> {
final _formKey = GlobalKey<FormState>();
final TextEditingController nameInput = TextEditingController();
final TextEditingController businessName = TextEditingController();
final TextEditingController productTypeId = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: new Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Container(
height: 70,
margin: EdgeInsets.only(bottom: 50),
child: Image(image: AssetImage('assets/logo.png')),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 30.0),
child: TextInput(inputController: nameInput, label: 'আপনার নাম'),
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_register(context);
}
},
child: Text('Next'),
)
]),
),
);
}
void _register(BuildContext context) {}
}
class TextInput extends StatelessWidget {
const TextInput({
Key? key,
required this.inputController,
required this.label,
}) : super(key: key);
final TextEditingController inputController;
final String label;
#override
Widget build(BuildContext context) {
return TextFormField(
controller: inputController,
keyboardType: TextInputType.text,
decoration: const InputDecoration(
border: UnderlineInputBorder(),
prefixIcon: Icon(Icons.phone),
labelText: label,
),
validator: (value) {
return Validation.required(value);
},
);
}
}
But I got this error:
What is wrong in my code? Is there any problem in my approach or should I stop thinking to refactor my code as I do? Please also suggest if there is any smarter way to make code more clean and manageable.
Oh I see so you have this
class TextInput extends StatelessWidget {
const TextInput({
Key? key,
required this.inputController,
required this.label,
}) : super(key: key);
final TextEditingController inputController;
final String label;
#override
Widget build(BuildContext context) {
return TextFormField(
controller: inputController,
keyboardType: TextInputType.text,
// Notice the const here right?
// So the idea is that decoration objects could rebuild to either change one thing or the other, so 'label' here cannot be a constant
//So to solve this InputDecoration should not have const.
decoration: const InputDecoration(
border: UnderlineInputBorder(),
prefixIcon: Icon(Icons.phone),
labelText: label,
),
validator: (value) {
return Validation.required(value);
},
);
}
}
Since you are using a variable in InputDecoration, you should not declare InputDecoration with const keyword.

Accessing state of Statefull Widget

I have a situtation where i would like to access to text of a text controller, from the Widget part of a statefull widget.
So what i would like to do is is write a method getText() that returns the current text in my _textController.
I know how to do this from the other way around. If i need to get data from my widget from the State part of my widget is use "widget.", but i dont know how to do this the other way around.
class MyTextWidget extends StatefulWidget {
String getText() {
// how can i access the _textController.text from here?
}
#override
_MyTextWidgetState createState() => _MyTextWidgetState();
}
class _MyTextWidgetState extends State<MyTextWidget> {
final TextEditingController _textController = TextEditingController();
_MyTextWidgetState();
#override
void dispose() {
_textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(2.0),
child: TextField(
controller: _textController,
key: ValueKey('MyTextWidgetinput_Key'),
maxLines: null,
autofocus: true,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(15.0))),
),
));
}
}
Simply move it up and access it in your state class using widget._textController:
class MyTextWidget extends StatefulWidget {
final TextEditingController _textController = TextEditingController();
String getText() {
_textController.text // do something with it
// how can i access the _textController.text from here?
}
#override
_MyTextWidgetState createState() => _MyTextWidgetState();
}
class _MyTextWidgetState extends State<MyTextWidget> {
_MyTextWidgetState();
#override
void dispose() {
widget._textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(2.0),
child: TextField(
controller: widget._textController,
key: ValueKey('MyTextWidgetinput_Key'),
maxLines: null,
autofocus: true,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(15.0))),
),
));
}
}

Flutter - How implement one E-Mail TextFormField widget for using in multiple screens?

I'm developing a app with a register, login and reset password screen. In all this screens the user must enter his e-mail address. Now i will not implement the e-mail address textfield for every single screen. So i will implement a email textfield widget for every screen like the code below.
import 'package:flutter/material.dart';
import 'package:email_validator/email_validator.dart';
class EMailTextFormField extends StatefulWidget {
#override
_EMailTextFormFieldState createState() => _EMailTextFormFieldState();
}
class _EMailTextFormFieldState extends State<EMailTextFormField> {
final _email = TextEditingController();
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 3.0),
child: TextFormField(
decoration: InputDecoration(
icon: Icon(Icons.email, size: IconTheme.of(context).size, color: IconTheme.of(context).color),
labelText: 'E-Mail...',
counterText: '',
),
keyboardType: TextInputType.emailAddress,
controller: _email,
validator: _validateEmail,
maxLength: 70,
),
);
}
String _validateEmail(String email) {
// validate E-Mail function...
}
}
My problem is now that i can't use the TextEditingController (_email) outside this widget in the different screens (register, login and reset password) like this as a example:
final FirebaseUser user = (await _auth.createUserWithEmailAndPassword(
email: _email.text.toString(), password: _password.text.toString())).user;
The error is "Undefined name _email" because the _email TextEditingController is in the EMailTextFormField widget, but how can i give the value of the _email field from EMailTextFormField widget to the other screens (register, login and reset password)?
Can anyone help me i found so far no solution.
You can do this using onSaved callback.
EMailTextFormField:
class EMailTextFormField extends StatefulWidget {
final void Function(String email) onSaved;
const EMailTextFormField({Key key, this.onSaved}) : super(key: key);
#override
EMailTextFormFieldState createState() => EMailTextFormFieldState();
}
class EMailTextFormFieldState extends State<EMailTextFormField> {
final _email = TextEditingController();
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 3.0),
child: TextFormField(
decoration: InputDecoration(
icon: Icon(Icons.email, size: IconTheme.of(context).size, color: IconTheme.of(context).color),
labelText: 'E-Mail...',
counterText: '',
),
keyboardType: TextInputType.emailAddress,
controller: _email,
validator: _validateEmail,
maxLength: 70,
onSaved: widget.onSaved, //callback
),
);
}
String _validateEmail(String email) {
// validate E-Mail function...
}
}
Page where you will use EMailTextFormField:
class EmailPage extends StatefulWidget {
EmailPage({Key key}) : super(key: key);
#override
_EmailPageState createState() => _EmailPageState();
}
class _EmailPageState extends State<EmailPage> {
String _email;
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Email Page'),
),
body: Form(
key: _formKey,
child: Column(
children: <Widget>[
EMailTextFormField(
onSaved: (String email) => _email = email,
),
RaisedButton(
child: Text('Go'),
onPressed: (){
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
print(_email);
}
},
)
],
),
),
);
}
}

How to correctly extract/composite custom widgets and handle their child fields

In my widget tree, I have several TextField widgets. All have the same decoration but different onChanged actions:
Widget _buildTextField() {
return TextField (
decoration: InputDecoraction(
border: OutlineInputBorder()
),
onChanged: (text) {
doSth();
}
);
}
Now I want to reduce the code duplication and was trying to extract the TextField with the duplicated decoration field into a CustomTextField. I read that with Flutter composition is over inheritance, so I tried to compose it this way:
class CustomTextField extends StatefulWidget {
#override
_CustomTextFieldState createState() => _CustomTextFieldState();
}
class _CustomTextFieldState extends State<CustomTextField> {
#override
Widget build(BuildContext context) {
return Container(
child: TextField(
decoration: InputDecoration( // <-- the decoration field
border: OutlineInputBorder(),
),
),
);
}
}
At the other side I did:
Widget _buildTextField() {
return CustomTextField ( // <-- new CustomTextField without decoration
onChanged: (text) { // <-- Problem: "Parameter is not defined"
doSth();
}
);
}
But now the onChanged call is not accepted. So, what is the correct way to extract own widgets and handle the child fields?
You need to register a callback function
class CustomTextField extends StatefulWidget {
final Function onChange;
const CustomTextField({Key key, this.onChange}) : super(key: key);
#override
_CustomTextFieldState createState() => _CustomTextFieldState();
}
class _CustomTextFieldState extends State<CustomTextField> {
#override
Widget build(BuildContext context) {
return Container(
child: TextField(
decoration: InputDecoration(
// <-- the decoration field
border: OutlineInputBorder(),
),
onChanged: widget.onChange,
),
);
}
}
and then
child: CustomTextField(
onChange: (item) {
print(item);
},
),