How to use switch case syntax on flutter - flutter

I'm making login and sign up page in my mobile application with flutter. I'm wondering how can I put the right data which is matched with the props.
For example, if I got the 'password' as a textValue, then I want to return the warningText which is 'Password must be at least 7 characters long.', the icon would be Icons.lock, and the hintText would be 'password'.
The textValue can be three options which are 'username', 'password' and 'email'.
textValue : username
warningText : 'Please enter at least 4 characters'
icon : Icons.account_circle,
hintText : username
textValue : email
warningText : 'Please enter a valid email address.'
icon : Icons.email,
hintText : email
Also, the reason why I want to use switch case syntax is because I want to make my codes shorter. I could just use multiple of textformfield, but I want to make my codes shorter and make them re-useable.
Because I'm using Provider for state management, I want to put the switch case codes into the Provider.
This is the textformfield widget code.
class LoginSignupTextFormField extends StatelessWidget {
const LoginSignupTextFormField({Key? key,
required this.valueKeyNumb,
required this.textValue})
: super(key: key);
final int valueKeyNumb;
final String textValue;
#override
Widget build(BuildContext context) {
return TextFormField(
obscureText: textValue == 'password' ? true : false,
key: const ValueKey(3),
validator: (value) {
if (value!.isEmpty || value.length < 6) {
return 'Password must be at least 7 characters long.';
}
return null;
},
onSaved: (value) {
context.watch<LoginSignupData>().userPassword = value!;
},
onChanged: (value) {
context.watch<LoginSignupData>().userPassword = value;
},
decoration: const InputDecoration(
prefixIcon: Icon(
Icons.lock,
color: Colors.red,
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.blueGrey),
borderRadius: BorderRadius.all(
Radius.circular(35.0),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.black),
borderRadius: BorderRadius.all(
Radius.circular(35.0),
),
),
hintText: 'password',
hintStyle: TextStyle(
fontSize: 14,
color: Colors.red),
contentPadding: EdgeInsets.all(10)),
);
}
}
This is my Provider code for the textformfield and I want to put the switch case codes in it.
class LoginSignupData extends ChangeNotifier{
final authentication = FirebaseAuth.instance;
bool isSignup = true;
final formKey = GlobalKey<FormState>();
String userName = '';
String userEmail = '';
String userPassword = '';
changeBool(status){
isSignup = status;
notifyListeners();
}
tryValidation() {
final isValid = formKey.currentState!.validate();
if (isValid) {
formKey.currentState!.save();
notifyListeners();
}
}
}

You may have to take three separate textfield for that and assign values to each.
If you want to make common textfield for all then you can take enum with three values and you have to pass that in constructor.
enum TextFieldType {EMAIL, PASSWORD, USERNAME};
Now you can check the value inside the validator and according to that you can show the error.
switch (enumValue) { // Your Enum Value which you have passed
case TextFieldType.PASSWORD:
//Your validations for password
break;
case TextFieldType.EMAIL:
//Your validations for email
break;
case TextFieldType.USERNAME:
//Your validations for username
break;
}
Hope it will help you.

Related

Flutter focusnode problem not working - only by clicking it works

iam using a device that scans barcode , I want after each read the focus to return the TextFormFiled - the code below do the work and I see the cursor is focused on the TextFormFiled but when I read next time its show nothing , I need to manually just click by my finger on the textfiled to activate the focus ,can somebody help me ( the device returned LF after each read)
TextFormField(
decoration: new InputDecoration(
border: new OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(10.0),
),
),
filled: true,
hintStyle: new TextStyle(
color: Colors.grey[800]),
hintText: "Read BarCode",
fillColor: Colors.white70),
focusNode: myFocusNode,
controller: search,
autofocus: true,
maxLines: null,
validator: (value) {
// print(value.toString().runes);
if (value.toString().contains("\n")) {
fetchProducts(value!);
search.text = "";
} else {}
},
),
Use your myFocusNode to activate the focus on textField.
void function(){
/// after scanning is complete call this
focusNode.requestFocus()
}
I pass for this and did solve it like this:
_addItem() {
final isValid = _formKey.currentState?.validate() ?? false;
if (!isValid) {
return;
}
final ean = int.parse(_eanController.text);
listaEan.add(ean);
_eanController.text = '';
setState(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_eanFocus.requestFocus();
});
});
}
but in physical scan device, did works fine. without use the addPostFramaCallback.

