how to get the TextFiled value outside the widget - flutter

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(),

Related

Reusing a Widget with Setters

New to Dart/Flutter and unsure if this is the proper way of going about this, but I want to make a class for an ElevatedButton widget that I can use over-and-over and only need to set the text and callback (onPressed) for each instance of ElevatedButton.
For now I'm just trying to get to the point where I can make a list of Widgets (ElevatedButtons) where I set each button text, but am struggling. This is what I have:
class AElevatedButton extends StatefulWidget
{
AElevatedButton({Key? key}) : super(key:key);
#override
State<AElevatedButton> createState() => ElevatedButtonState();
}
class ElevatedButtonState extends State<AElevatedButton>
{
String buttonText = "Button";
void setText(String buttonText) {
setState(() {
this.buttonText = buttonText;
});
}
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(5),
child: ElevatedButton(
onPressed: null,
child: Text(buttonText)
)
);
}
}
In my "home page" State I have below but am not able to access the "setText" method in ElevatedButtonState, which somewhat makes sense as I'm creating a ElevatedButton object and not a ElevatedButtonState. Unsure if there is a way to get the state so I can call the method?
class _MyHomePageState extends State<MyHomePage>
{
AElevatedButton firstButton = new AElevatedButton();
AElevatedButton secondButton = new AElevatedButton();
void initiateButtons()
{
firstButton.setText("Button 1"); <---- error
secondButton.setText("Button 2"); <---- error
}
#override
Widget build(BuildContext context) {
initiateButtons();
return Scaffold(
appBar: AppBar(
title: const Text("Test Buttons")
),
body:
Column(
children: <Widget>[
firstButton,
secondButton
])
);
}
}
It seems that what you're making is more or less a wrapper of an existing widget with your own customization. This is rather common in Flutter, and actually its how a lot of material widgets are implemented, just setting up a bunch of properties with a predefined state.
In your case the correct way to achieve what you want, is to make a new class just as you did, but you don't need to create setters to change the state/attributes of your new widgets. Simply pass them in the constructors of your new class, for example:
class AElevatedButton extends StatelessWidget {
final String text;
final VoidCallback? onTap;
const AElevatedButton({required this.text,this.onTap});
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(5),
child: ElevatedButton(
onPressed: onTap,
child: Text(text)
)
);
}}
Here there is only text and onTap, but you could add more attributes as your necessities change, and to use them is just like any other flutter widget:
class _MyHomePageState extends State<MyHomePage>
{
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Test Buttons")
),
body:
Column(
children: <Widget>[
AElevatedButton(text:"Button 1",onTap:testcallback1),
AElevatedButton(text:"Button 2",onTap:testcallback2)
])
);
}
}
If I missed any point or could clarify something more, do comment.
After some experimenting, I got a solution but unsure if this is the best way:
class AElevatedButton extends StatefulWidget
{
AElevatedButton({Key? key}) : super(key:key);
String buttonText = "Default";
Function() cb = nothingfunc;
#override
State<AElevatedButton> createState() => ElevatedButtonState();
}
class ElevatedButtonState extends State<AElevatedButton>
{
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(5),
child: ElevatedButton(
onPressed: widget.cb,
child: Text(widget.buttonText)
)
);
}
}
And the homepage State:
AElevatedButton firstButton = new AElevatedButton();
AElevatedButton secondButton = new AElevatedButton();
void initiateButtons()
{
firstButton.buttonText = "Button 1";
firstButton.cb = testcallback1;
secondButton.buttonText = "Button 2";
secondButton.cb = testcallback2;
}
Some misc. functions added for testing above:
void nothingfunc()
{
}
void testcallback1()
{
print("Button 1 pressed");
}
void testcallback2()
{
print("Button 2 pressed");
}

How to transfer information from one class to another in Flutter?

