Home > Net >  Access data from custom widget created on different class in flutter
Access data from custom widget created on different class in flutter

Time:09-17

I new to Flutter and i was trying to find a solution for the below issue for several hours. I have searched and every solution provided does not work form me.

I have page where one of the widgets is the autocomplete text input. I have created this autocomplete widget on different class. I have added this widget as StatefulBuilder within my main widget. it is working fine however, i am not able to access its value so I can store it with other fields.

My code look like

class ItemDetails extends StatefulWidget {
  const ItemDetails({Key? key}) : super(key: key);

  static const routeName = '/item_details';

  @override
  State<ItemDetails> createState() => _ItemDetails();
}

class _ItemDetails extends State<ItemDetails> {
  late TextEditingController labelController;
  late TextEditingController valueController;
  late TextEditingController notesController;

  bool _submitted = false;
  late var args;
  String _itemLabel2 = "";
  // var labelAutoComp = LabelSugg();

  @override
  void initState() {
    super.initState();
    labelController = TextEditingController();
    valueController = TextEditingController();
    notesController = TextEditingController();
  }

  @override
  void dispose() {
    labelController.dispose();
    valueController.dispose();
    notesController.dispose();
    // Hive.close();
    super.dispose();
  }

  String? _labelErrorText(context) {
    final text = labelController.value.text;
    if (text.isEmpty) {
      // return 'Can\'t be empty';
      return AppLocalizations.of(context)!.noEmpty;
    }
  }

  String? _valueErrorText(context) {
    final text = valueController.value.text;
    if (text.isEmpty) {
      // return 'Can\'t be empty';
      return AppLocalizations.of(context)!.noEmpty;
    }
  }

  @override
  Widget build(BuildContext context) {
    try {
      args = ModalRoute.of(context)!.settings.arguments as Map;
    } on Exception catch (e) {
      // print(e);
    }

    // print(args);
    return Scaffold(
        appBar: AppBar(
          title: Text(args['title']),
        ),
        body: Container(
            padding: const EdgeInsets.all(20),
            child: Column(
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.all(20),
                  child: Column(
                      // mainAxisSize: MainAxisSize.min,
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: [
                        LabelSugg(getLabelText: (String val) {
                          print(val);
                          _itemLabel2 = val;
                        }),
                        TextField(
                          autofocus: true,
                          decoration: InputDecoration(
                            labelText: AppLocalizations.of(context)!.label,
                            hintText: AppLocalizations.of(context)!.labelHint,
                            errorText:
                                _submitted ? _labelErrorText(context) : null,
                          ),
                          controller: labelController,
                          onChanged: (_) => setState(() {}),
                        ),
                        const SizedBox(height: 5),
                        TextField(
                          autofocus: false,
                          decoration: InputDecoration(
                            labelText: AppLocalizations.of(context)!.value,
                            hintText: AppLocalizations.of(context)!.valueHint,
                            errorText:
                                _submitted ? _valueErrorText(context) : null,
                          ),
                          controller: valueController,
                          keyboardType: const TextInputType.numberWithOptions(
                              decimal: true, signed: false),
                          inputFormatters: <TextInputFormatter>[
                            FilteringTextInputFormatter.allow(
                                RegExp(r"[0-9.]")),
                            TextInputFormatter.withFunction(
                                (oldValue, newValue) {
                              try {
                                final text = newValue.text;
                                if (text.isNotEmpty) double.parse(text);
                                return newValue;
                              } catch (e) {}
                              return oldValue;
                            }),
                          ], // Only numbers can be entered
                          onChanged: (_) => setState(() {}),
                        ),
                        const SizedBox(height: 5),
                        TextField(
                          autofocus: true,
                          decoration: InputDecoration(
                            labelText: AppLocalizations.of(context)!.notes,
                            hintText: AppLocalizations.of(context)!.noteHint,
                          ),
                          controller: notesController,
                          onChanged: (_) => setState(() {}),
                        ),
                      ]),

                  // ],
                ),
                Expanded(
                  child: Align(
                      alignment: FractionalOffset.bottomCenter,
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          ElevatedButton.icon(
                            onPressed: () {
                              setState(() => _submitted = true);
                              if (_labelErrorText(context) == null &&
                                  _valueErrorText(context) == null) {
                                //insert
                                var localLabel = labelController.value.text;

                                var _localValue = 0.0;
                                if (valueController.value.text != '') {
                                  _localValue =
                                      double.parse(valueController.value.text);
                                } else {
                                  _localValue = 0.0;
                                }

                                var localNotes = notesController.value.text;

                                addItemToList(
                                    localLabel, _localValue, localNotes);
                                Navigator.of(context).pop();
                                labelController.clear();
                                valueController.clear();
                                notesController.clear();
                              }
                            },
                            label: Text(AppLocalizations.of(context)!.add),
                            icon: const Icon(Icons.save, size: 18),
                          ),
                          const SizedBox(width: 10),
                          ElevatedButton.icon(
                            onPressed: () => {Navigator.pop(context)},
                            label: Text(AppLocalizations.of(context)!.cancel),
                            icon: const Icon(Icons.cancel, size: 18),
                          ),
                        ],
                      )),
                ),
                // )
              ],
            )));
  }

  void addItemToList(String localLabel, double localValue, String localNotes) {
    var _item = YearItems()..yearID = args['year'];
    _item.itemLabel = localLabel;
    _item.itemValue = localValue;
    _item.itemNote = localNotes;
    print(_itemLabel2);

    final itemsBox = ItemsBoxes.getTransactions();
    itemsBox.add(_item);
  }
}