How to dynamically change colour of errorstyle in textformfield based on validator?

I have a TextFormField that checks if the input meets a set of requirements using validator. If the input does not meet a specific requirement an error text in red will inform the user accordingly. I want to return a text in green ("Secure password") if the password meets all of the requirements.
class RegForm extends StatefulWidget {
const RegForm({Key? key}) : super(key: key);
#override
_RegFormState createState() => _RegFormState();
}
class _RegFormState extends State<RegForm> {
Color errorClr = Colors.red;
final String checkAll =
r'^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!##\$&*~]).{8,}$';
final String checkLetters = r'^(?=.*?[A-Z])(?=.*?[a-z])';
final String checkNumbers = r'^(?=.*?[0-9])';
#override
Widget build(BuildContext context) {
return Scaffold(
body: TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a valid password';
} else {
if (value.length < 8)
return 'Password has to be 8 characters or more!';
else if (!RegExp(checkLetters).hasMatch(value))
return 'Password has to contain Uppercase and Lowercase letters!';
else if (!RegExp(checkNumbers).hasMatch(value))
return 'Password has to contain numbers!';
else if (RegExp(checkAll).hasMatch(value)) {
errorClr = Colors.green;
return 'Very secure password!';
}
}
errorClr = Colors.green;
return 'Secure password!';
},
decoration: InputDecoration(
errorStyle: TextStyle(
color: errorClr,
),
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
hintText:
'Password must be at least 8 characters long, contains numbers and both upper and lowercase letters',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
),
);
}
}
This code above will not return the "Secure password" text in green and I figured its because validator doesn't rebuild the widget, thus the errorStyle doesn't update. Is there a way to make this work?
You can't call setState() from whithin the validator function, which is what you need to change the errorClr or the success text. Also the validator function needs to return null to indicate that the validation passes; so you cant simply return Secure password!.
What do you think about using helperText to output the success message.
If non-null, the text is displayed below the InputDecorator.child, in the same location as errorText. If a non-null errorText value is specified then the helper text is not shown.
Define a new variable that indicates if the password is valid (default: false):
bool validPassword = false;
Use onChanged to check for the valid password:
onChanged: (String value) {
if (RegExp(checkAll).hasMatch(value))
setState(() {
validPassword = true;
});
},
Add helperText and helperStyle to your InputDecoration:
helperText: validPassword ? 'Secure Password' : null,
helperStyle: TextStyle(color: Colors.green),
Complete Example:
class _RegFormState extends State<RegForm> {
final String checkAll = r'^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!##\$&*~]).{8,}$';
final String checkSpecial = r'^(?=.*?[!##\$&*~])';
final String checkLetters = r'^(?=.*?[A-Z])(?=.*?[a-z])';
final String checkNumbers = r'^(?=.*?[0-9])';
bool validPassword = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
if (value == null || value.isEmpty || value.trim().length < 8) // use trim() for the whitrespaces
return 'Password has to be 8 characters or more!';
if (!RegExp(checkLetters).hasMatch(value))
return 'Password has to contain Uppercase and Lowercase letters!';
if (!RegExp(checkNumbers).hasMatch(value))
return 'Password has to contain numbers!';
if (!RegExp(checkSpecial).hasMatch(value))
return 'Password has to contain a special character!';
return null; // to indicate a success
},
onChanged: (String value) {
if (RegExp(checkAll).hasMatch(value))
setState(() {
validPassword = true;
});
},
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
helperText: validPassword ? 'Secure Password' : null,
helperStyle: TextStyle(color: Colors.green),
hintText:
'Password must be at least 8 characters long, contains numbers and both upper and lowercase letters',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
),
);
}

