how to Enable button if "All textfield" is not empty flutter - flutter

my app has two textformfield. I want to enable button if all textfield are not empty.
In the internet, there are a way to enable button if "one" textfield are not empty.
I try to make isFilledTitle true if title of textfield is not empty. and isFilledContent true if content of textfield is not empty. and then if they are all true, isButtonActive is true. but it doesn't work.
late TextEditingController _titleEditingController;
late TextEditingController _contentEditingController;
bool isButtonActive = true;
bool isFilledContent = false;
bool isFilledTitle = false;
#override
void initState() {
super.initState();
_titleEditingController = TextEditingController();
_contentEditingController = TextEditingController();
_titleEditingController.addListener(() {
final isFilledTitle = _titleEditingController.text.isNotEmpty;
setState(() {
this.isFilledTitle = isFilledTitle;
});
});
_contentEditingController.addListener(() {
final isFilledContent = _contentEditingController.text.isNotEmpty;
setState(() {
this.isFilledContent = isFilledContent;
});
});
if(isFilledContent && isFilledTitle){
setState(() {
isButtonActive = true;
});
} else {
setState(() {
isButtonActive = false;
});
}
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: Container(
child: SafeArea(
child: Scaffold(
appBar: AppBarWriteContainer(
onButtonPressed: isButtonActive
? (widget.postNo != null)
? () => revisePost()
: () => newPost()
: null,
),

Yeah your answer is correct but the user needs validation on two text field so i will modify the answer like
class _TempDialogState extends State<TempDialog> {
final TextEditingController _inputController = TextEditingController();
final TextEditingController _inputController2 = TextEditingController();
bool enable = false;
#override
void initState() {
super.initState();
}
#override
void dispose() {
_inputController.dispose();
_inputController2.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Center(
child: Column(children: [
TextField(
controller: _inputController,
onChanged: (data) {
if (_inputController.text.isEmpty ||
_inputController2.text.isEmpty) {
enable = false;
} else {
enable = true;
}
setState(() {});
},
),
TextField(
controller: _inputController2,
onChanged: (data) {
if (_inputController.text.isEmpty ||
_inputController2.text.isEmpty) {
enable = false;
} else {
enable = true;
}
setState(() {});
},
),
ElevatedButton(
onPressed: enable ? () {} : null,
child: Text('${enable}'),
)
])),
),
);
}
}

You don't need any additional libraries to do that. Flutter has it out-of-the-box and you can make sure you're not going to rebuild the whole tree on each keystroke.
TextEditingController extends ValueNotifier<TextEditingValue> which means you can utilize ValueListenableBuilder from material package to listen to text changes.
class MyWidget extends StatelessWidget {
final TextEditingController _inputController = TextEditingController();
#override
void dispose() {
_inputController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Column(children: [
TextField(controller: _inputController),
ValueListenableBuilder<TextEditingValue>(
valueListenable: _inputController,
builder: (context, value, child) {
return ElevatedButton(
onPressed: value.text.isNotEmpty ? () {} : null,
child: Text('I am disabled when text is empty'),
);
},
),
]);
}
}

You only set the isButtonActive in the initState. Try updating it in the controller listeners. Maybe something like:
#override
void initState() {
super.initState();
_titleEditingController = TextEditingController();
_contentEditingController = TextEditingController();
_titleEditingController.addListener(() {
final isFilledTitle = _titleEditingController.text.isNotEmpty;
setState(() {
this.isFilledTitle = isFilledTitle;
updateIsButtonActive();
});
});
_contentEditingController.addListener(() {
final isFilledContent = _contentEditingController.text.isNotEmpty;
setState(() {
this.isFilledContent = isFilledContent;
updateIsButtonActive();
});
});
}
void updateIsButtonActive() {
if(isFilledContent && isFilledTitle){
isButtonActive = true;
} else {
isButtonActive = false;
}
}

Related

CircularProgressIndicator not stopping while moving back

Created a demo for understanding future..
I have taken two screen....Using Future.delayed I am forwarding to next screen in 5 seconds with CircularProgressIndicator...but when I move to back..it still showing CircularProgressbar...
What should I correct my code?
here is my code
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
// TODO: implement initState
super.initState();
}
bool isloading = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Async demo'),
),
body: isloading == true
? Center(child: CircularProgressIndicator())
: Center(
child: TextButton(
child: Text('Pressed'),
onPressed: () {
setState(() {
isloading = true;
});
Future.delayed(Duration(seconds: 5), () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return NextScreen();
}));
isloading = false;
});
},
),
),
);
}
}
What should I correct my code?
Wrap the second assignment of isloading to false inside setState (just as you did when setting to true). Something like the following:
onPressed: () {
setState(() {
isloading = true;
});
Future.delayed(Duration(seconds: 5), () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return NextScreen();
}));
setState(() {
isloading = false;
});
});
},
you need to use setState when you are assign variable = false
For that do as below
replace this
isloading = false;
with
setState(() {
isloading = false;
});