my labelAutoComp widget code look like

class LabelSugg extends StatefulWidget {
  final ValueChanged<String> getLabelText;
  const LabelSugg({Key? key, required this.getLabelText}) : super(key: key);

  @override
  State<LabelSugg> createState() => _LabelSugg();
}

class _LabelSugg extends State<LabelSugg> {
  late TextEditingController fieldTextEditingController2;

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  getLabel() {
    return widget.getLabelText(fieldTextEditingController2.text);
  }

  @override
  Widget build(BuildContext context) {
    List<LabelsAc> labelOptions = <LabelsAc>[
      LabelsAc(label: AppLocalizations.of(context)!.labelClothes),
      LabelsAc(label: AppLocalizations.of(context)!.labelFood),
      LabelsAc(label: AppLocalizations.of(context)!.labelPerfumes),
      LabelsAc(label: AppLocalizations.of(context)!.labelCapital),
    ];

    return Autocomplete<LabelsAc>(
      optionsBuilder: (TextEditingValue textEditingValue) {
        return labelOptions
            .where((LabelsAc _label) => _label.label
                .toLowerCase()
                .startsWith(textEditingValue.text.toLowerCase()))
            .toList();
      },
      displayStringForOption: (LabelsAc option) => option.label,
      fieldViewBuilder: (BuildContext context,
          TextEditingController fieldTextEditingController,
          // fieldTextEditingController,
          FocusNode fieldFocusNode,
          VoidCallback onFieldSubmitted) {
        return TextField(
            controller: fieldTextEditingController,
            focusNode: fieldFocusNode,
            style: const TextStyle(fontWeight: FontWeight.bold),
            // onChanged: getLabel(),
            onChanged: (String val) {
              fieldTextEditingController2 = fieldTextEditingController;
              getLabel();
            });
      },
      onSelected: (LabelsAc selection) {
        fieldTextEditingController2 =
            TextEditingController(text: selection.label);
        getLabel();
      },
      optionsViewBuilder: (BuildContext context,
          AutocompleteOnSelected<LabelsAc> onSelected,
          Iterable<LabelsAc> options) {
        return Align(
          alignment: Alignment.topLeft,
          child: Material(
            child: Container(
              // width: 350,
              // color: Colors.cyan,
              child: ListView.builder(
                padding: const EdgeInsets.all(10.0),
                itemCount: options.length,
                itemBuilder: (BuildContext context, int index) {
                  final LabelsAc option = options.elementAt(index);

                  return GestureDetector(
                    onTap: () {
                      onSelected(option);
                    },
                    child: ListTile(
                      title: Text(option.label,
                          style: const TextStyle(color: Colors.black)),
                    ),
                  );
                },
              ),
            ),
          ),
        );
      },
    );
    //   ),
    // );
  }
}

class LabelsAc {
  LabelsAc({required this.label});
  String label;
}

CodePudding user response:

  • first is redundant when you wrap your class that extend StatefullWidget with StatefullBuilder. LabelSugg is a component Widget. you can use it like other widget.

  • benefit to separate widget with StatefullWidget class is, we can update the value inside the class without re-build the current page. which is good for performance. that's why developer recomend to separete with class insted compared to make local method.

  • as you see, when you create LabelSugg extend StatefullWidget class , we will have _LabelSugg . underscore means that: all variable only accessible on current file. thats why we can't call getLabel() or other variable from different file. its used for handle the State in 'LabelSugg` widget.


now how to pass the value from LabelSugg is by created variable outside the state. here you are:

class LabelSugg extends StatefulWidget {
  // use this to pass any changes when we use LabelSugg
  final ValueChanged<String> getLabelText; 
  const LabelSugg({Key? key, required this.getLabelText}) : super(key: key);

  @override
  State<LabelSugg> createState() => _LabelSugg();
}

then we can call the onChaged inside _LabelSugg state. because its Statefull widget, we can acces by : widget.getLabelText()

class _LabelSugg extends State<LabelSugg> {
  late TextEditingController fieldTextEditingController;
.....

getLabel() {
    return widget.getLabelText(fieldTextEditingController.text);
  }

then in other class we call LabelSugg like common widget

import 'package:../labelsug.dart';

class ItemDetails extends StatefulWidget {
.....

return Scaffold(
    appBar: AppBar(
      title: Text(args['title']),
    ),
    body: Container(
        padding: const EdgeInsets.all(20),
        child: Column(
          children: <Widget>[
             // now use it like a widget 
             LabelSug(
                 getLabelText: (String val){
                     print(val);
                  }

:)

  • Related