How to disable mobile keyboard action key using Flutter? - flutter

I just need to disable the action button while the search query is empty. I'm not sure if this is possible with native Flutter.
Does this need to be done with each platform specifically? (iOS / Android)

You have a couple of options.
You can create a new focus:
FocusScope.of(context).requestFocus(new FocusNode())
Example:
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: ....,
)
)
)
}
The other option is to release the existing focus:
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
There is also a package: keyboard_dismisser

You just need to add a FocusNode to your TextFormField and request focus for it if the user presses the submit button on the keyboard when the field is empty. Here is a complete example:
class KeyboardKeeper60943209 extends StatefulWidget {
#override
_KeyboardKeeper60943209State createState() => _KeyboardKeeper60943209State();
}
class _KeyboardKeeper60943209State extends State<KeyboardKeeper60943209> {
List<String> items = List.generate(20, (index) => 'item $index');
TextEditingController _textEditingController = TextEditingController();
FocusNode _focusNode = FocusNode();
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: TextFormField(
focusNode: _focusNode,
controller: _textEditingController,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
labelText: 'Search',
hasFloatingPlaceholder: false,
),
// This is the key part
onFieldSubmitted: (value) {
if(value == ''){
_focusNode.requestFocus();
}
},
),
),
FlatButton(onPressed: search, child: Text('Search'))
],
),
Expanded(
child: Container(
child: ListView.builder(
itemBuilder: (context, index){
return ListTile(
title: Text(items[index]),
subtitle: Text(items[index]),
);
}
),
),
),
],
);
}
void search(){
print('search');
}
}

TextFormField(
readOnly: true,
showCursor: false,
controller: consName,
decoration: InputDecoration(
hintText: 'Enter Name',
labelText: 'Username',
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.deepPurple,
width: 2,
),
),
),
),
Very Simple & Easy Use "readOnly: true" for disabling keyboard and if you don't want pointer or cursor then "showCursor: false". That's it, hope this'll work. Eat Sleep Workout Code Repeat. Happy Coding😊.

You can simply use textInputAction: TextInputAction.none on the TextField.

Related

Good way to mange state inside dialog with a form on it, being displayed from page with its own bloc?