I just wanted to know how to pass data from one class to another in Flutter.
Like suppose, I want to transfer the text field data that is in the first class, and then pass it onto another class (which is in another file, but same folder) to display it.
Please help...
Data is held in objects, not in classes, unless it is defined as "static". To transfer data, you can do it by methods, constructors, fields, etc..
For example:
class A {
String data;
}
class B {
String data;
static String static_data;
}
somewhere in the code you have objects:
var a = A();
var b = B();
To tranfer the data you can use the public field directly:
b.data = a.data;
or in the static case:
B.data = a.data;
In Flutter, you typically have Widget with TextField. To access the text in it, use TextEditingController. Then open the other widget with constructor passing value:
class _Widget1State extends State<Widget1> {
final text_ctrl = TextEditingController();
Widget build(BuildContext context) {
return Column(children: [
TextField(controller: text_ctrl),
ElevatedButton(onPressed: () => Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => Widget2(text_ctrl.text)));)
]);
}
}
and the Widget2 will take it like this:
class Widget2 extends StatelessWidget {
final String text;
Widget2 (this.text);
#override
Widget build(BuildContext context) {
return Text(text);
}
}
If you want to pass from one class to another you can do something like this,
Example:
Here is the first class given below which will have a TextField
class ExampleClassOne extends StatelessWidget {
final textEditingController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Column(
children: [
TextField(
controller: textEditingController,
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ExampleClassTwo(
text: textEditingController.text,
)));
},
child: Text("Enter")),
],
),
),
);
}
}
Here is the class which will receive the text value. It will receive using it's constructor.
class ExampleClassTwo extends StatelessWidget {
final String text;
const ExampleClassTwo({this.text});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Text(text),
),
);
}
}

Pass form to multiple widgets

I'm building an app with a long form in it. So I decided to seperate it into several steps.
Each step would be a widget containing formFields.
So I would have something like this:
int _currentStep = 0;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
String _firstname;
String _lastname;
String _city;
List<Widget> formGroups = [
FormGroup1(),
FormGroup2(),
FormGroup3(),
];
The form would bind to the state like this
Form(
key: _formKey,
child: formGroups[_currentStep],
),
My idea is to be able to navigate to the next widget like this
void goToNext() {
setState(() {
if (_currentStep < formGroups.length - 1) {
_currentStep += 1;
}
});
}
Firstly, is it a good practice?
How can I get the main widget to get the input from the children widgets?
For example if the FormGroup2 contains the inputfield to set the _lastname, how can I make it availaibe at the form level?
Thank you for your help.
Whenever you want parent widget to get input from the child widget you always use the NotificationListener at parent and pass Notification containing data from the child.
This technique is used by many flutter widgets like DefaultTabController receives OverscrollNotification when user swipes to the last page and is still trying to swipe.
In your use case you can pass the value notifications from the child Widgets to the Form Widget.
Here is a Demo for your reference Run on dartpad
Following is a working code demonstrating the use of this widget:
import 'package:flutter/material.dart';
main() => runApp(MaterialApp(home: MyApp()));
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _string = 'You haven\'t pressed a button yet' ;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Notification demo'),),
body: NotificationListener<IntegerNotification>(
onNotification: (IntegerNotification notification){
print(notification.value);
setState(() {
_string = 'You have pressed button ${notification.value} times.';
});
return true;
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_string),
ChildWidget(),
],
),
),
);
}
}
class ChildWidget extends StatefulWidget {
#override
_ChildWidgetState createState() => _ChildWidgetState();
}
class _ChildWidgetState extends State<ChildWidget> {
int _counter = 0;
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text('Increment'),
onPressed: () {
IntegerNotification(++_counter).dispatch(context);
},
),
);
}
}
class IntegerNotification extends Notification {
final value;
IntegerNotification(this.value);
}
I hope this helps, in case of any doubt please comment.

Does TextField onChange method trigger the build method?

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

Widget rebuild after TextField selection Flutter

