kar
kar

Reputation: 3651

How to pass text value between two widgets not existing within same file

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

Answers (4)

Heila Al-Mogren
Heila Al-Mogren

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

Sajjad
Sajjad

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

Marcos Boaventura
Marcos Boaventura

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

KuKu
KuKu

Reputation: 7492

Here is my try.

  • Need to change from 'Stack' to 'Column' in MyForm class.
  • getting value from TextFormField using 'formKey' (Although I used a formkey, but I hope to control value from outside of 'MyForm' than Getting a value by using 'formkey')
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

Related Questions