flutter, How to copy subranges of multi-step input - flutter

When copying text from TextField, how should I copy the subrange in case of multi-step input (composing mode)?
For example, the following code
import 'package:flutter/material.dart';
class SampleWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: HomeWidget());
}
}
class HomeWidget extends StatelessWidget {
TextEditingController _controller1 = TextEditingController();
FocusNode _focusNode1 = FocusNode();
TextEditingController _controller2 = TextEditingController();
FocusNode _focusNode2 = FocusNode();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
children: [
TextField(
focusNode: _focusNode1,
controller: _controller1,
),
TextField(
focusNode: _focusNode2,
controller: _controller2,
),
RaisedButton(
child: Text("copy text"),
onPressed: () {
if(_focusNode1.hasFocus) {
_controller2.text = _controller1.text;
_focusNode1.unfocus();
_focusNode2.requestFocus();
} else {
_controller1.text = _controller2.text;
_focusNode2.unfocus();
_focusNode1.requestFocus();
}
}),
],
),
),
),
);
}
}
void main() {
runApp(SampleWidget());
}
It is possible to copy text in English "abcd" in TextField.
but in case of multi-step input(composing mode), copying text is not possible.
Korean is a combinatorial language, that means characters such as '가', '구', and '거' can be written ordinarily 'ㄱ' + ‘ㅏ’, ‘ㄱ’+ ‘ㅜ’ and ‘ㄱ’ + ‘ㅓ’.
However, the code moves to the next character range just after 'ㄱ'.
(I want the code to keep until ‘ㄱ’ is combined with ‘ㅏ’)
How can I solve this problem?

Related

onChanged method with setState of TextField does not update the result?