I'm developping a Flutter App that needed to have a form. So when the user open the app, a Splash Screen appear before the form that have the following code :
import 'package:flutter/material.dart';
import '../model/User.dart';
import './FileManager.dart';
import './MyListPage.dart';
class UserLoader extends StatefulWidget {
#override
_UserLoaderState createState() => new _UserLoaderState();
}
class _UserLoaderState extends State<UserLoader> {
final userFileName = "user_infos.txt";
User _user;
#override
Widget build(BuildContext context) {
print("build UserLoader");
final _formKey = new GlobalKey<FormState>();
final _firstNameController = new TextEditingController();
final _lastNameController = new TextEditingController();
final _emailController = new TextEditingController();
final _phoneController = new TextEditingController();
return new Scaffold(
appBar: new AppBar(
title: new Text("Informations"),
actions: <Widget>[
new IconButton(
icon: const Icon(Icons.save),
onPressed: () {
_user = _onFormValidate(
_formKey.currentState,
_firstNameController.text,
_lastNameController.text,
_emailController.text,
_phoneController.text);
})
],
),
body: new Center(
child: new SingleChildScrollView(
child: new Form(
key: _formKey,
child: new Column(children: <Widget>[
new ListTile(
leading: const Icon(Icons.person),
title: new TextFormField(
decoration: new InputDecoration(
hintText: "Prénom",
),
keyboardType: TextInputType.text,
controller: _firstNameController,
validator: _validateName,
),
),
new ListTile(
leading: const Icon(Icons.person),
title: new TextFormField(
decoration: new InputDecoration(
hintText: "Nom",
),
keyboardType: TextInputType.text,
controller: _lastNameController,
validator: _validateName,
),
),
Etc, etc ...
However when i tap the TextField, the keyboard appear and close immediately and all the component is rebuild. So it is impossible for me to complete the form..
Can someone have a solution please? Thanks in advance !
You haven't given us the entire code for this, so I don't know what the context is.
One pitfall I myself have fallen into (and might be affecting you, as I gather from your description) is having a stateful widget nested inside another stateful widget.
For instance,
class Parent extends StatefulWidget {
#override
ParentState createState() => ParentState();
(...)
}
class ParentState extends State<Parent> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Child(),
);
}
(...)
}
class Child extends StatefulWidget {
#override
ChildState createState() => ChildState();
(...)
}
class ChildState extends State<Child> {
#override
Widget build(BuildContext context) {
return TextField(...);
}
(...)
}
The problem here is that a rebuild of Parent means that ParentState().build() is run, and a new Child instance is created, with a new ChildState object. Which resets everything.
Try not recreating ChildWidget, but instead saving it on ParentState, like so:
class Parent extends StatefulWidget {
#override
ParentState createState() => ParentState();
(...)
}
class ParentState extends State<Parent> {
Child _child;
#override
void initState() {
_child = Child();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _child,
);
}
(...)
}
// The rest remains the same
Edit: You just need to remember that, if your widget tree is a bit more complex, you may need to 1) pass a callback from the Parent to notify of state changes, and 2) not forget to also call setState() on the Child.
you just need make a new class and import that on your target class that seen problem. for example :
I usually create a class like this :
class MiddleWare
{
static MiddleWare shared = MiddleWare();
_MiddleWare(){}
String myText = "my Text";
// every variables should be here...
}
and
import "MiddleWare.dart";
class myclass extends StatefulWidget {
#override
_myclassState createState() => _myclassState();
}
class _myclassState extends State<myclass> {
#override
Widget build(BuildContext context) {
return Container(child: Text(MiddleWare.shared.myText));
}
}
that's it.
hi dont use Scaffold key i.e
Scaffold (
...
key: _scaffoldKey, //remove this
...
)
on the page and do a complete page rebuild (not hot reload), and you should be fine worked for me tho!
In my case, I have two stateful widgets, the parent and the child. I used the pushReplacement method on the parent to fix the widget reload issue when the text form field is selected in the child widget.
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => WidgetChildren(idUser:
widget.idUser)),
);
try to create a function which receives context like this
class YourPage extends StatefulWidget {
const YourPage(Key key) : super(key: key);
static Future<void> show({ BuildContext context,}) async {
await Navigator.of(context, rootNavigator: true).push(
MaterialPageRoute(
builder: (context) => YourPage()
);}
#override
_YourPageState createState() => _YourPageState();
}
......YourPage Build.....
then provide context to your page, when rebuilding it will have core context that prevents parent rebuild.
onPressed: () async {
await YourPage.show(context: context);
Move your variables (controllers and keys) from build to class-fields level.
in my case it was related to this property in Scaffold widget: 'resizeToAvoidBottomInset'
I changed it to true and problem solved.