See for the invoice page I have BlocBuilder wrapped in a scaffold of stateful page, inside that body under several widgets is a call to future void in separate file call to create a dialog widget. And inside the dialog method is a call to create an invoice form which is in a separate file and is stateful class displayed to be displayed on the dialog screen. In this form the user will be able to add and delete UI elements from a list view what I need to do is rebuild the widget either dialog screen/form or the list view/ to reflect the changes made by the user
import 'package:flutter/material.dart';
import 'dart:developer' as dev;
import 'package:track/src/features/invoices/application/bloc.dart';
import 'package:track/src/features/invoices/application/events.dart';
import 'package:track/src/features/invoices/application/pdf_invoice_api.dart';
class InvoiceForm extends StatefulWidget {
final InvoiceBlocController blocController;
const InvoiceForm(this.blocController, {Key? key}) : super(key: key);
#override
State<InvoiceForm> createState() => _InvoiceFormState();
}
class _InvoiceFormState extends State<InvoiceForm> {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
controller: TextEditingController()
..text = widget.blocController.invoice.client,
validator: (value) {
value!.isEmpty ? 'Enter a value for client' : null;
},
style: Theme.of(context).textTheme.labelMedium,
decoration: InputDecoration(
focusedBorder: const UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
),
),
enabledBorder: const UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
),
),
labelText: 'Client:',
labelStyle: Theme.of(context).textTheme.labelMedium),
),
TextFormField(
controller: TextEditingController()
..text =
'${widget.blocController.invoice.projectNumber}-${widget.blocController.invoice.invoiceNumber}',
validator: (value) {
value!.isEmpty ? 'Enter a valid project number' : null;
},
style: Theme.of(context).textTheme.labelMedium,
decoration: InputDecoration(
focusedBorder: const UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
),
),
enabledBorder: const UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
),
),
labelText: 'Client:',
labelStyle: Theme.of(context).textTheme.labelMedium),
),
ListView.builder(
shrinkWrap: true,
itemCount: widget.blocController.invoice.items.length,
itemBuilder: (context, index) {
final item = widget.blocController.invoice.items[index];
return ListTile(
contentPadding: EdgeInsets.zero,
trailing: IconButton(
onPressed: () {
widget.blocController.add(DeleteItemFromInvoice(index));
},
icon: Icon(Icons.delete)),
title: Column(
children: [
Row(
children: [
itemTextFormField(
initialValue: item.name ?? '',
labelText: 'name',
index: index),
SizedBox(width: 20),
itemTextFormField(
initialValue: item.description ?? '',
labelText: 'description',
index: index),
],
),
Row(
children: [
itemTextFormField(
initialValue: item.quantity.toString(),
labelText: 'quantity',
index: index),
SizedBox(width: 20),
itemTextFormField(
initialValue: item.costBeforeVAT.toString(),
labelText: 'Cost Before VAT',
index: index),
],
),
SizedBox(height: 30),
Divider(
thickness: 2,
color: Colors.black,
)
],
),
);
},
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
onPressed: () {
dev.log('button clicked to add new item');
widget.blocController.add(AddNewItemToInvoice());
},
icon: Icon(Icons.add)),
IconButton(
onPressed: () async {
_formKey.currentState!.save();
Navigator.pop(context);
await PdfInvoiceApi.generate(widget.blocController.invoice);
},
icon: Icon(Icons.send))
],
)
],
),
);
}
Expanded itemTextFormField({
required String initialValue,
required String labelText,
required int index,
}) {
return Expanded(
child: TextFormField(
controller: TextEditingController()..text = initialValue,
onSaved: (newValue) {
widget.blocController.add(UpdateInvoiceDetails(index));
},
style: Theme.of(context).textTheme.labelMedium,
decoration: InputDecoration(
focusedBorder: const UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
),
),
enabledBorder: const UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
),
),
labelText: labelText,
labelStyle: Theme.of(context).textTheme.labelMedium,
),
),
);
}
}
InvoiceDialog Source code: https://pastebin.com/PCjmCWsk
InvoiceDialog Source code: https://pastebin.com/VS5CG22D
Edit 2: Made the follwoing changes to bloc per Mostafa answer as best I could, getting pressed against a deadline here so really need some help:
These changes were to main page calling the show dialog passing bloc.
showDialog(
context: context,
builder: (context) => BlocProvider.value(
value: blocController,
child: InvoiceDetailsDialog(
screenWidth: screenWidth,
screenHeight: screenHeight),
),
);
This file was the original place where showdialog was called and was custom Future return showDialog.
Results: showDialog takes enitre screen. Rendering Invoice form reulsts in error being displayed in place of the form:
No Material widget found.
Edit 3: fixed previous error but back where i started bloc is still being called succesfully but no changes to the ui:
Widget build(BuildContext context) {
final blocController = BlocProvider.of<InvoiceBlocController>(context);
return Center(
child: Material(color: Colors.red,
borderRadius: BorderRadius.circular(50),
child: SizedBox(
width: screenWidth / 2, height: screenHeight / 2,
child: Padding(padding: const EdgeInsets.all(20),
child: Column(children: [
Expanded(child: ListView(children: [
Text('Invoices',
style: Theme.of(context)
.textTheme.bodyLarge?.copyWith(color: Colors.white)),
InvoiceForm()
]))])))));
}
As form nothing changed except instead of passing the blocController through a method I am now calling it like:
class _InvoiceFormState extends State<InvoiceForm> {
final _formKey = GlobalKey<FormState>();
late final InvoiceBlocController blocController;
#override
void initState() {
blocController = BlocProvider.of<InvoiceBlocController>(context);
super.initState();
}
Still nothing changes.
Edit 4: Set state does work and leaving in bloc code was executing and if I clicked add two items would be added or delete would remove two items. But with setstate commented out it went back to not rebuilding. Using setstate for now but not preferred.
Edit 5: Don't if this is still being paid attention to hopefully is. Can I keep add add events like: add(NewItem), add(deleteItem),add(GeneratePDF). Without changing state. currently I have done that once so far. Is this bad practice
You can pass the main bloc to the dialog widget and call the bloc function that you want and it will reflect on the main screen
How can you do this? by injecting the MainBloc value to DialogWidget with BlocProvider.value
MainWidget
class MainWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => MainBloc(),
child: BlocConsumer<MainBloc, MainStates>(
listener: (BuildContext context, MainStates state) {},
builder: (BuildContext context, MainStates state) {
final bloc = MainBloc.get(context);
return GestureDetector(
onTap: () {
showDialog(
context,
builder: (context) => BlocProvider.value(
value: bloc,
child: WidgetTwoDialog(),
),
);
},
child: Item(),
);
},
),
);
}
}
DialogWidget
class DialogWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
final bloc = MainBloc.get(context);
return GestureDetector(
onTap: () {
bloc.addToList();
},
child: Text('Remove form the main screen'),
);
}
}
Also, this answer might help you to get my point well here
you can wrap your dialog within the stateful builder and then you will get the method to set your dialog state.

