Make the text editable after press the button - flutter

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,
});
},
)
]);
}
}

Related

TextField not accept text on first click Flutter

On my first page I have ListView.builder.
ListView.builder(
itemCount:_docLocatedLot.length,
itemBuilder: (context, index) => EditListTile(
model: _docLocatedLot[index], onChanged: (ItemsLocatedLot updatedModel) {
_docLocatedLot[index] = updatedModel;
},
),
)
Bellow is my source code of EditListTile, after click on edit button and click on TextField new value from keyboard not accept. After click again on TextField (second click) I can enter value in TextField.
How fix this problem?
Where is error in my code? Why I need two click to enter new value?
class EditListTile extends StatefulWidget {
final ItemsLocatedLot model;
final Function(ItemsLocatedLot listModel) onChanged;
const EditListTile({super.key, required this.model, required this.onChanged});
#override
State<EditListTile> createState() => _EditListTileState();
}
class _EditListTileState extends State<EditListTile> {
late ItemsLocatedLot model;
late bool _isEditingMode;
late TextEditingController _subTitleEditingController;
#override
void initState() {
super.initState();
model = widget.model;
_isEditingMode = false;
}
#override
Widget build(BuildContext context) {
return ListTile(
title: titleWidget,
subtitle: subTitleWidget,
trailing: tralingButton,
);
}
Widget get titleWidget {
return Text(
model.lot
);
}
Widget get subTitleWidget {
if (_isEditingMode) {
_subTitleEditingController = TextEditingController(text: model.quantity);
return TextField(
controller: _subTitleEditingController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
suffix: InkWell(
onTap: () {
_subTitleEditingController.text = '';
},
child: Icon(
Icons.clear,
),
),
)
);
} else {
return Text(
model.quantity.toString();
);
}
}
Widget get tralingButton {
if (_isEditingMode) {
return IconButton(
icon: const Icon(Icons.check),
onPressed: saveChange,
);
} else {
return IconButton(
icon: const Icon(Icons.edit),
onPressed: _toggleMode,
);
}
}
void _toggleMode() {
setState(() {
_isEditingMode = !_isEditingMode;
});
}
void saveChange() {
model.quantity = _subTitleEditingController.text;
_toggleMode();
widget.onChanged(model);
}
}
You are using InkWell inside TextField ,So
First Click is consumed by InkWell
Second Click is consumed by TextField
Problem causing widget
TextField
|_ InkWell
TextField(
controller: _subTitleEditingController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
suffix: InkWell(
onTap: () {
_subTitleEditingController.text = '';
},
child: Icon(
Icons.clear,
),
),
)
);
Solution
Replace InkWell with IconButton.
TextField(
controller: _subTitleEditingController,
keyboardType: TextInputType.number,
decoration: InputDecoration(
suffixIcon: IconButton(
onTap: () {
_subTitleEditingController.text = '';
},
child: Icon(
Icons.clear,
),
),
)
);
Output:

Delete Widget at button press Flutter