A TextEditingController was used after being disposed in Flutter

I made two TextEditing Controllers and initialized them. Also, I initialized them in my initState too, so that when my screen is disposed they can be initialized again, then why am I getting this error? The two text controllers are used in various places and the OTPBackButton is the widget which takes us back to the previous screen.
When I am opening this screen for first time, it works fine but when I click on this OTPBackButton (or simply the back button) and come back to this screen again, this error is shown to me.
I am including full widget if it helps -
class MobileInput extends StatefulWidget {
final bool isOTP;
final bool isSignup;
VoidCallback signUpState;
TwoArgumentEventCallback signInWithOTP;
Callback sendOTP;
MobileInput({
this.isOTP = true,
required this.sendOTP,
required this.signInWithOTP,
required this.signUpState,
this.isSignup = false,
});
#override
_MobileInputState createState() => _MobileInputState();
}
class _MobileInputState extends State<MobileInput> {
.....
TextEditingController contactController = TextEditingController();
TextEditingController otpController = TextEditingController();
.....
#override
void initState() {
...
contactController = TextEditingController();
otpController = TextEditingController();
super.initState();
}
#override
void dispose() {
// contactController.dispose();
// otpController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
.........
(isOTP)
? Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: text.bodyText2!
.apply(color: secondaryTextColor),
children: [
TextSpan(
text:
'We sent a verification code to your\nphone number '),
TextSpan(
text: (contactController.text.isNotEmpty)
? '(+91) ' +
contactController.text.toString()
: '',
style:
text.bodyText2!.apply(color: brandPurple),
),
],
),
),
)
: Container(),
SizedBox(
height: getProportionateScreenWidth(32),
),
isOTP
? PinCodeTextField(
animationType: AnimationType.scale,
focusNode: focusNode,
onChanged: (value) {
print(otpController.text + ' after input');
if (otpController.text.length < 6) {
setState(() {
isDisabled = true;
});
}
},
.....
),
onCompleted: (value) {
otpController.text = value;
setState(() {
otpButtonText = "Verify Phone Number";
isDisabled = false;
});
},
)
: CustomTextField(
.....
onChanged: (value) {
if (isSignup) {
if (contactController.text.length == 10) {
FocusScope.of(context).unfocus();
emailEntered = true;
if (checkboxValue) {
setState(() {
isDisabled = false;
});
} else {
setState(() {
isDisabled = true;
});
}
} else {
setState(() {
isDisabled = true;
});
}
} else {
if (contactController.text.length == 10) {
FocusScope.of(context).unfocus();
emailEntered = true;
setState(() {
isDisabled = false;
});
} else {
setState(() {
isDisabled = true;
});
}
}
},
......
),
.....
......
isOTP
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Didn\'t received?',
style: text.bodyText2!.apply(color: Colors.white),
),
TextButton(
onPressed: () {
setState(() {
otpController.clear();
focusNode.requestFocus();
// autoFocus = true;
});
(otpController.text.isEmpty)
? widget.sendOTP(contactController.text)
: null;
},
child: RichText(
........
)
: Container(),
SizedBox(height: getProportionateScreenWidth(20)),
isOTP
? LoginCTA(
//After input otp
onPressed: () async {
.....
if (emailEntered &&
otpController.text.length == 6) {
bool res;
try {
res = await widget.signInWithOTP(
contactController.text, otpController.text);
} catch (e) {
res = false;
print(e);
}
..............
LoginCTA(
//After input mobile number
onPressed: () async {
if (emailEntered &&
contactController.text.length == 10) {
widget.sendOTP(contactController.text);
setState(() {
isOTP = true;
isDisabled = true;
});
} else {
......
}
},
.......
),
SizedBox(height: getProportionateScreenWidth(28)),
!isOTP
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
(isSignup)
? 'Already have an account? '
: 'Don\'t have an account? ',
style: TextStyle(
fontSize: 16,
color: Colors.white,
),
),
GestureDetector(
onTap: () => setState(() {
isSignup = !isSignup;
if (isSignup) {
if (contactController.text.length == 10) {
FocusScope.of(context).unfocus();
emailEntered = true;
if (checkboxValue) {
setState(() {
isDisabled = false;
});
} else {
setState(() {
isDisabled = true;
});
}
} else {
setState(() {
isDisabled = true;
});
}
} else {
if (contactController.text.length == 10) {
FocusScope.of(context).unfocus();
emailEntered = true;
setState(() {
isDisabled = false;
});
} else {
setState(() {
isDisabled = true;
});
}
}
}),
.......
],
)
: Container(),
],
),
),
],
),
),
);
}
}
You need to mark your TextEditingController instances as late final when you define them and then initialize them in the initState() function and dispose of them in dispose() as I show you here:
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late final TextEditingController _textController;
#override
void initState() {
_textController = TextEditingController();
super.initState();
}
#override
void dispose() {
_textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
throw UnimplementedError();
}
}