How To Create Number Picker as a alert dialog in flutter?

I am developing e com application, when user click on quantity label want to show dialog box for select how many quantity he want to buy.
I tried to wrap number picker inside Alert dialog. It show alert dialog, but the problem is not updating value when scroll
I can I use Number Picker for that?
Use a drop button combo widget. The DropdownButtonFormField holds a collection of YourClassView type objects held in the list listStatusMenuItems. The _currentStatusComboView variable holds the selected value. It is assigned on the onChange event. I use a restful call to load the listStatusMenuItems.
List<DropdownMenuItem> listStatusMenuItems = <DropdownMenuItem>[];
StatusComboView _currentStatusComboView;
_loadStatusCombo() {
Provider.of<Api>(context, listen: false)
.getYourClassViews()
.then((listView) {
setState(() {
listStatusMenuItems =
listView?.map<DropdownMenuItem<YourClassView>>((item) {
return DropdownMenuItem<StatusComboView>(
value: item, child: Text(item.displayValue));
}).toList();
});
});
#override
void initState() {
super.initState();
_loadStatusCombo();
}
DropdownButtonFormField<YourClassView>(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(5.0),
),
),
filled: true,
hintStyle:
TextStyle(color: Colors.grey[800]),
hintText: "Select a Value",
fillColor: Colors.orange),
items: listStatusMenuItems,
isDense: true,
isExpanded: true,
value: this._currentStatusComboView,
validator: (value) =>
value == null ? 'field required' : null,
onChanged: (StatusComboView value) {
setState(() {
this._currentStatusComboView = value;
});
}),

Whats the best possible way to have labelTexts change based on the text being put in a Text Field?

I am working on making some label texts for Text Fields respond to if certain text fields have text in them. Example being if fields B and C are blank, the labelText for field A will have an asterisk before it ("* Label"). Or if field A is blank the labelText for B and C will have an asterisk before it ("* Label"). I have it sort of working currently, but to have the correct asterisk appear after I type in a field I have to swipe the drawer away Im doing this in and reopen it. Kinda like setting the state so it knows that theres new stuff to display. How would I get it to change the correct label right when I type instead of having to do it manually? Thanks!
Field A
TextField(
controller: Data.NumberEditingController,
obscureText: false,
decoration: InputDecoration(
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
labelText: requiredNumber(),
labelStyle: TextStyle(
color: Color.fromRGBO(105, 105, 105, 10),
)),
),
Field B
TextField(
controller: Data.NameEditingController,
obscureText: false,
decoration: InputDecoration(
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(12.0)),
),
labelText: requiredName(),
labelStyle: TextStyle(
color: Color.fromRGBO(105, 105, 105, 10),
)),
),
Field C
TextField(
controller: Data.addressEditingController,
obscureText: false,
decoration: InputDecoration(
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius:
BorderRadius.all(Radius.circular(12.0)),
),
labelText: requiredAddress(),
labelStyle: TextStyle(
color: Color.fromRGBO(105, 105, 105, 10),
)),
),
These are the functions I use to check for any text in the controllers so they can return the correct label to use. As stated above they work correctly but only upon dismissing the drawer and opening it again so everything is refreshed and I want it to be automatic
requiredNumber() {
if (Data.addressEditingController.text == "") {
return "* Number";
}
else if (Data.nameEditingController.text == "") {
return "* Number";
}
else {
return "Number";
}
}
requiredName() {
if (Data.numberEditingController.text == "") {
return "* Name";
}
else {
return "Name";
}
}
requiredAddress() {
if (Data.numberEditingController.text == "") {
return "* Address";
}
else {
return "Address";
}
}
Best solution and cleanest for your case would be to put the logic inside the onChanged property of the TextField. I'll give an example using your first function:
Change this:
requiredName() {
if (Data.numberEditingController.text == "") {
return "* Name";
}
else {
return "Name";
}
}
into this:
String requiredNumber = "Number";
String requiredName = "Name";
String Address = "Address";
//and inside your TextField for the Number for example, use onChanged
onChanged: (value) {
if (value == '') {
setState(() {
requiredName = '* Name';
});
} else {
setState(() {
requiredName = 'Name';
});
}
}
define String variables for all your labels in your stateful widget:
String numberLabel = '';
Add listeners on all your textControllers during initState like so:
#override
void initState() {
super.initState();
// Start listening to changes.
addressEditingController.addListener(setState((){numberLabel=requiredNumber()})); // Im not sure here callback may be wrapped in () {}
}
Then use this variables in your labelText properties:
labelText: numberLabel,
Remove registered listeners before dispose method:
#override
void dispose() {
addressEditingController.removeListener(setState((){numberLabel=requiredNumber()}));
addressEditingController.dispose();
super.dispose();
}