Recently implemented a tagForm widget at "+" button press, I want to delete those widgets now at "delete" button press, but right now, even when I press the "delete" button, nothing happens.
How can I solve this?
Any help appreciated!
code:
import 'package:flutter/material.dart';
import '../database/firestoreHandler.dart';
import '../models/todo2.dart';
import '../widgets/dialogs.dart';
class TodoEdit extends StatefulWidget {
String? doctitle;
String? doctdescription;
String? docimage;
String? docid;
List? doctags;
TodoEdit({Key? key, this.doctitle, this.doctdescription, this.docimage, this.docid,this.doctags}) : super(key: key);
#override
_TodoEditState createState() => _TodoEditState();
}
class _TodoEditState extends State<TodoEdit> {
final _formKey = GlobalKey<FormState>();
final tcontroller = TextEditingController();
final dcontroller = TextEditingController();
final icontroller = TextEditingController();
var textEditingControllers = <TextEditingController>[];
//-----------------the list where the form is stored----------
var textformFields = <Widget>[];
void _addformWidget(controller) {
setState(() {
textformFields.add(tagForm(controller));
});
}
//------------------------------------------------------------------------
Widget tagForm(controller){
return TextFormField(
controller: controller,
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
labelText: "Tag",
labelStyle: TextStyle(color: Colors.white60),
fillColor: Colors.black,
filled: true,
suffixIcon: IconButton(
icon:Icon(Icons.delete, color: Colors.white,),
//--------------------- doesn't work?-------------------
onPressed: (){
setState(() {
textformFields.remove(tagForm(controller));
});
},
--------------------------------------------------------------
)
),
);
}
//-----------------------------------------------------------
#override
void initState() {
super.initState();
tcontroller.text = widget.doctitle.toString();
dcontroller.text = widget.doctdescription.toString();
icontroller.text = widget.docimage.toString();
widget.doctags?.forEach((element) {
var textEditingController = new TextEditingController(text: element);
textEditingControllers.add(textEditingController);
//return textformFields.add(tagForm(textEditingController)
return _addformWidget(textEditingController);
//);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[900],
appBar: AppBar(
actions: [
IconButton(onPressed: (){
showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
title: Text('Delete TODO'),
actions: [
TextButton(
child: Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
),
TextButton(
child: Text('Delete'),
onPressed: () {
deleteData(widget.docid.toString(), context);
setState(() {
showSnackBar(context, 'todo "${widget.doctitle}" successfully deleted!');
});
},
),
],
);
},
);
},
icon: Icon(Icons.delete))
],
backgroundColor: Colors.grey[900],
title: Text("${widget.doctitle}"),
),
body: Container(
child: SafeArea(
child: Form(
key: _formKey,
child: Column(
children: [
SizedBox(height: 10),
TextFormField(
controller: tcontroller,
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
labelText: "Title",
labelStyle: TextStyle(color: Colors.white60),
fillColor: Colors.black,
filled: true,
),
),
SizedBox(height: 10),
TextFormField(
controller: dcontroller,
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
labelText: "Description",
labelStyle: TextStyle(color: Colors.white60),
fillColor: Colors.black,
filled: true,
),
),
SizedBox(height: 10),
TextFormField(
controller: icontroller,
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
labelText: "Image url",
labelStyle: TextStyle(color: Colors.white60),
fillColor: Colors.black,
filled: true,
),
),
SizedBox(height: 10),
Row(children: [
Text("Tags:", style:TextStyle(color: Colors.white)),
IconButton(onPressed: (){
var textEditingController = new TextEditingController(text: "tag");
textEditingControllers.add(textEditingController);
_addformWidget(textEditingController);
print(textformFields.length);
},
icon: Icon(Icons.add,color: Colors.white,),
)
],),
/*SingleChildScrollView(
child: new Column(
children: textformFields,
)
),*/
Expanded(
child: SizedBox(
height: 200.0,
child: ListView.builder(
itemCount: textformFields.length,
itemBuilder: (context,index) {
return textformFields[index];
}),
)
),
],
),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){
List<String> test = [];
textEditingControllers.forEach((element) {
test.add(element.text);
});
if(tcontroller == '' && dcontroller == '' && icontroller == ''){
print("not valid");
}else{
var todo = Todo2(
title: tcontroller.text,
description: dcontroller.text,
image: icontroller.text,
tags: test,
);
updateData(todo, widget.docid.toString(),context);
setState(() {
showSnackBar(context, 'todo ${widget.doctitle} successfully updated!');
});
}
},
child: Icon(Icons.update),
),
);
}
}
You can't remove anything from the list with objects from tagForm(controller), because these objects are newly created and therefore not the same as in the list (as long as the == operator is not overwritten)
If you still want to have the widgets in a list instead of just storing the controllers and without having to change much, you could remove the widgets like this:
onPressed: (){
setState(() {
controller.dispose();
textEditingControllers.remove(controller);
textformFields.removeWhere((w) => w.controller = controller));
});
},
and change the type of your List: var textformFields = <TextFormField>[]; and of the method TextFormField tagForm(controller).
In general, you can of course optimize the state management, but with this solution it should work for now.
Dont't store Widget, it is bad way. Insteads store there property, render by List then remove by index when you need.
ps: some code syntax can wrong, i write this on browser.
class _TodoEditState extends State<TodoEdit> {
...
var textformFields = <String>[];
...
void _addformWidget([String? initValue]) {
setState(() => textformFields.add(initValue ?? ""));
}
...
Widget tagForm(String value, void Function(String) onChange, void Function() onRemove){
var openEditor = () {
// Open dialog with text field to edit from [value] call onChange with
// new value
OpenDialog().then((newvalue) {
if(newvalue != null) onChange(newvalue);
}
};
var delete = () {
// Open confirm dialog then remove
OpenConfirmDialog("your message").then((continue) {
if(continue) onRemove();
});
};
return InkWell(
onTap: openEditor,
child: Text(value), // render your tag value
);
}
...
#override
void initState() {
...
textformFields = List.filled(widget.doctags ?? 0, ""); // or List.generate/map if you want replace by own value.
}
...
#override
Widget build(BuildContext context) {
...
ListView.builder(
itemCount: textformFields.length,
itemBuilder: (context,index) => tagForm(
textformFields[index],
(newvalue) => setState(() => textformFields[index] = newvalue),
() => setState(() => textformFields = textformFields..removeAt(index));,
),
),
...
);
}

Is there any way I can reset the dynamic fields I added into a form to their previous state if user doesn't make any changes (Presses back)?

I'm trying to create a dynamic form so I used the idea of using a listview builder to create it. I was able to successfully create it but I faced that I cannot discard changes made to the form by popping it off after editing it. The two textFormField Job name and rate per hour were able to discard changes as they were using onsaved but on the checkbox I can't do that as it has onChanged which wraps setstate to change its state.
You can take a look at the video at this link to see how it functions as of now - https://vimeo.com/523847256
As you can see that it is retaining the data even after popping the page and coming back which I don't want it to. I'm looking for a way to prevent that and make the form the same as before if the user didn't press save.
I have tried to reassign the variables() in onpressed of back button but that didn't work. I also tried push replacement to the same page to reset it but that also didn't work. I think the cuprit here is the sublist and the initialValueTextFormField and initialValueCheckbox which are used declared under ListView.builder but I don't know how to fix that without affecting the dynamic list functionality.
class EditJobPage extends StatefulWidget {
const EditJobPage({Key key, this.job}) : super(key: key);
final Job job;
static Future<void> show(BuildContext context, {Job job}) async {
await Navigator.of(context, rootNavigator: true).pushNamed(
AppRoutes.editJobPage,
arguments: job,
);
}
#override
_EditJobPageState createState() => _EditJobPageState();
}
class _EditJobPageState extends State<EditJobPage> {
final _formKey = GlobalKey<FormState>();
String _name;
int _ratePerHour;
List<dynamic> _subList = [];
Set newSet = Set('', false);
#override
void initState() {
super.initState();
if (widget.job != null) {
_name = widget.job?.name;
_ratePerHour = widget.job?.ratePerHour;
_subList = widget.job?.subList;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 2.0,
title: Text(widget.job == null ? 'New Job' : 'Edit Job'),
leading: IconButton(
icon: Icon(Icons.clear),
onPressed: () {
Navigator.of(context).pop();
},
),
actions: <Widget>[
FlatButton(
child: const Text(
'Save',
style: TextStyle(fontSize: 18, color: Colors.white),
),
onPressed: () => _submit(),
),
],
),
body: _buildContents(),
backgroundColor: Colors.grey[200],
);
}
Widget _buildContents() {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: _buildForm(),
),
),
),
);
}
Widget _buildForm() {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: _buildFormChildren(),
),
);
}
List<Widget> _buildFormChildren() {
print(_subList);
return [
TextFormField(
decoration: const InputDecoration(labelText: 'Job name'),
keyboardAppearance: Brightness.light,
initialValue: _name,
validator: (value) =>
(value ?? '').isNotEmpty ? null : 'Name can\'t be empty',
onChanged: (value) {
setState(() {
_name = value;
});
},
),
TextFormField(
decoration: const InputDecoration(labelText: 'Rate per hour'),
keyboardAppearance: Brightness.light,
initialValue: _ratePerHour != null ? '$_ratePerHour' : null,
keyboardType: const TextInputType.numberWithOptions(
signed: false,
decimal: false,
),
onChanged: (value) {
setState(() {
_ratePerHour = int.tryParse(value ?? '') ?? 0;
});
},
),
Column(
children: <Widget>[
ListView.builder(
shrinkWrap: true,
itemCount: _subList?.length ?? 0,
itemBuilder: (context, index) {
String initialValueTextFormField =
_subList[index].subListTitle.toString();
bool initialValueCheckbox = _subList[index].subListStatus;
return Row(
children: [
Checkbox(
value: initialValueCheckbox,
onChanged: (bool newValue) {
setState(
() {
initialValueCheckbox = newValue;
_subList.removeAt(index);
_subList.insert(
index,
Set(initialValueTextFormField,
initialValueCheckbox));
},
);
},
),
Expanded(
child: TextFormField(
minLines: 1,
maxLines: 1,
initialValue: initialValueTextFormField,
autofocus: false,
textAlign: TextAlign.left,
onChanged: (title) {
setState(() {
initialValueTextFormField = title;
_subList.removeAt(index);
_subList.insert(
index,
Set(initialValueTextFormField,
initialValueCheckbox));
});
},
decoration: InputDecoration(
border: UnderlineInputBorder(),
labelStyle: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
),
filled: true,
hintText: 'Write sub List here',
),
),
),
],
);
},
),
TextButton(
onPressed: () {
setState(() {
_subList.add(newSet);
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.add),
Text('Add Sub Lists'),
],
),
),
],
),
];
}
void _submit() {
final isValid = _formKey.currentState.validate();
if (!isValid) {
return;
} else {
final database = context.read<FirestoreDatabase>(databaseProvider);
final id = widget.job?.id ?? documentIdFromCurrentDate();
final job = Job(
id: id,
name: _name ?? '',
ratePerHour: _ratePerHour ?? 0,
subList: _subList);
database.setJob(job);
Navigator.of(context).pop();
}
}
}
And this is the link to the full repository of the whole flutter app in case you want to look at any other part:- https://github.com/brightseagit/dynamic_forms . Thank you.
Note - This is the edited code of this repo - https://github.com/bizz84/starter_architecture_flutter_firebase.
When assigning the list we need to use _subList = List.from(widget.job.subList) instead of _subList = widget.job.subList.
Otherwise, the changes made in _subList will also be made in job.subList .