Assign different field input for different textformfields under same Wrap child

I followed the flutter documentation here on how to validate multiple TextFormField at once. But in this example all the textformfields are created with same input field i.e, Name. I want that different fields can be used for different inputs such as Name, Password, Email, etc. Can someone help on how to implement the above?
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
Widget build(BuildContext context) {
return Material(
child: Center(
child: Shortcuts(
shortcuts: <LogicalKeySet, Intent>{
// Pressing enter on the field will now move to the next field.
LogicalKeySet(LogicalKeyboardKey.enter): NextFocusIntent(),
},
child: FocusTraversalGroup(
child: Form(
autovalidate: true,
onChanged: () {
Form.of(primaryFocus.context).save();
},
child: Wrap(
children: List<Widget>.generate(5, (int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ConstrainedBox(
constraints: BoxConstraints.tight(Size(200, 50)),
child: TextFormField(
decoration: const InputDecoration(
icon: Icon(Icons.person),
hintText: 'What do people call you?',
labelText: 'Name *',
),
onSaved: (String value) {
// This optional block of code can be used to run
// code when the user saves the form.
},
validator: (String value) {
return value.contains('#') ? 'Do not use the # char.' : null;
},
),
),
);
}),
),
),
),
),
),
);
}
}
Make a class with hintText, labelText etc as fields , make a list of instances of this class and provide it to TextFormField :
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
final List<HintAndLabel> list = const <HintAndLabel>[HintAndLabel(labelText:'Name',hintText:"What do people call you?"),
HintAndLabel(labelText:'label',hintText:"hint"),
HintAndLabel(labelText:'label',hintText:"hint"),
HintAndLabel(labelText:'label',hintText:"hint"),
HintAndLabel(labelText:'label',hintText:"hint")];
#override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Shortcuts(
shortcuts: <LogicalKeySet, Intent>{
// Pressing enter on the field will now move to the next field.
LogicalKeySet(LogicalKeyboardKey.enter): NextFocusIntent(),
},
child: FocusTraversalGroup(
child: Form(
autovalidate: true,
onChanged: () {
Form.of(primaryFocus.context).save();
},
child: Wrap(
children: List<Widget>.generate(5, (int index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ConstrainedBox(
constraints: BoxConstraints.tight(Size(200, 50)),
child: TextFormField(
decoration: InputDecoration(
icon: Icon(Icons.person),
hintText: list[index].hintText,
labelText: list[index].labelText,
),
onSaved: (String value) {
// This optional block of code can be used to run
// code when the user saves the form.
},
validator: (String value) {
return value.contains('#') ? 'Do not use the # char.' : null;
},
),
),
);
}),
),
),
),
),
),
);
}
}
class HintAndLabel
{
final String hintText;
final String labelText;
const HintAndLabel({this.hintText,this.labelText});
}

Flutter Textform Validation not showing up