I am experimenting with flutter and I have a very simple code as follows:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
static double d = 0;
static TextEditingController editingController = TextEditingController();
#override
void initState() {
editingController.addListener(() {
setState(() {});
});
super.initState();
}
#override
void dispose() {
editingController.dispose();
super.dispose();
}
Calc calc = Calc(d: d, e: editingController);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(25),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextField(
keyboardType: TextInputType.number,
onChanged: (value) {
setState(() {
if (value.isNotEmpty) {
d = double.parse(value);
} else if (value.isEmpty) {
d = 0;
}
});
},
inputFormatters: [
FilteringTextInputFormatter.allow(
RegExp(r'(^(\d{1,})\.?(\d{0,2}))'),
),
],
),
TextField(
keyboardType: TextInputType.number,
controller: editingController,
inputFormatters: [
FilteringTextInputFormatter.allow(
RegExp(r'(^(\d{1,})\.?(\d{0,2}))'),
),
],
),
Text(
'First Text Field Value + 2 = \n${calc.dString()}',
style: TextStyle(fontSize: 30, color: Colors.purpleAccent),
),
Text(
calc.eString(),
style: TextStyle(fontSize: 30, color: Colors.deepOrangeAccent),
),
],
),
),
),
);
}
}
class Calc {
final double d;
final TextEditingController e;
Calc({this.d, this.e});
String dString() {
double result = d + 2;
return result.toStringAsFixed(0);
}
String eString() {
return e.text;
}
}
As we can see I am passing both the text fields' values into another class for some math and getting the results. These results are displayed using the Text widgets.
For the 1st TextField, I have used onChange method, and for the 2nd TextField, I have used TextEditingController.
I get return value for 2nd TextField from the Calc class, but not for the 1st TextField!
I think I am missing something basic and I did not find any solution so far. Can you please help me what am I missing here.
1st of all, you are creating just a single object of Calc,
yes as you can see your 2nd textField update perfectly because it's using TextEditingController but for the 1st one, it just call once and become 2 because of dString(), while on 1st run d becomes 0 passed on Calc.
if you want to use Calc to update text, you can simply put it inside build method like this , i dont suggest it, you can use callBackMethod to handle this or use another TextEditingController.
Hope you get it now
#override
Widget build(BuildContext context) {
Calc calc = Calc(d: d, e: editingController);
return Scaffold(
body: Center(
Your Calc Object is not being affect by setState() call. To run be able to get value of the calcobject, run it in you onChanged() function.

Can't able to focus on newly added text field flutter

import 'package:flutter/material.dart';
class Demo {
int no;
String value;
Demo({this.value, this.no});
}
class Control {
TextEditingController controller;
FocusNode node;
Control({this.controller, this.node});
}
class DemoPage extends StatefulWidget {
static const routeName = '/Demo';
DemoPage({Key key}) : super(key: key);
#override
_DemoPageState createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
List<Demo> txtfield;
List<Control> control;
#override
void initState() {
txtfield = [];
control = [];
// no = 0;
add();
super.initState();
}
int no;
void add() {
no = (no ?? 0) + 1;
setState(() {});
txtfield.add(Demo(no: no));
control.add(Control(
controller: TextEditingController(),
node: FocusNode(),
));
// no = no +1;
}
#override
Widget build(BuildContext context) {
// print(txtfield[0].no);
// FocusScope.of(context).requestFocus(control[control.length - 1].node);
return Scaffold(
appBar: AppBar(),
body: Center(
child: Card(
child: Container(
child: Column(
children: txtfield
.map((f) => TextField(
controller: control[f.no - 1].controller,
focusNode: control[f.no - 1].node,
autofocus: true,
))
.toList(),
),
width: 400,
padding: EdgeInsets.all(20),
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
add();
print(no);
FocusScope.of(context).requestFocus(control[control.length - 1].node);
},
),
);
}
}
I used above code. but I can't able to focus on a newly added text field.
when I check for that newly added text field has focus, it shows true, but I can't able to write anything in that text field.
I don't know what is an error in that code.
I search for this solution for more than 4 days. but I can't able to find solution.
At the onPressed of your floatingActionButton change this line:
FocusScope.of(context).requestFocus(control[control.length - 1].node);
with this
control[control.length - 1].node.requestFocus();

Show TextField value on same screen on button click

This is my screen with TextField and Button. When someone clicks on show button, I want it to show the name below the button as shown in below picture.
Code below:
class Demo extends StatefulWidget {
#override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
final name = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Row(
children: [
Text(
'Name'
),
TextField(
controller: name,
)
],
),
RaisedButton(
onPressed: (){
},
child: Text('Show'),
)
],
),
),
);
}
}
This can be a basic example for your question. The UI is not exactly what you've shown above
class Question extends StatefulWidget {
Question({Key key}) : super(key: key);
#override
_QuestionState createState() => _QuestionState();
}
class _QuestionState extends State<Question> {
String text = '';
bool shouldDisplay = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Center(
child: TextField(
onChanged: (value) {
setState(() {
text = value;
});
},
),
),
FlatButton(onPressed: () {
setState(() {
shouldDisplay = !shouldDisplay;
});
}, child: Text('Submit')),
shouldDisplay ? Text(text) : Spacer()
],
);
}
}
Hope this helps.
Initialize two variables. One for a TextEditingController and one for the text value.
TextEditingController controller = TextEditingController();
String display = '';
Give your TextField a controller.
TextField(controller:controller);
In your button, set onPressed to change display text to the controller text.
FlatButton(
child: Text("Show"),
onPressed()=> setState((){display = controller.text;});
),
Then where you want to show the text, set the text string to display.
Text(display);
I would advice you to learn the basics of flutter first before asking these kinds of questions. This can be simply achieved through using TextEditingController and setState(). Simply define a controller for your TextField and then call setState() when your button is pressed. Note that you have to be on a StatefulWidget since calling setState() rebuilds the UI.
Create a TextEditingController and string above the #override Widget build:
String displayName="";
final myController = TextEditingController();
Create a TextField and add assign the controller to it:
TextField(
controller: myController,
);
Call setState() on button pressed:
MaterialButton(
child: Text("Show"),
onPressed: (){
setState(() {
displayName=myController.text;
});
})
Display it using a Text widget:
Text(displayName);
Good Luck!
You can find out how to use TextEditingController here: https://flutter.dev/docs/cookbook/forms/retrieve-input
More about widgets here: https://www.youtube.com/playlist?list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG

How can I correctly focus a Textfield that is conditionally created based on the focus node's hasFocus value?