Flutter Visibility Toggle not working as expected

Creating a Screen where I want to perform Flutter-FireBase Searching.But Visibility Toggle is not working as desired.
Desired Toggle Behaviour : When clicked on TextForm field , prefix icon and result card should be visible. Upon Clicking the Prefix Icon(Back Arrow) , Result List (Card) and the prefix icon itself should become invisible and TextField should unfocus .
Actual Behaviour : On clicking the prefix icon , Result set and prefix icon don't disappear , Prefix icon remains there and result set becomes invisible but occupies some space beneath the TextFormField
class AddAppointmentWidget extends StatefulWidget {
#override
_AddAppointmentWidgetState createState() => _AddAppointmentWidgetState();
}
class _AddAppointmentWidgetState extends State<AddAppointmentWidget> {
bool searchbartapped = false;
var queryResultSet = [];
var tempSearchStore = [];
// Search Function
initiateSearch(value) {
//body
}
#override
Widget build(BuildContext context) {
return ListView(
children: [
SizedBox(
height: 15,
),
Padding(
padding: const EdgeInsets.all(18.0),
child: Text('Search',
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
flex: 5,
child: TextFormField(
style: TextStyle(color: Color(0xff2a2a2a), fontSize: 18),
keyboardType: TextInputType.name,
onChanged: (value) {
initiateSearch(value);
},
onTap: () {
setState(() {
searchbartapped = true;
});
},
cursorColor: Color(0xff2a2a2a),
cursorWidth: 1.5,
decoration: InputDecoration(
hintText: "Search by Name",
prefixIcon: Visibility(
visible: searchbartapped,
child: IconButton(
icon: Icon(Icons.arrow_back),
color: Colors.black54,
onPressed: () {
setState(() {
searchbartapped = !searchbartapped;
queryResultSet = [];
tempSearchStore = [];
});
FocusScope.of(context).unfocus();
}),
),
)),
),
],
),
),
Visibility(
visible: searchbartapped,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
padding: EdgeInsets.all(5.0),
primary: false,
shrinkWrap: true,
children: tempSearchStore.map((element) {
print(element['name']);
return buildResult(context, element);
}).toList()),
),
),
],
);
}
}
Note The buildResult widget is working perfectly fine.
Problem is only with the visibilty toggle
The issue: When you tap the prefixIcon:
onPressed is called, setting searchbartapped to false which is what you want.
The onTap method of your TextFormField is also called (since prefixIcon is inside it), setting searchbartapped to true.
So what you want is to prevent the second event from happening. I tried to prevent the notification from bubbling up the tree but I couldn't. So what I ended up doing is a bit more manual but works just as well.
Solution: Add a variable (for example hideSearchTapped) which is set to true when the prefixIcon is called. Then when the onTap method of your TextFormField is called, check this variable:
If hideSearchTapped is true, set it to false
Else change searchbartapped as you did
Here is a working example:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() async {
runApp(
MaterialApp(
home: Scaffold(
body: new AddAppointmentWidget(),
),
),
);
}
class AddAppointmentWidget extends StatefulWidget {
#override
_AddAppointmentWidgetState createState() => _AddAppointmentWidgetState();
}
class _AddAppointmentWidgetState extends State<AddAppointmentWidget> {
bool searchbartapped = false;
bool hideSearchTapped = false;
var queryResultSet = [];
var tempSearchStore = [];
// Search Function
initiateSearch(value) {
//body
}
#override
Widget build(BuildContext context) {
return ListView(
children: [
SizedBox(
height: 15,
),
Padding(
padding: const EdgeInsets.all(18.0),
child: Text('Search', style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold)),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
flex: 5,
child: TextFormField(
style: TextStyle(color: Color(0xff2a2a2a), fontSize: 18),
keyboardType: TextInputType.name,
onChanged: (value) {
initiateSearch(value);
},
onTap: () {
setState(() {
if (hideSearchTapped) {
hideSearchTapped = false;
} else {
searchbartapped = true;
}
});
},
cursorColor: Color(0xff2a2a2a),
cursorWidth: 1.5,
decoration: InputDecoration(
hintText: "Search by Name",
prefixIcon: Visibility(
visible: searchbartapped,
child: IconButton(
icon: Icon(Icons.arrow_back),
color: Colors.black54,
onPressed: () {
hideSearchTapped = true;
searchbartapped = !searchbartapped;
queryResultSet = [];
tempSearchStore = [];
setState(() {
});
FocusScope.of(context).unfocus();
return true;
}),
),
)),
),
],
),
),
Visibility(
visible: searchbartapped,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
padding: EdgeInsets.all(5.0),
primary: false,
shrinkWrap: true,
children: tempSearchStore.map((element) {
print(element['name']);
}).toList()),
),
),
],
);
}
}
Note: you should use lowerCamelCase to name your variable. So searchbartapped would become searchBarTapped.

How to disable mobile keyboard action key using 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.