I have watched a couple of tutorials on how to make validators work but none of them seemed to work. Can anyone help me with this? This is the code of a simple sign in page. My validators don't show up on screen if there's any sort of error it should be detecting. I've watched tutorials where it shows up in red but in my app, it doesn't show up at all.
class UserLogin extends StatefulWidget {
UserLogin({this.auth,this.onSignedIn});
final BaseAuth auth;
final VoidCallback onSignedIn;
#override
State<StatefulWidget> createState()=> _UserLoginState();
}
class _UserLoginState extends State<UserLogin> {
final formkey = GlobalKey<FormState>();
bool _validateAndSave()
{
final form = formkey.currentState;
if(form.validate())
{
form.save();
return true;
}
else
return false;
}
static final incorrect_icon = Icon(
Icons.error,
color: Colors.pink,
);
void _validateAndSubmit() async
{
if(_validateAndSave()) {
try {
String userId = await widget.auth.signIn(emailid, password);
print('Signed in! $userId');
//widget.onSignedIn();
Navigator.push(context, MaterialPageRoute(builder: (context)=>Feed()));
}
catch (e) {
print('Error: $e');
}
}
}
static final TextEditingController emailContr = new TextEditingController();
static final TextEditingController passwordContr = new TextEditingController();
static String get emailid => emailContr.text;
static String get password => passwordContr.text;
final _email = Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: TextFormField(
keyboardType: TextInputType.emailAddress,
controller: emailContr,
autofocus: false,
validator: (input) {
if(input.isEmpty)
{
return 'Email cannot be empty';
}
return null;
},
//onSaved: (input)=> emailid = input,
decoration: InputDecoration(
hintText: 'Enter Email Address',
suffixIcon: Icon(Icons.email),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)
),
),
),
);
final _pass = Container(
padding: EdgeInsets.only(left: 10, right: 10),
child: TextFormField(
controller: passwordContr,
obscureText: true,
autofocus: false,
validator: (input) {
if(input.length <= 6)
{
return 'Password should be at least 6 characters';
}
return null;
},
decoration: InputDecoration(
hintText: 'Enter password',
suffixIcon: Icon(Icons.lock),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)
),
),
),
);
/*final login_button =
},
);
*/
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.yellow,
body: Container(
child: Form(
key: formkey,
child: Column(
children: <Widget>[
SizedBox(height: 200,),
Text('Vibing',
style:TextStyle(
fontWeight: FontWeight.bold,
fontSize: 64,
),
),
SizedBox(height: 100,),
_email,
SizedBox(height: 20,),
_pass,
SizedBox(height:30),
RaisedButton(
color: Colors.yellow,
elevation: 5,
child: Text('Login'),
onPressed: (){
_validateAndSubmit();
formkey.currentState.reset();
}
),
SizedBox(height:10),
FlatButton(
child: Text('Forgot password'),
onPressed: ()=> Navigator.push(context, MaterialPageRoute(builder:(context)=>ForgotPassword()),)
),
SizedBox(height:10),
FlatButton(
child: Text('New? Register here!'),
onPressed: ()=> Navigator.push(context, MaterialPageRoute(builder:(context)=>UserReg()),)
),
],
),
),
) ,
);
}
}
The problem is that you're resetting the form after validation so any error shown will reset. Just remove this line from your login button callback:
formkey.currentState.reset();
And voila:
reset():
Resets every [FormField] that is a descendant of this [Form] back to its
[FormField.initialValue].
In your case, the initialValue is empty string "" and that's why when you called reset() method of Form, it's setting an empty string, that will not show any error, as nothing is there.

Flutter:How to automatically clear my TextFields?

I want to recreate automatically my page and clear all my TextFields on button press.Is there a method to do it?
Edited :
Here is an example of how to achieve this :
class _MyWidgetState extends State<MyWidget> {
final myAController = TextEditingController();
final myBController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: <Widget>[
FlatButton(
onPressed: () {
setState(() {
myAController.clear();
myBController.clear();
});
},
child: Text(
"Clear text fields",
),
),
TextField(
controller: myAController,
decoration: InputDecoration(
border: InputBorder.none, hintText: 'Enter value A'),
),
TextField(
controller: myBController,
decoration: InputDecoration(
border: InputBorder.none, hintText: 'Enter Value B'),
),
],
),
),
);
}
}
As you can see every time i press my flatbutton the widget reloads with the updated counter value.
Here is an updated working demo.
I think it's
setState((){
// If there is anything you want to change put it here
});
This will rebuild the widget.

Make the text editable after press the button

Is anyone know how to make the text editable after I press the button? I want it to be editable only when the user clicks the button. Below is the result that I needed.
If I want to edit the text "Festive Leave", then I need to click the edit button.
I think this should do what you want.
TextEditingController _controller =
TextEditingController(text: "Festive Leave");
bool _isEnable = false;
//These are initialize at the top
Row(
children: <Widget>[
Container(
width: 100,
child: TextField(
controller: _controller,
enabled: _isEnable,
),
),
IconButton(
icon: Icon(Icons.edit),
onPressed: () {
setState(() {
_isEnable = true;
});
})
],
),
Row(
children: <Widget>[
Container(
width: 100,
child: TextField(
decoration: InputDecoration(
hintText: "Festive Leave",
hintStyle: TextStyle(color: Colors.black),
),
enabled: _isEnable,
),
),
IconButton(
icon: Icon(Icons.edit),
onPressed: () {
setState(() {
_isEnable = true;
});
})
],
),
your choice this one the text will go away as soon as the user starts to type though
I think this will help you
class SampleDemo extends StatefulWidget {
#override
_SampleDemoState createState() => _SampleDemoState();
}
class _SampleDemoState extends State<SampleDemo> {
String title = "MyTitle";
bool isEditable=false;
#override
Widget build(BuildContext context) {
return Row(children: [
Expanded(
child: !isEditable
? Text(title)
: TextFormField(
initialValue: title,
textInputAction: TextInputAction.done,
onFieldSubmitted: (value) {
setState(() => {isEditable = false, title = value});
})),
IconButton(
icon: Icon(Icons.edit),
onPressed: () {
setState(() => {
isEditable = true,
});
},
)
]);
}
}