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.
Related
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
I have a only one question
I begin learn a flutter in yesterday.
How to get a Textfield value ?
I tried call a TextEditingController anybody in a class.
But It's not working to.
[![enter image description here][1]][1]
class LoginScreen extends StatefulWidget {
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<LoginScreen> {
final userId = TextEditingController();
final userPass = TextEditingController();
...TextInputField(),
PasswordInput(),
LoginButton()
}
void loginAct() async {
var data = {
"user_id": userId, //undefined
"user_pass": userPass, //undefined
};
class TextInputField extends StatelessWidget {
const TextInputField({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: Container(
height: size.height * 0.08,
width: size.width * 0.8,
decoration: BoxDecoration(
color: Colors.grey[500],
borderRadius: BorderRadius.circular(16),
),
child: TextField(
controller: userId, <=== (The getter 'userId' isn't defined for the class 'TextInputField')
decoration: InputDecoration(
border: InputBorder.none,
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Icon(
FontAwesomeIcons.user,
size: 28,
color: Colors.black54,
),
),
hintText: 'id',
),
),
),
);
}
}
how to resolve that?
if flutter you can get text field value by different methods
by text editing controller
add this on top
final userNameController = TextEditingController();
final passwordController = TextEditingController();
then on text field
TextField(
controller: userNameController,
);
TextField(
controller: passwordController,
);
and on function pass the value as you like
onPressed: () => loginAct(userNameController.text,passwordController.text),
in this case you need to specify what is initial value through controller
for Second Question
http.get(Uri.https('https://swapi.co', 'api/people'));
or
http.get(Uri.parse('https://swapi.co/api/people'));
try this on your code
class TextInputField extends StatelessWidget {
const TextInputField({ Key? key }) : super(key: key);
#override
Widget build(BuildContext context) {
final userIdController = TextEditingController();
final passwordController = TextEditingController();
return Container(
child: TextField(
controller: userId,
onpressed: () {loginAct(password:passwordController.text,userName:userIdController.
text)})
);
}
your login function
void loginAct({String password, String userName}) async {
var data = {
"user_id": password,
"user_pass": userName,
}
Using the maxLines = null property, I can make the text wrap in a TextField. However, this also uses the Enter to create newlines.
I don't want that - I want to reserve the Enter key to onSubmitted() function. How do I do that?
You can achieve this with the onKey of either the TextField's FocusNode (WidgetOne) or a RawKeyboardListener (WidgetTwo):
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
void main() {
runApp(
MaterialApp(
title: 'Wrapping Single Line Text Fields',
home: _Page(),
),
);
}
class _Page extends HookWidget {
const _Page({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final result = useState('');
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_WidgetOne(onSubmitted: (value) => result.value = value),
_WidgetTwo(onSubmitted: (value) => result.value = value),
Container(
margin: const EdgeInsets.all(15.0),
padding: const EdgeInsets.all(3.0),
decoration:
BoxDecoration(border: Border.all(color: Colors.black)),
child: Text('Result: ${result.value}')),
],
),
),
);
}
}
class _WidgetOne extends StatelessWidget {
final ValueChanged<String> onSubmitted;
const _WidgetOne({Key key, this.onSubmitted}) : super(key: key);
#override
Widget build(BuildContext context) {
final _controller = TextEditingController();
final _focusNode = FocusNode(onKey: (node, event) {
if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
onSubmitted(_controller.text);
node.unfocus();
return true;
}
return false;
});
return TextField(
decoration: InputDecoration(hintText: 'With FocusNode onKey'),
controller: _controller,
focusNode: _focusNode,
maxLines: null,
);
}
}
class _WidgetTwo extends StatelessWidget {
final ValueChanged<String> onSubmitted;
const _WidgetTwo({Key key, this.onSubmitted}) : super(key: key);
#override
Widget build(BuildContext context) {
final _controller = TextEditingController();
final _focusNode = FocusNode(onKey: (node, event) {
if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
onSubmitted(_controller.text);
node.unfocus();
return true;
}
return false;
});
return RawKeyboardListener(
focusNode: _focusNode,
onKey: (event) {
if (event.isKeyPressed(LogicalKeyboardKey.enter)) {
onSubmitted(_controller.text);
}
},
child: TextField(
decoration: InputDecoration(hintText: 'With RawKeyboardListener onKey'),
controller: _controller,
maxLines: null,
),
);
}
}
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);
}
},
)
],
),
),
);
}
}
Please help for my 2 questions.
1st Question. Why text value disappear?[FIXED] by #Praneeth
I added gif
please click
I used this way my code,
In my widget I called UsernameTextField class.
Widget looks like this,
Widget build > WillPopScope > Scaffold > Form > ListView > children>
Container (below container) I added key: _scaffoldKey, and key: formKey, also.
Container(
padding: EdgeInsets.all(16.0),
margin: EdgeInsets.only(top: 30.0),
child: UsernameTextField(),
),
UsernameTextField()
class UsernameTextField extends StatefulWidget{
final usernameController = TextEditingController();
#override
State<StatefulWidget> createState() {
return UsernameTextFieldState(usernameController);
}
}
class UsernameTextFieldState extends State<UsernameTextField>{
final usernameController;
UsernameTextFieldState(this.usernameController);
#override
Widget build(BuildContext context) {
return AppTextField(
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(20.0),
labelText: AppTranslations.of(context)
.text("loginpage_username"),
),
myController: usernameController,
textInputType: TextInputType.emailAddress
);
}
}
AppTextField() class, I used this class for my every TextField Widget
class AppTextField extends StatelessWidget {
final InputDecoration decoration;
final myController;
final TextInputType textInputType;
AppTextField({
this.decoration,
this.myController,
this.textInputType
});
#override
Widget build(BuildContext context) {
return TextFormField(
controller: myController,
keyboardType: textInputType,
textAlign: TextAlign.left,
decoration: decoration
);}}
2nd Question. How to get textField value?
In my button onPressed() method I called, I called validation method, But result is null
usernameValidation(){
String username = UsernameTextField().usernameController.text;
print(username);
}
First convert UsernameTextField class to a Stateful one by extending as a StatefulWidget instead of StatelessWidget.
Then you can get value from usernameController.text
UPDATE
class UsernameTextField extends StatefulWidget{
final usernameController = TextEditingController();
UsernameTextField(this.usernameController)
#override
State<StatefulWidget> createState() {
return UsernameTextFieldState(usernameController);
}
}
class UsernameTextFieldState extends State<UsernameTextField>{
#override
Widget build(BuildContext context) {
return AppTextField(
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(20.0),
labelText: AppTranslations.of(context)
.text("loginpage_username"),
),
myController: widget.usernameController,
textInputType: TextInputType.emailAddress
);
}
}