I'm trying to insert an initial value to the flutter_quill flutter text editor with no success. Link to package https://pub.dev/packages/flutter_quill . From a normal TextField I would have done something like below using the controller.
class _FooState extends State<Foo> {
TextEditingController _controller;
#override
void initState() {
super.initState();
_controller = new TextEditingController(text: 'Initial value');
}
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new TextField(
// The TextField is first built, the controller has some initial text,
// which the TextField shows. As the user edits, the text property of
// the controller is updated.
controller: _controller,
),
],
);
}
}
I appreciate your help.
Related
My goal is to get the selected text of the TextFormField, but each time the button is pressed, the TextFormField loses its focus, and the print in the console shows only a selection between -1 and -1.
It did work a few weeks ago, did the behavior change in the latest release? I am on the stable Flutter channel. (Flutter Channel stable, 2.5.0)
This is my test example:
import 'package:flutter/material.dart';
class Play extends StatefulWidget {
const Play({Key? key}) : super(key: key);
#override
_PlayState createState() => _PlayState();
}
class _PlayState extends State<Play> {
TextEditingController _controller = TextEditingController();
FocusNode _node = FocusNode();
void _onPressed() {
TextSelection selection = _controller.selection;
print("selection.start: ${selection.start}");
print("selection.end: ${selection.end}");
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
focusNode: _node,
controller: _controller,
),
ElevatedButton(
onPressed: _onPressed,
child: Text("do something"),
),
],
),
);
}
}
Since it works fine on mobile, I'd say it's a bug in Flutter, but fear not! Welcome to the world of workarounds and temporary solutionsTM; replace your entire _onPressed method by this:
int start = -1;
int end = -1;
#override
void initState() {
super.initState();
_controller.addListener(() async {
TextSelection selection = _controller.selection;
if (selection.start > -1) start = selection.start;
if (selection.end > -1) end = selection.end;
});
}
void _onPressed() {
print(start);
print(end);
}
Let me know if something is not very clear and I'll be happy to edit the answer with further explanations.
DartPad example
The Flutter community would love you to file a bug report (if it doesn't exist already)! :D
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
If I have this:
class SomethingState extends State<Something> {
String name;
#override
Widget build(BuildContext context) {
return TextField(
onChange: (text) {
name = text
}
)
}
}
Do I have to wrap name = text in setState to trigger the build method or no because when the user types something in the TextField it already does that?
This is how I have it now and it works, but I want to make sure I understand this correctly.
The value will change without setState but will not change on the UI. To update the UI you must use setState and rebuild the widgets.
This code for the question in the comments
class Homepage extends StatelessWidget {
final controller = TextEditingController();
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Center(child: TextField(controller: controller,),),
FlatButton(child: Text("change"),onPressed: (){
controller.text = 'new text';
},)
],
);
}
}
the TextFiled class
class InputContainer extends StatefulWidget{
final _InputContainerInner input = new _InputContainerInner();
#override
State<StatefulWidget> createState() {
return input;
}
}
class _InputContainerInner extends State<InputContainer>{
TextEditingController controller = TextEditingController();
String num = '';
#override
Widget build(BuildContext context) {
return Container(
...
child: TextField(
...
controller: controller,
...
)
)
}
i use it in another file
Widget build(BuildContext context) {
InputContainer passWord = new InputContainer();
return Scaffold(
body: new Column(
children: <Widget>[
passWord,
new MaterialButton(
onPressed: () {
print(passWord.input);
print(passWord.input.num);
}
)
]
)
}
i click the button , but got nothing of this TextFiled, print result is
flutter: InputContainerInner#9818c(lifecycle state: created, no widget, not mounted)
flutter:
maybe it is the matter of lifecycle, but i have made it in the widget , what happen ?
Maybe U can save it in some variable e.g.
String password = controller.text
then call it from other class by creating object or something
I think you shouldn't create a new instance of InputContainer in the other widget class, it's wrong.
The ideal way is to use inheritedwidget or scopedmodel,provider but that is more complicated
You can try creating a globalkey inside the other class and access the InputContainerInner text controller from there
Note: Change your _InputContainerInner to InputContainerInner
//put this inside your class widget, where u declare all the variables
final GlobalKey<InputContainerInner> containerKey;
Widget build(BuildContext context) {
return Scaffold(
body: new Column(
children: <Widget>[
//import the inputContainer class
InputContainer(),
new MaterialButton(
onPressed: () {
print(widget.containerKey.currentState.controller.text);
print(widget.containerKey.currentState.controller.input.num);
}
)
]
)
}
full example
TextEditingController completeAddressController = new TextEditingController();
TextFormField(
controller: completeAddressController,
),
get value of TextFormField
String text = completeAddressController.text.toString(),
I have a default page with two tabs, using TabBar and TabController. All is working fine, except when I enter the page, the second tab's build method is only called when I click to change to the second tab. The problem is that when I enter the page I want both tabs to be already created (a.k.a their build methods executed). Any thoughts?
Illustration code:
//This widget is used inside a Scaffold
class TabsPage extends StatefulWidget {
#override
State<StatefulWidget> createState() => new TabsPageState();
}
class TabsPageState extends State<TabsPage> with TickerProviderStateMixin {
List<Tab> _tabs;
List<Widget> _pages;
TabController _controller;
#override
void initState() {
super.initState();
_tabs = [
new Tab(text: 'TabOne'),
new Tab(text: 'TabTwo')
];
_pages = [
//Just normal stateful widgets
new TabOne(),
new TabTwo()
];
_controller = new TabController(length: _tabs.length, vsync: this);
}
#override
Widget build(BuildContext context) {
return new Padding(
padding: EdgeInsets.all(10.0),
child: new Column(
children: <Widget>[
new TabBar(
controller: _controller,
tabs: _tabs
),
new SizedBox.fromSize(
size: const Size.fromHeight(540.0),
child: new TabBarView(
controller: _controller,
children: _pages
),
)
],
),
);
}
}
I had a similar issue, I'm using Tabs with Forms and I wanted to validate all of them, my solution was to switch to the second tab programmatically, It doesn't make sense to not visit a tab with a form that requires validation.
So given a global key for each form:
final _formKey = GlobalKey<FormState>();
The reason I wanted all tabs to be built was to use:
_formKey.currentState.validate()
So my fix is
if (_formKey.currentState == null) { //The tab was never visited, force it
_tabController.animateTo(1);
return;
}
I was able to achieve this.
In my case, I had 2 tabs and I wanted to preload the other tab.
So what I did was, I initialised the tab controller in initState like this:
tabController = TabController(
length: 2,
vsync: this,
initialIndex: 1,
);
I set the initialIndex to the other tabs index, and then set the index back in after the build was done like this:
WidgetsBinding.instance.addPostFrameCallback((_) {
tabController.index = 0;
});
But this may only work if you have 2 tabs