I am getting a dynamic number of TextFormFields from the backend. Some fields are required and others are not. As it's dynamic I can't use FocusScope. What I want to achieve is When the user clicks on the next button, the focus should be redirected to the required fields which are empty. How can I achieve this? I can't provide code.
You can use this onEditingComplete, which will show you this example :
Note: If you made the static method just pass context to it.
It worked for me right.
TextField(
controller: controller,
keyboardType: type,
autofocus: false,
enableInteractiveSelection: false,
maxLength: maxLength,
///Using this :
onEditingComplete: () => FocusScope.of(context).nextFocus(),
decoration: InputDecoration(
hintText: 'Enter text',
border: InputBorder.none,
counterText: "",
contentPadding:
EdgeInsets.symmetric(vertical: 18.0, horizontal: 24.0)),
)
You should post some code always, here you go,I didn't try it
class MyTextFieldHolder {
final controller = TextEditingController();
final focusNode = FocusNode();
Widget builder(context) {
return TextField(
focusNode: focusNode,
controller: controller,
);
}
dispose(){
controller.dispose();
focusNode.dispose();
}
}
class MyForm extends StatefulWidget {
final List<dynamic> fields;
const MyForm({Key key, this.fields}) : super(key: key);
#override
_MyFormState createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
List<MyTextFieldHolder> _holders = [];
#override
void initState() {
widget.fields.forEach((element) {
_holders.add(MyTextFieldHolder());
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Form(
child: ListView.builder(
itemCount: _holders.length,
itemBuilder: (context, i) {
return _holders[i].builder(context);
},
),
);
}
bool _goToNextEmptyField(){
for(final holder in _holders){
if(holder.controller.text.isEmpty){
holder.focusNode.requestFocus();
return true;
}
}
return false;
}
}
Related
I am trying to clear a the TextField whenever user presses a space and then it should be cleared and get the focus again for next word. I want to type the content word by word. I am using ValueListenableBuilder for managing the state. Below code can re-produce the issue.
Issue:- When I press space on physical keyboard it looses the physical keyboard focus. Works fine with phone's virtual keyboard. But my user requirement is to use physical keyboard connected to the host mobile phone.
void main() {
runApp(const TestScreen());
}
class TestScreen extends StatefulWidget {
const TestScreen({Key? key}) : super(key: key);
#override
State<TestScreen> createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
final ValueNotifier<String> _notifier = ValueNotifier("");
final FocusNode _fnode = FocusNode();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("Text Demo")),
body: ValueListenableBuilder<String>(
valueListenable: _notifier,
builder: (context, value, child) {
// print("textfield is being built.");
return TextField(
focusNode: _fnode,
textAlign: TextAlign.center,
autofocus: true,
cursorColor: Colors.blue,
decoration: const InputDecoration(),
controller: TextEditingController(text: value),
maxLines: 1,
keyboardType: TextInputType.multiline,
onChanged: (text) {
if (text.contains(" ")) {
_notifier.value = "";
_notifier.notifyListeners();
_fnode.requestFocus();
}
},
);
}),
),
);
}
}
I am working on Flutter speech to text. The scenery is user can give their comment using voice. While they give input for comment by their voice they may edit the comment field and enter some text(given by their voice) into any position. Like, I give input using my voice and want to enter some words on the first or middle or any place where I think I've to add some words for completing my comments.
I am using speech_to_text: ^5.2.0. Following are the code sample. Here, I just came to display the text into TextFormField. The value remove when I re-enter my voice. But what I need the value have to be in TextFormField and I can edit it any position of the given word that I think I've to input here.
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String text = 'Hi';
bool isListening = false;
final _textEditingController = TextEditingController();
late stt.SpeechToText _speech;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SafeArea(
child: Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
TextFormField(
controller: _textEditingController,
),
Text(text),
],
)
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: AvatarGlow(
animate: isListening,
endRadius: 150,
glowColor: Theme.of(context).primaryColor,
child: FloatingActionButton(
onPressed: toggleRecording,
child: Icon(isLintening ? Icons.mic : Icons.mic_none),
),
),
),
));
}
Future toggleRecording() => SpechApi.toggleRecording(
onResult: (text) => setState(() {
this.text = text;
_textEditingController.text = text;
}),
onListening: (isLintening) {
setState(() => this.isListening = isListening);
if(!isListening) {
Future.delayed(Duration(seconds: 1), () {
// Utils.scanText(text);
_textEditingController.text = text;
});
}
});
void onListen() async {
bool available = await _speech.initialize(
onStatus: (val) => print('onStatus: $val'),
onError: (val) => print('onError: $val'));
if (!isListening) {
if (available) {
setState(() {
isLintening = false;
_speech.listen(
onResult: (val) => setState(() {
_textEditingController.text = text;
}),
);
});
}
} else {
setState(() {
isLintening = false;
_speech.stop();
});
}
}
}
I have two textfields that accept number inputs. I want to calculate the sum of the two textfields while the user input the numbers in the textfields and in realtime show the results in a third textfield. This is what I have tried so far.
void _calculation() {
setState((){
_total = int.parse(_quantityController.text) * int.parse(feedPrice.text);
},
);
print(_total);
}
And show the result in the third textfield
TextField(
readOnly: true,
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: _total.toString(),
),
),
I pass the total as a string to Textfield hint field. What am I missing or what am I doing wrong?
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class _YourPageState extends State<YourPage> {
int _age1 = 0, _age2 = 0, _totalAge = 0;
final firstNumber = TextEditingController();
final secondNumber = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
children: <Widget>[
TextField(
controller: firstNumber,
textInputAction: TextInputAction.next,
keyboardType: TextInputType.number,
onSubmitted: (String value) {},
),
TextField(
controller: secondNumber,
textInputAction: TextInputAction.next,
keyboardType: TextInputType.number,
onSubmitted: (String value) {},
),
Text(
'Sum is: ${firstNumber.text + secondNumber.text}',
textAlign: TextAlign.center,
style: const TextStyle(fontWeight: FontWeight.bold),
)
],
),
),
);
}
}
you are doing wrong at setting hintText .Because hint Text that suggests what sort of input the field accepts.
you should set text to text field instead of hinttext like this
text:_total.toString()
Demo Widget
class ApplicantsX extends StatefulWidget {
const ApplicantsX({Key? key}) : super(key: key);
#override
State<ApplicantsX> createState() => _ApplicantsXState();
}
class _ApplicantsXState extends State<ApplicantsX> {
double a = 0;
double b = 0;
final TextEditingController controller = TextEditingController();
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
TextField(onChanged: (value) {
final x = double.tryParse(value);
setState(() {
a = x ?? 0; // handle null and String
controller.text = (a + b).toStringAsFixed(2);
});
}),
TextField(onChanged: (value) {
final x = double.tryParse(value);
setState(() {
b = x ?? 0;
controller.text = (a + b).toStringAsFixed(2);
});
}),
TextField(
controller: controller,
readOnly: true,
)
],
);
}
}
After reset all textfield values and result when i enter a new number in any textfield that number is added to previous result so, can u plz give the code for previous result not considered.
I created two checkboxes but after clicking on one of them both are marked, as in the picture below, could someone help me solve this problem?
only one can be marked,
my code:
class _LanguageSelectorState extends State<LanguageSelector> {
static final List<String> languagesList = application.supportedLanguages;
static final List<String> languageCodesList =
application.supportedLanguagesCodes;
final Map<dynamic, dynamic> languagesMap = {
languagesList[0]: languageCodesList[0],
languagesList[1]: languageCodesList[1],
};
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
iconTheme: IconThemeData(color: Colors.black),
title: Text(AppTranslations.of(context).text("settings_language"), style: TextStyle(color: Colors.black, letterSpacing: 1)),
elevation: 0.0,
centerTitle: true,
bottom: PreferredSize(child: Container(color: Colors.black, height: 0.1), preferredSize: Size.fromHeight(0.1),),
),
body: _buildLanguagesList()
);
}
String selectedLanguage = '';
_buildLanguagesList() {
return ListView.builder(
itemCount: languagesList.length,
itemBuilder: (context, index) {
return _buildLanguageItem(languagesList[index]);
},
);
}
bool _value = false;
_buildLanguageItem(String language) {
return CheckboxListTile(
title: Text(language),
value: _value,
onChanged: (value) {
setState(() {
_value = value;
application.onLocaleChanged(Locale(languagesMap[language]));
});
},
controlAffinity: ListTileControlAffinity.trailing,
);
}
}
thanks for any help :)
////////////////////////////////////////////////////////////////////
Take a look at this example.. Hope that will answer your question how to use checkboxes in listView
List<Map<String, dynamic>> languagesList = [
{'value': false},
{'value': false}
];
ListView.builder(
itemCount: languagesList.length,
itemBuilder: (context, index) {
return CheckboxListTile(
title: Text(languagesList[index]['value'].toString()),
value: languagesList[index]['value'],
onChanged: (value) {
setState(() {
languagesList[index]['value'] = value;
});
},
controlAffinity: ListTileControlAffinity.trailing,
);
}),
The reason your approach didn't work was because you have assigned one variable to all your checkboxes so no wander your checkboxes were updated together
Because all the widgets created by the ListView has the same value _value, if one of the widget gets checked, the value for all of the widgets change as the all depend on the same variable.
Here is a demonstration of how you can do it. it may contain errors.
import 'package:flutter/material.dart';
class LanguageItem extends StatefulWidget {
Key key;
bool isSelected = false;
YOURCLASS application;
String language;
LanguageItem({#required language, #required this.application, this.key
}):super(key:key);
#override
_LanguageItemState createState() => _LanguageItemState();
}
class _LanguageItemState extends State<LanguageItem> {
#override
Widget build(BuildContext context) {
return CheckboxListTile(
title: Text(widget.language),
value: widget.isSelected,
onChanged: (value) {
setState(() {
widget.isSelected = value;
widget.application.onLocaleChanged(Locale(languagesMap[language]));
});
},
controlAffinity: ListTileControlAffinity.trailing,
);
}
}
In my app, I do have a list, on which I have implemented the long press selection of this post of Raouf Rahiche. When the selection is enabled I do have a different appbar, that has an IconButton on it, that should disable the selection. But I do not know how to do that.
Till now it is not working the way it should. The behaviour is displayed in the video below.
The longpress-selection is a StatefulWidget:
class _SelectableItems extends State<SelectableItems> {
bool isSelected = false;
GoogleMaterialColors googleMaterialColors = new GoogleMaterialColors();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return new GestureDetector(
onLongPress: () {
setState(() {
isSelected = !isSelected;
});
widget.callback();
},
onTap: () {
setState(() {
isSelected = !isSelected;
});
if (widget.longPressEnabled) {
widget.callback();
} else {
Navigator.push(
context,
MaterialPageRoute(builder: (context)=>RecipeDetails(widget.name))
);
}
},
child: ListTile(
leading: CircleAvatar(
child: (isSelected
? Icon(
Icons.check,
color: Colors.white,
)
: (widget.image != "no image"
? Container(
width: 40.0,
height: 40.0,
decoration: new BoxDecoration(
image: new DecorationImage(
colorFilter: ColorFilter.mode(Colors.black.withOpacity(0.2), BlendMode.darken),
image: AssetImage(widget.image),
fit: BoxFit.cover,
),
borderRadius: new BorderRadius.all(new Radius.circular(50.0)),
),
)
: Text(
widget.name[0].toUpperCase(),
style: TextStyle(
color: Colors.white,
fontSize: 21.0,
fontWeight: FontWeight.w400
),
)
)
),
backgroundColor: (isSelected
? googleMaterialColors.primaryColor()
: widget.color.withOpacity(1.00)
)
),
title: Padding(
padding: EdgeInsets.only(top: 25.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
widget.title
],
),
),
),
);
}
}
I am calling this widget inside a SideHeaderListView like this:
bool longPressFlag = false;
List<String> indexList = new List();
//other code
return SideHeaderListView(
hasSameHeader: (int a, int b){
return snapshot.data[a].name[0] == snapshot.data[b].name[0];
},
itemCount: snapshot.data.length,
headerBuilder: (BuildContext context, int index){
return new Padding(
padding: EdgeInsets.only(top: 30.0, left: 20.0, right: 25.0),
child: Container(
width: 10.0,
child: Text(
snapshot.data[index].name[0].toUpperCase(),
style: TextStyle(
color: googleMaterialColors.primaryColor().withGreen(120),
fontFamily: "Google-Sans",
fontSize: 15.0,
fontWeight: FontWeight.w600
),
),
),
);
},
itemExtend: 70.0,
itemBuilder: (BuildContext context, int index){
Color usedColor = convertColor.convertToColor(snapshot.data[index].backgroundColor);
String image = snapshot.data[index].image;
return SelectableItems(
color: usedColor,
name: snapshot.data[index].name,
title: (searchController.text.isEmpty
? Text(snapshot.data[index].name)
: recipeName(searchCondition, snapshot.data[index].name)
),
index: index,
image: image,
longPressEnabled: longPressFlag,
//isSelected: selectedFlag,
callback: () {
if (indexList.contains(snapshot.data[index].name)) {
indexList.remove(snapshot.data[index].name);
} else {
indexList.add(snapshot.data[index].name);
}
longPress();
},
);
},
);
void longPress() {
setState(() {
if (indexList.length == 0) {
longPressFlag = false;
} else {
longPressFlag = true;
}
});
}
I hope somebody would be able to solve my problem. Thanks in advance.
The first thing is that you should add each item a key in constructor like this :
MyItem({Key key}): super(key: key);
Why a key ?
A key allow you to identify your widget correctly.
See in doc :
A new widget will only be used to update an existing element if its
key is the same as the key of the current widget associated with the
element.
Create a GlobalKey (a GLobal key extends Key)
For each item to access the widget from, create a global key.
From the doc :
A key that is unique across the entire app. Global keys uniquely
identify elements. Global keys provide access to other objects that
are associated with elements, such as the a [BuildContext] and, for
[StatefulWidget]s, a [State].
Add in the code the creation of a global key for each item (in your SelectableItem for you) :
...
var key = new GlobalKey<SelectableItem >();
this.items.put(position, key);
return new SelectableItem(key: key,...);
Items is a map where you can save position and Global Key.
Now when you want to select a View from the parent just access the globalKey from the map of items and access the widget to do what you want.(update, uncheck, etc...)
Edit : exemple :
class SideHeaderListView {
Map<int, GlobalKey<_SelectableItems>> map = new Map();
create() {
for (int i = 0; i< 10; i++) {
var key = new GlobalKey<_SelectableItems>();
var item = new SelectableItems(key: key);
map.putIfAbsent(i, () => key);
}
}
redrawItem(int i) {
var widget = this.map[i].currentState;
widget.redraw();
}
}
class SelectableItems extends StatefulWidget {
SelectableItems({key: Key}) : super(key: key);
#override
State<StatefulWidget> createState() {
return new _SelectableItems();
}
}
class _SelectableItems extends State<SelectableItems> {
#override
Widget build(BuildContext context) {
return new Text("test");
}
redraw() {
setState(() {
});
}
}
You have commented part of code - //isSelected: selectedFlag,
I think, you have to add this field to your widget
class SelectableItems extands StatefulWidget {
SelectableItems({this.isSelected = false});
final bool isSelected;
...
class _SelectableItems extends State<SelectableItems> {
bool isSelected;
#override
void initState() {
isSelected = widget.isSelected ?? false;
super.initState();
}
....
And when you're creating list of items:
return SelectableItems(
...
isSelected: indexList.contains(snapshot.data[index].name)
I think this could work