Try checking that the value isn't 'null' before using it as a condition

How can solve this problem of null safety
class _SelectEmpTaskState extends State<SelectEmpTask> {
bool? _value = false;
#override
Widget build(BuildContext context) {
return Container(
child: InkWell(
onTap: () {
setState(() {
print("object");
_value = !_value // Not working
});
},
),
);
}
}
You are using flutter version with sound null safety or you have enabled null safety
The first problem is no semicolon in the code below
_value = !_value // Not working
Second change use:
late bool _value = false
instead of bool? _value = false;
complete source code below:
class _SelectEmpTaskState extends State {
late bool _value = false;
#override
Widget build(BuildContext context) {
return Container(
child: InkWell(
onTap: () {
setState(() {
print("object");
_value = !_value; // Not working
});
},
),
);
}
}

Flutter setting variable of CupertinoPicker after showModalBottomSheet

I'm referencing this answer:https://stackoverflow.com/a/57592970/8616895
Trying to get selectedValue when the bottommodal is closed with WhenComplete.
The function in WhenComplete() is called, but when I print the selectedValue, it only responds as the default, 0. I have checked and SelectedValue does change as I rotate, but the correct value is not retrieved when I close the modal, I assume it has something to do with async or scope. Please share any suggestions.
My class is as follows:
class PopupButton extends StatefulWidget {
final String initialValue;
final List<dynamic> options;
final Function callback;
int initialIndex;
PopupButton(
{#required this.initialValue,
#required this.options,
this.callback,
this.initialIndex = 0});
#override
_PopupButtonState createState() => _PopupButtonState();
}
class _PopupButtonState extends State<PopupButton> {
int selectedValue;
String text = "";
void showPicker(
BuildContext context,
) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
height: 200,
child: CupertinoPicker(
itemExtent: 32,
onSelectedItemChanged: (value) {
if (mounted) {
setState(() {
selectedValue = value;
});
}
},
children: (widget.options.map((option) => Text(
option,
style: TextStyles.headerLight,
))).toList()),
);
}).whenComplete(() {
// THIS ALWAYS PRINTS 0
print("Selected: $selectedValue");
if (widget.callback != null) {
widget.callback(selectedValue);
}
TextEditingController();
return;
});
}
#override
Widget build(BuildContext context) {
selectedValue = widget.initialIndex;
text = widget.initialValue;
return GestureDetector(
child: Text(text),
onTap: () {
showPicker(context);
},
);
}
}
your mistake is initialization selectedValue in build method.
Every time you call
setState(() {
selectedValue = value;
});
the selectedValue initiated by widget.initialIndex.
Please move selectedValue = widget.initialIndex; to initState method.
#override
void initState() {
super.initState();
selectedValue = widget.initialIndex;
text = widget.initialValue;
}
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Text(text),
onTap: () {
showPicker(context);
},
);
}