TextField keeps old value

I have an issue with a textfield in flutter.
I need to get a town or postal code to redirect the user to another page. But it seems like I keep old value even if the text inside textfield changes.
I tried to clear it, make conditions but I don't understand why the text does not keep the current value.
Here some code :
Check if it's Paris, for the borough
#override
void initState() {
editingController.addListener((){
setState(() {
if(editingController.text.isEmpty) {
editingController.clear();
isParis = false;
}
});
});
super.initState();
}
Button which redirect, the method getPcFromQuery (it's an async method) retrieve data relatives to a town name.
RaisedButton(
onPressed: (){
print(editingController.text);
getPCFromQuery(editingController.text)
.then((town) {city = town.featureName; postalCode = town.postalCode;});
setState(() {
if(city.toString().toLowerCase() == "paris")
isParis = true;
else
isParis = false;
});
print("postalCode : $postalCode");
postalCode != null && city.toString().toLowerCase() != "paris"?
Navigator.of(context).push(MaterialPageRoute(builder: (context) => PreferencesPage(postalCode: postalCode))) :
Container(child: Text(""));
})
But when I click on the next button (I need to click on it two times to make it work) and when I change the value inside my textfield, it keeps the previously entered value.
Do you know where it can come from?
This is TextField built:
class SearchBar extends StatelessWidget {
const SearchBar({
Key key,
#required this.editingController,
}) : super(key: key) ;
final TextEditingController editingController;
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(10.0)),
child: TextField(
controller: editingController,
decoration: InputDecoration(
hintText: AppStrings.searchTown,
hintStyle: GoogleFonts.notoSans(fontSize: 14.0),
border: InputBorder.none,
fillColor: Colors.grey.withOpacity(0.5),
prefixIcon: Icon(Icons.search, color: Colors.grey)
),
)
);
}
}
And method _getPcFromQuery :
_getPCFromQuery(String city) async {
var addresses = await Geocoder.local.findAddressesFromQuery(city);
var first = addresses.first;
print(first.featureName);
print(first.addressLine);
return first;
}
The weird thing is, when I try the first time, the prints inside _getPcFromQuery method get text, but the print "print("postalCode : $postalCode");" on the onPressed method button is null.
Specify the key parameter for TextField.
class SearchBar extends StatelessWidget {
const SearchBar({
Key key,
#required this.editingController,
}) : super(key: key) ;
final TextEditingController editingController;
final searchKey = GlobalKey(); // <-- create a global key
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(10.0)),
child: TextField(
key: searchKey, // <-- specify the declared key here
controller: editingController,
decoration: InputDecoration(
hintText: AppStrings.searchTown,
hintStyle: GoogleFonts.notoSans(fontSize: 14.0),
border: InputBorder.none,
fillColor: Colors.grey.withOpacity(0.5),
prefixIcon: Icon(Icons.search, color: Colors.grey)
),
)
);
}
}