With my current code the TextField becomes focused, but the cursor and keyboard aren't triggered (requires a second tap). I believe this is because the TextField doesn't exist when the focus node is initially focused, but I'm struggling to find a solution.
Here is a simple recreation of the problem based on a Cookbook recipe:
class MyCustomForm extends StatefulWidget {
#override
_MyCustomFormState createState() => _MyCustomFormState();
}
class _MyCustomFormState extends State<MyCustomForm> {
FocusNode myFocusNode;
bool _editingField2 = false;
#override
void initState() {
super.initState();
myFocusNode = FocusNode();
myFocusNode.addListener(_focusListener);
}
#override
void dispose() {
myFocusNode.dispose();
super.dispose();
}
// Set _editingField2 to true when focusNode has focus.
_focusListener() {
if (myFocusNode.hasFocus) {
setState(() {
_editingField2 = true;
});
} else {
setState(() {
_editingField2 = false;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Text Field Focus'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
// The first text field is focused on as soon as the app starts.
TextField(
autofocus: true,
),
// The second text field is created when _editingField2 (after FAB press).
_editingField2
? TextField(
focusNode: myFocusNode,
)
: Text('ayy'),
],
),
),
floatingActionButton: FloatingActionButton(
// Give focus node focus on FAB press.
onPressed: () => FocusScope.of(context).requestFocus(myFocusNode),
tooltip: 'Focus Second Text Field',
child: Icon(Icons.edit),
),
);
}
}
Here is my code, with important bits commented.
class TaskListItem extends StatefulWidget {
final Task task;
TaskListItem({#required this.task});
#override
State createState() => _TaskListItemState();
}
class _TaskListItemState extends State<TaskListItem> {
bool _isEditing;
FocusNode _focusNode;
final TextEditingController _textEditingController = TextEditingController();
#override
initState() {
super.initState();
_isEditing = false;
_textEditingController.text = widget.task.text;
_textEditingController.addListener(_handleTextFieldUpdate);
_focusNode = FocusNode(debugLabel: 'TaskListItem');
_focusNode.addListener(_handleFocusChange);
}
#override
void dispose() {
_focusNode.removeListener(_handleFocusChange);
_focusNode.dispose();
_textEditingController.dispose();
super.dispose();
}
_handleTextFieldUpdate() {
Provider.of<TaskListModel>(context, listen: false)
.updateTaskText(widget.task, _textEditingController.text);
}
// Update state to determine if Text or TextField widget is created in build().
_handleFocusChange() {
if (_focusNode.hasFocus) {
setState(() {
_isEditing = true;
});
} else {
setState(() {
_isEditing = false;
});
}
}
Widget _buildTitle() {
return Row(
children: <Widget>[
Expanded(
// Create either TextField or Text based on _isEditing value.
child: _isEditing && !widget.task.isComplete
? TextField(
focusNode: _focusNode,
controller: _textEditingController,
)
: Text(
widget.task.text,
style: widget.task.isComplete
? TextStyle(decoration: TextDecoration.lineThrough)
: null,
),
),
],
);
}
#override
Widget build(BuildContext context) {
return ListTile(
leading: Checkbox(
value: widget.task.isComplete,
//Dismiss focus when box is checked
onChanged: (bool checked) {
_focusNode.unfocus();
Provider.of<TaskListModel>(context, listen: false)
.toggleComplete(widget.task);
},
),
title: _buildTitle(),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => Provider.of<TaskListModel>(context, listen: false)
.deleteTask(widget.task),
),
onTap: () {
// I'm requesting focus here, but the Textfield doesn't exist yet?
FocusScope.of(context).requestFocus(_focusNode);
print('tapped');
},
);
}
}
What you have to do is change focus inside build, you're trying to change focus before the screen has done rebuilding that widget already. try this please, using your own code.
I'm not sure if you needed to really listen to that focus change or if you only wanted to accomplish the focus change after enabling the widget, if you do want to listen for the focus change let me know.
class MyCustomForm extends StatefulWidget {
#override
_MyCustomFormState createState() => _MyCustomFormState();
}
class _MyCustomFormState extends State<MyCustomForm> {
FocusNode myFocusNode = FocusNode();
bool _editingField2 = false;
#override
void dispose() {
myFocusNode?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
//here you do the focus request
if (_editingField2) {
FocusScope.of(context).requestFocus(myFocusNode);
}
return Scaffold(
appBar: AppBar(
title: Text('Text Field Focus'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
// The first text field is focused on as soon as the app starts.
TextField(
autofocus: true,
),
// The second text field is created when _editingField2 (after FAB press).
_editingField2
? TextField(
focusNode: myFocusNode,
)
: Text('ayy'),
],
),
),
floatingActionButton: FloatingActionButton(
// Give focus node focus on FAB press.
onPressed: () {
setState(() {
_editingField2 = true;
});
},
tooltip: 'Focus Second Text Field',
child: Icon(Icons.edit),
),
);
}
}

Flutter switch widget not updating

I have created a stateless widget that has a Flutter switch widget I implement this widget in the parent and pass in the required parameters but it won't change value when I press the switch.
I thought it might have been due to the fact that the child widget wasn't stateful but that made no difference.
Here is a brief example of code from my two widget files
class SettingsButton extends StatelessWidget {
final String text;
final bool initalValue;
final void Function(bool) onOffCallback;
SettingsButton({
this.text,
this.initalValue = false,
this.onOffCallback,
});
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
child: SubHeading(text),
),
Switch(
onChanged: isOnOff ? onOffCallback : null,
activeColor: Theme.of(context).accentColor,
value: initalValue,
)
]);
class _SettingsState extends State<Settings> {
bool test = true;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
body: ListView(
children: [
Column(
children: [
SettingsButton(
text: "Test",
onOffCallback: (test) => setState(() {
print("Called");
test = !test;
}),
initalValue: test,
),
],
)
],
),
);
}
You're setting the test variable recieved in the callback, rather than that defined in the _SettingsState class. What you should have is this for the callback:
onOffCallback: (newTest) => setState(() {
print("$newTest");
test = newTest;
// or (it shouldn't matter)
test = !test;
print("$test");
}),