Reputation: 3651
I have the following widgets where each widget exists in their own file.
(There is alot more going on in each file. I have condensed it to keep it minimal to only see what's needed for this query).
I wish to capture values passed into a TextFormField within one widget and print out those values in another widget.
There is no visual state changes going on thus trying not to store these values via Provider which I felt would be silly for this usecase.
Thus the query is on how to pass down the value captured in a TextEditingController for each widget's instance and pass it down to another widget?
To reiterate, the following 3 classes, they exist in their own Dart file.
I was initially sticking with stateless widget for all 3 but from what I read up, advice is to user a stateful widget where TextEditingController is involved.
Thus the MyField widget is stateful.
MyField Widget - This is where the value is expected to get stored to controller based on what's been typed in.
class MyField extends StatefulWidget {
final String title;
final TextEditingController controller;
const MyField({this.controller, this.title});
@override
_MyFieldState createState() => _MyFieldState();
}
class _MyFieldState extends State<MyField> {
@override
Widget build(BuildContext context) {
return TextFormField(
controller: widget.controller,
);
}
}
MyForm Widget - This takes in 2 instances of above widget, each having its own controller.
This widget helps pass on the text values down to the MyButton widget.
class MyForm extends StatelessWidget {
final formKey = GlobalKey<FormState>();
final nameController = TextEditingController();
final passController = TextEditingController();
@override
Widget build(BuildContext context) {
return Form(
key: formKey,
child: Stack(
children: [
MyField(
title: 'name',
controller: nameController,
),
MyField(
title: 'pass',
controller: passController,
),
MyButton(
name: nameController.text,
pass: passController.text,
formKey: formKey)
],
),
);
}
}
MyButton Widget - This widget captures those text values and tries to print out the values and it currently comes out empty.
class MyButton extends StatelessWidget {
final formKey;
final String name;
final String pass;
const MyButton({Key key, this.formKey, this.name, this.pass}) : super(key: key);
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
// I want to be able to retrieve the text via the controllers for the 2 text fields.
// currently these values are empty which is the issue.
print('name: $name pass: $pass');
},
);
}
}
Upvotes: 0
Views: 1384
Reputation: 237
You can store the value in an object file and get or edit its value from any other file in the project.. for example in file named user.dart :
class user {
static String name;
static String pass;
}
then at any other place import the file and set or get its values as you want:
user.name = nameController.text
user.pass = passController.text
print('name: ' + user.name + 'pass: ' + user.pass);
if your problem is that you want to show the text before the button is clicked, I think you might make your widget stateful and then you can use change event in the Text field:
onChanged: (value) {
setState(() {
user.name = nameController.text;
});}
Upvotes: 1
Reputation: 3218
please change MyForm
class to below Class...
How It Works ?
your Button Should rebuild after each change on TextFormField so I used a Stream builder and a Stream
class MyForm extends StatelessWidget {
final formKey = GlobalKey<FormState>();
final nameController = TextEditingController();
final passController = TextEditingController();
StreamController<int> sc = StreamController<int>();
@override
Widget build(BuildContext context) {
return Form(
key: formKey,
onChanged: () {
sc.add(1);
},
child: Column(
children: [
MyField(
title: 'name',
controller: nameController,
),
MyField(
title: 'pass',
controller: passController,
),
StreamBuilder<int>(
stream: sc.stream,
builder: (context, snapshot) {
return MyButton(
name: nameController.text,
pass: passController.text,
formKey: formKey);
}
)
],
),
);
}
}
Upvotes: 0
Reputation: 4741
Well if you are not using the user typed texts to update the UI state of MyButton
widget you don't even need it you can just access the controllers texts in MyForm
widget.
class MyForm extends StatelessWidget {
final formKey = GlobalKey<FormState>();
final nameController = TextEditingController();
final passController = TextEditingController();
@override
Widget build(BuildContext context) {
return Form(
key: formKey,
child: Column(
children: [
MyField(
title: 'name',
controller: nameController,
),
MyField(
title: 'pass',
controller: passController,
),
RaisedButton(
onPressed() {
print("${nameController.text}");
print("${passController.text}");
}
),
],
),
);
}
}
But if you want to update MyButton
widget on the fly while the user is typing a text so MyForm
widgets needs to be Statefull
and must rebuild in every user type event.
//NOTE: Assuming `MyForm` is a Statefull widget
final nameController = TextEditingController();
final passController = TextEditingController();
@override
void initState() {
// listening the textfield.
nameController.addListener(_controllerListener);
passController.addListener(_controllerListener);
super.initState();
}
void _controllerListener(){
if(mounted)
setState((){});
}
@override
Widget build(BuildContext context) {
return Form(
key: formKey,
child: Stack(
children: [
MyField(
title: 'name',
controller: nameController,
),
MyField(
title: 'pass',
controller: passController,
),
MyButton(
name: nameController.text,
pass: passController.text,
onPressed: () {
print("${nameController.text} - ${passController.text}");
})
],
),
);
}
}
class MyButton extends StatelessWidget {
final String name;
final String pass;
final VoidCallback onPressed;
const MyButton({Key key, this.onPressed, this.name, this.pass}) : super(key: key);
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: this.onPressed,
// updating UI on type event.
child: Text('$name and $pass'),
);
}
}
Upvotes: 0
Reputation: 7492
Here is my try.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _buildBody(),
floatingActionButton: FloatingActionButton(
onPressed: () {},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
Widget _buildBody() {
return MyForm();
}
}
class MyButton extends StatelessWidget {
final formKey;
final String label;
const MyButton({Key key, this.formKey, this.label}) : super(key: key);
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
// I want to be able to retrieve the text via the controllers for the 2 text fields.
// currently these values are empty which is the issue.
print(
'name: ${formKey.currentWidget.child.children[0].controller.text} ');
print(
'pass: ${formKey.currentWidget.child.children[1].controller.text} ');
},
child: Text(label),
);
}
}
class MyForm extends StatelessWidget {
final formKey = GlobalKey<FormState>();
final nameController = TextEditingController();
final passController = TextEditingController();
@override
Widget build(BuildContext context) {
return Form(
key: formKey,
child: Column(
children: [
MyField(
title: 'name',
controller: nameController,
),
MyField(
title: 'pass',
controller: passController,
),
MyButton(label: 'Button', formKey: formKey)
],
),
);
}
}
class MyField extends StatefulWidget {
final String title;
final TextEditingController controller;
const MyField({this.controller, this.title});
@override
_MyFieldState createState() => _MyFieldState();
}
class _MyFieldState extends State<MyField> {
@override
Widget build(BuildContext context) {
return TextFormField(
controller: widget.controller,
);
}
}
Upvotes: 0