Disabled button when textInputField is invalid on change text Flutter

I'm new on Flutter.
I'm trying to disabled button while I compile textFormField and it is invalid.
My problem is that it works only if I click on "confirm" on keyboard and not while I compile my input.
So would like disable button while I write the input.
I've done a pastebin for example:
https://pastebin.com/q5WuwrCm
class AddCartButton extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return AddCartButtonState();
}
}
class AddCartButtonState extends State<AddCartButton>{
TextEditingController myController = TextEditingController();
bool isValid = false;
#override
Widget build(BuildContext context) {
void _addToCart(){
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("QUANTITY"),
content: Column(
children: <Widget>[
TextFormField(
controller: myController,
decoration: new InputDecoration(labelText: "quantity"),
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: <TextInputFormatter>[],
autovalidate: true,
validator: (value) {
if (value.isEmpty) {
isValid = false;
return "the quantity cannot be empty";
} else if (double.tryParse(value) == null) {
isValid = false;
return "the quantity must be valid number";
} else {
isValid = true;
return null;
}
}
)
],
),
actions: <Widget>[
FlatButton(
disabledTextColor: Colors.grey,
child: Text("add"),
onPressed: isValid ? () { print("is valid"); }: null
)
],
);
},
);
}
}
}
You can use addListener() function on your TextEditingController myController.
myController.addListener((){
//With this, you can "listen" all the changes on your text while
//you are typing on input
print("value: ${myController.text}");
//use setState to rebuild the widget
setState(() {
//you can check here if your text is valid or no
//_isValidText() is just an invented function that returns
//a boolean representing if the text is valid or not
if(_isValidText(myController.text)) isValid = true;
else isValid = false;
});
});
If I understood what you want to achieve the following code works for you. The conditions I set are the same as your validation rules:
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
TextEditingController _textEditingController = TextEditingController();
Function _submitFunction;
// We need a reusable timer and it needs to not be null so that we can cancel it from th start.
Timer _timer = Timer(Duration(milliseconds: 1), () => print('initialTimer'));
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Auto disable button'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
controller: _textEditingController,
onChanged: (text) {
setState(() {
_submitFunction = null;
});
restartTimer();
},
),
RaisedButton(
child: Text('Submit'),
onPressed: _submitFunction,
),
],
),
),
)
);
}
void submitForm(){
print(_textEditingController.text);
}
void enableButton(){
if(_textEditingController.text != ''){
setState(() {
_submitFunction = submitForm;
});
}
}
void restartTimer(){
_timer.cancel();
_timer = Timer(Duration(milliseconds: 1500), enableButton);
}
}
You can use this method .
bool isEnable = false;
void validateButton() {
bool isValid = true;
isValid = userEmail.isNotEmpty &&
userPassword.isNotEmpty &&
validateEmail(userEmail) &&
userPassword.length >= 8;
setState(() {
isEnable = isValid;
});
}
now in your Textfield onChanged method you have to call this function
like This
onChanged: (email) {
userEmail = email;
setState(() {});
validateButton();
},
and in your Login Button
isEnable?ActiveButton():DisableButton()