I have a general textfield which I am using throughout the app for text inputs and forms. But textfield focus is making the whole form unnatural. To manage focus, I am doing these steps.
First, on MaterialApp:
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: GetMaterialApp()
),
Then custom textfield:
class CustomTextField extends StatefulWidget {
CustomTextField(
{this.title,
this.controller,
this.hint,
this.hide,
this.maxlines,
this.minLines,
this.validator,
this.onChanged});
final title, controller, hint, hide, maxlines, minLines, validator, onChanged;
#override
_CustomTextFieldState createState() => _CustomTextFieldState();
}
class _CustomTextFieldState extends State<CustomTextField> {
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
//color: Color(0xffE4E5EA),
borderRadius: BorderRadius.circular(4),
),
child: TextFormField(
onEditingComplete: () {
FocusScope.of(context).unfocus();
},
validator: widget.validator,
controller: widget.controller,
obscureText: widget.hide ?? false,
maxLines: widget.maxlines ?? 1,
minLines: widget.minLines ?? 1,
decoration: InputDecoration(
hintText: widget.hint,
labelText: widget.title,
hintStyle: Theme.of(context)
.textTheme
.bodyText2
.apply(color: Colors.grey),
labelStyle: Theme.of(context).textTheme.subtitle1.apply(
color: Colors.grey[600]),
contentPadding:
const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(width: 2, color: Colors.blueAccent)),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(4),
borderSide: BorderSide(color: Colors.grey[400], width: 1.5)),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4),
borderSide: BorderSide(color: Colors.grey[200], width: 1.5)),
),
),
),
const SizedBox(height: 10),
],
);
}
}
It works for the most part but when I try to do some task like pick date or file or something, the last used textfield gains focus and keyboard opens. Is there any way to manage focus better way?
Related
I have a FormBuilderTextField that I want to toggle it's enabled property based on another FormBuilderTextField value length.
I know there are ways with TextEditingController, but I want to try it another way because I have a lot of FormBuilderTextField.
I tried using onChanged but to no avail.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
TextEditingController username = TextEditingController();
TextEditingController password = TextEditingController();
bool enabledPassword = false;
bool enabledSubmitButton = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
const SizedBox(height: 12),
TextField(
controller: username,
onChanged: (value) {
setState(() {
enabledPassword = value.length >= 8 ? true : false;//this
});
},
decoration: const InputDecoration(
hintText: 'Username',
contentPadding:
EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 2.0),
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
),
),
const SizedBox(height: 12),
TextField(
controller: password,
onChanged: (value) {
setState(() {
enabledSubmitButton = value.length >= 8 ? true : false;//this
});
},
enabled: enabledPassword,//this
decoration: const InputDecoration(
hintText: 'Password',
contentPadding:
EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
border: OutlineInputBorder(),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 2.0),
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
disabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 2.0),
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
),
),
const SizedBox(height: 12),
ElevatedButton(
onPressed: enabledSubmitButton//this
? () {
print('Username:${username.text}');
print('Password:${password.text}');
}
: null,
child: const Text('Submit'),
)
],
);
}
}
See result here
I create 3 text fields to get input from the user on this screen. The problem is when the keyboard appears, the UI didn't move up to the keyboard.. I already add resizeToAvoidBottomInset: false inside the Scaffold widget but it won't work. Here is the screenshot and the code for this page.
Screenshot
class AddEmployee extends StatelessWidget {
const AddEmployee({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const Scaffold(
resizeToAvoidBottomInset: false,
body: SafeArea(child: AddNewEmployeeWidget()),
);
}
}
class AddNewEmployeeWidget extends StatefulWidget {
const AddNewEmployeeWidget({Key? key}) : super(key: key);
#override
State<AddNewEmployeeWidget> createState() => _AddNewEmployeeWidgetState();
}
class _AddNewEmployeeWidgetState extends State<AddNewEmployeeWidget> {
TextEditingController nameController = TextEditingController(),
usernameController = TextEditingController(),
passwordController = TextEditingController();
addEmployee() async {
await FirebaseFirestore.instance.collection('employee').add({
'username': usernameController.text,
'name': nameController.text,
'password': passwordController.text
});
Fluttertoast.showToast(
msg: "Successfully Add a new employee!",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.TOP,
timeInSecForIosWeb: 1,
backgroundColor: Colors.grey,
textColor: Colors.white,
fontSize: 20.0);
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Form(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
//Logo
// Center(
// child: Image.asset(
// 'assets/mashiso.png',
// height: 250,
// ),
// ),
const SizedBox(
height: 100,
),
// name
SizedBox(
width: 300,
child: TextField(
controller: nameController,
textInputAction: TextInputAction.next,
decoration: const InputDecoration(
focusColor: Color(0xffFDBF05),
prefixIcon: Icon(Icons.alternate_email),
focusedBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Color(0xffFDBF05), width: 3),
borderRadius: BorderRadius.all(Radius.circular(25))),
hintText: "Enter name",
enabledBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Color(0xffFDBF05), width: 3),
borderRadius: BorderRadius.all(Radius.circular(25)))),
),
),
//
const SizedBox(
height: 10,
),
//username
SizedBox(
width: 300,
child: TextFormField(
controller: usernameController,
textInputAction: TextInputAction.next,
decoration: const InputDecoration(
focusColor: Color(0xffFDBF05),
prefixIcon: Icon(Icons.person),
focusedBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Color(0xffFDBF05), width: 3),
borderRadius: BorderRadius.all(Radius.circular(25))),
hintText: "Enter username",
enabledBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Color(0xffFDBF05), width: 3),
borderRadius: BorderRadius.all(Radius.circular(25)))),
),
),
//
const SizedBox(
height: 10,
),
//password
SizedBox(
width: 300,
child: TextField(
controller: passwordController,
textInputAction: TextInputAction.next,
obscureText: true,
decoration: const InputDecoration(
focusColor: Color(0xffFDBF05),
prefixIcon: Icon(Icons.lock),
focusedBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Color(0xffFDBF05), width: 3),
borderRadius: BorderRadius.all(Radius.circular(25))),
hintText: "Enter password",
enabledBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Color(0xffFDBF05), width: 3),
borderRadius: BorderRadius.all(Radius.circular(25)))),
),
),
const SizedBox(
height: 10,
),
//Add Button
SizedBox(
width: 170,
height: 50,
child: ElevatedButton(
onPressed: () {
setState(() {
usernameController;
nameController;
passwordController;
});
addEmployee();
Future.delayed(
const Duration(seconds: 1),
(() => Navigator.pushReplacement(
context,
PageTransition(
child: const MobileHome(),
type: PageTransitionType.leftToRight))));
},
child: const Text(
"Add",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 30),
),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(const Color(0xffFDBF05))),
),
),
],
),
),
);
}
}
If you remove 'resizeToAvoidBottomInset: false' your code should work as expected. Are you using this widget inside another one? Maybe the wrapper widget is having some issue.
Wrap your Column with a SingleChildScrollView
i created a rounded textformfield and validate it, textbox are look like this
here is the code for this textbox
firstly i create a container
import 'package:flutter/material.dart';
class TextFieldContainer extends StatelessWidget {
final Widget child;
const TextFieldContainer({
Key key,
this.child,
}) : super(key: key);
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
margin: EdgeInsets.symmetric(vertical: 10),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 5),
width: size.width * 0.8,
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.circular(29),
),
child: child,
);
}
}
and then return this container and creating a textformfield
emailtext(){
return TextFieldContainer(
child: TextFormField(
controller: TextEditingController(text: user.username),
autofillHints: [AutofillHints.email],
onEditingComplete: ()=>TextInput.finishAutofillContext(),
decoration: InputDecoration(
border: InputBorder.none,
icon: Icon(Icons.email,color: Colors.blue,),
labelText: 'Username'),
onChanged: (value){
user.username=value;
},
validator: (value) {
if (value.isEmpty) {
return 'Please enter username';
}
return null;
},
),
);
}
same i done for password textbox.
but when i click on login button, error message is displaying like this
i want to show it outside from this blue container.
i used fillcolor, but that is not working too.
please help if anyone know how to do this.
You can use each case separetely in decoration like this:
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.greenAccent, width: 5.0),
borderRadius: BorderRadius.circular(25.0),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red, width: 5.0),
borderRadius: BorderRadius.circular(25.0),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red, width: 5.0),
borderRadius: BorderRadius.circular(25.0),
),
And for background color use fillColor inside decoration
use this code:
Container(
alignment: Alignment.center,
padding: EdgeInsets.fromLTRB(30, 3, 20, 0),
margin: EdgeInsets.only(left: 10, right: 10),
height: 50,
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.white,
borderRadius: BorderRadius.circular(10)),
child: TextField(
keyboardType: TextInputType.emailAddress,
cursorColor: Colors.black,
controller: emailController,
textInputAction: TextInputAction.search,
decoration: InputDecoration(
border: InputBorder.none,
suffixIcon: IconButton(
onPressed: () {},
icon: Icon(Icons.email_outlined),
color: Colors.grey,
),
hintText: 'Correo Electrónico',
hintStyle: TextStyle(color: Colors.black),
),
style: TextStyle(color: Colors.black),
),
Example of desired effect
I have a TextField with an InputDecoration where I set a hintText and a prefixIcon, however the prefixIcon property that I am using will always pull the Icon all the way to the left.
TextField(
textAlign: TextAlign.center,
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 30, 0, 30),
prefixIcon: Icon(Icons.search),
hintText: 'Search',
fillColor: Color(0xffF1F4FB),
filled: true,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(25),
borderSide: BorderSide(
color: Color(0xffF1F4FB),
),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(25),
borderSide: BorderSide(
color: Color(0xffF1F4FB),
),
),
),
),
I'm trying to change this to have the search icon and the hint text initially centered next to each other. How can I achieve this effect?
Here is a solution using IntrinsicWidth.
With this solution, the search icon will be next to the hinttext and then it will stay next to the text input from the User.
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
);
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
height: 48.0,
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 0.0),
margin: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.amber.shade300,
border: Border.all(
color: Colors.brown,
width: 3.0,
),
borderRadius: BorderRadius.circular(25),
),
child: Center(
child: IntrinsicWidth(
child: TextField(
textAlignVertical: TextAlignVertical.center,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
hintText: 'Search',
border: InputBorder.none,
),
),
),
),
),
),
);
}
}
setting prefixIcon as a Row with MainAxisAlignment.center could do the trick. but it covers the whole textField, so we need to replace it with a normal icon when the textField is in focus.
You can detect focus using a FocusNode. To learn more read Focus and text fields
Code:
class MyTextField extends StatefulWidget {
const MyTextField({Key key}) : super(key: key);
#override
_MyTextFieldState createState() => _MyTextFieldState();
}
class _MyTextFieldState extends State<MyTextField> {
TextEditingController _controller = TextEditingController();
FocusNode _focusNode = FocusNode();
#override
void initState() {
// rebuild the textfield on focus changes
_focusNode.addListener(() {
setState(() {});
});
super.initState();
}
#override
void dispose() {
_controller.dispose();
_focusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return TextField(
controller: _controller,
focusNode: _focusNode,
textAlign: TextAlign.center,
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(0, 30, 0, 30),
prefixIcon: !_focusNode.hasFocus && _controller.text.isEmpty
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.search),
Text('Search'),
],
)
: Icon(Icons.search),
// hintText: 'Search',
fillColor: Color(0xffF1F4FB),
filled: true,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(25),
borderSide: BorderSide(
color: Color(0xffF1F4FB),
),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(25),
borderSide: BorderSide(
color: Color(0xffF1F4FB),
),
),
),
);
}
}
I use prefixIconConstraints to do that. Just add that to your TextFormField's decoration with your prefixIcon like this:
prefixIconConstraints: const BoxConstraints(),
prefixIcon: Container(
margin: EdgeInsets.symmetric(horizontal: 12),
child: const Text('Rp'),
),
you should also want to make your field look bigger by adding some padding to TextFormField's decoration:
contentPadding: EdgeInsets.all(8),
The following NoSuchMethodError was thrown while handling a gesture:
The method 'phone' was called on null.
Receiver: null
Tried calling: phone= "1235451"
I'm creating a page to update user registration data, and when I click on update this error appears and nothing happens. I'm learning flutter now, and there's a lot that I don't know, can someone help me?
[User dart file]: https://i.stack.imgur.com/RLimv.png
import 'package:flutter/material.dart';
import '../../generated/l10n.dart';
import '../models/user.dart';
import '../helpers/helper.dart';
import '../elements/BlockButtonWidget.dart';
import '../helpers/app_config.dart' as config;
import 'package:mvc_pattern/mvc_pattern.dart';
import '../repository/user_repository.dart' as repository;
import '../repository/user_repository.dart';
class SettingsController extends ControllerMVC {
GlobalKey<FormState> loginFormKey;
GlobalKey<ScaffoldState> scaffoldKey;
SettingsController() {
loginFormKey = new GlobalKey<FormState>();
this.scaffoldKey = new GlobalKey<ScaffoldState>();
}
void update(User user) async {
user.deviceToken = null;
repository.update(user).then((value) {
scaffoldKey?.currentState?.showSnackBar(SnackBar(
content: Text("Atualizado com Sucesso"),
));
});
}
}
class ProfileSettingsDialog extends StatefulWidget {
final User user;
final VoidCallback onChanged;
ProfileSettingsDialog({Key key,#required this.user, this.onChanged}) : super(key: key);
#override
_ProfileSettingsDialogState createState() => _ProfileSettingsDialogState();
}
class _ProfileSettingsDialogState extends State<ProfileSettingsDialog> {
GlobalKey<FormState> _profileSettingsFormKey = new GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: Helper.of(context).onWillPop,
child: Scaffold(
resizeToAvoidBottomPadding: false,
body: Stack(
alignment: AlignmentDirectional.topCenter,
children: <Widget>[
Positioned(
top: 0,
child: Container(
width: config.App(context).appWidth(100),
height: config.App(context).appHeight(29.5),
decoration: BoxDecoration(color: Theme.of(context).accentColor),
),
),
Positioned(
top: config.App(context).appHeight(29.5) - 120,
child: Container(
width: config.App(context).appWidth(84),
height: config.App(context).appHeight(29.5),
child: Text(
S.of(context).lets_start_with_register,
style: Theme.of(context).textTheme.headline2.merge(TextStyle(color: Theme.of(context).primaryColor)),
),
),
),
Positioned(
top: config.App(context).appHeight(29.5) - 50,
child: Container(
decoration: BoxDecoration(color: Theme.of(context).primaryColor, borderRadius: BorderRadius.all(Radius.circular(10)), boxShadow: [
BoxShadow(
blurRadius: 50,
color: Theme.of(context).hintColor.withOpacity(0.2),
)
]),
margin: EdgeInsets.symmetric(
horizontal: 20,
),
padding: EdgeInsets.symmetric(vertical: 50, horizontal: 27),
width: config.App(context).appWidth(88),
// height: config.App(context).appHeight(55),
child: Form(key: _ProfileSettingsFormKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
keyboardType: TextInputType.text,
onSaved: (input) => widget.user.phone = input,
validator: (input) => input.trim().length < 3 ? S.of(context).not_a_valid_phone : null,
decoration: InputDecoration(
labelText: "Celular",
labelStyle: TextStyle(color: Theme.of(context).accentColor),
contentPadding: EdgeInsets.all(12),
hintText: S.of(context).john_doe,
hintStyle: TextStyle(color: Theme.of(context).focusColor.withOpacity(0.7)),
prefixIcon: Icon(Icons.person_outline, color: Theme.of(context).accentColor),
border: OutlineInputBorder(borderSide: BorderSide(color: Theme.of(context).focusColor.withOpacity(0.2))),
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Theme.of(context).focusColor.withOpacity(0.5))),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Theme.of(context).focusColor.withOpacity(0.2))),
),
),
SizedBox(height: 30),
TextFormField(
keyboardType: TextInputType.text,
onSaved: (input) => widget.user.address = input,
validator: (input) => input.trim().length < 3 ? S.of(context).not_a_valid_address : null,
decoration: InputDecoration(
labelText: "Endereço",
labelStyle: TextStyle(color: Theme.of(context).accentColor),
contentPadding: EdgeInsets.all(12),
hintText: 'johndoe#gmail.com',
hintStyle: TextStyle(color: Theme.of(context).focusColor.withOpacity(0.7)),
prefixIcon: Icon(Icons.alternate_email, color: Theme.of(context).accentColor),
border: OutlineInputBorder(borderSide: BorderSide(color: Theme.of(context).focusColor.withOpacity(0.2))),
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Theme.of(context).focusColor.withOpacity(0.5))),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Theme.of(context).focusColor.withOpacity(0.2))),
),
),
SizedBox(height: 30),
TextFormField(
keyboardType: TextInputType.text,
onSaved: (input) => widget.user.bio = input,
validator: (input) => input.trim().length < 3 ? S.of(context).not_a_valid_biography : null,
decoration: InputDecoration(
labelText: "Biografia",
labelStyle: TextStyle(color: Theme.of(context).accentColor),
contentPadding: EdgeInsets.all(12),
hintText: 'johndoe#gmail.com',
hintStyle: TextStyle(color: Theme.of(context).focusColor.withOpacity(0.7)),
prefixIcon: Icon(Icons.alternate_email, color: Theme.of(context).accentColor),
border: OutlineInputBorder(borderSide: BorderSide(color: Theme.of(context).focusColor.withOpacity(0.2))),
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Theme.of(context).focusColor.withOpacity(0.5))),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Theme.of(context).focusColor.withOpacity(0.2))),
),
),
SizedBox(height: 30),
BlockButtonWidget(
text: Text(
S.of(context).register,
style: TextStyle(color: Theme.of(context).primaryColor),
),
color: Theme.of(context).accentColor,
onPressed: () {
_profileSettingsFormKey.currentState.save();
widget.onChanged();
Navigator.pop(context);
update(currentUser.value);
//setState(() {});
},
// widget.update(currentUser.value);
),
SizedBox(height: 25),
// FlatButton(
// onPressed: () {
// Navigator.of(context).pushNamed('/MobileVerification');
// },
// padding: EdgeInsets.symmetric(vertical: 14),
// color: Theme.of(context).accentColor.withOpacity(0.1),
// shape: StadiumBorder(),
// child: Text(
// 'Register with Google',
// textAlign: TextAlign.start,
// style: TextStyle(
// color: Theme.of(context).accentColor,
// ),
// ),
// ),
],
),
),
),
),
],
),
),
);
}
InputDecoration getInputDecoration({String hintText, String labelText}) {
return new InputDecoration(
hintText: hintText,
labelText: labelText,
hintStyle: Theme.of(context).textTheme.bodyText2.merge(
TextStyle(color: Theme.of(context).focusColor),
),
enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: Theme.of(context).hintColor.withOpacity(0.2))),
focusedBorder: UnderlineInputBorder(borderSide: BorderSide(color: Theme.of(context).hintColor)),
floatingLabelBehavior: FloatingLabelBehavior.auto,
labelStyle: Theme.of(context).textTheme.bodyText2.merge(
TextStyle(color: Theme.of(context).hintColor),
),
);
}
}
I cannot see the definition of your User class but it seems that the property phone is defined as final (hence, you get the error the setter method was called on null).
You need to make the property mutable (non-final such as String phone) in order to be able to change its value in the same way as in your code.