0

Hopefully someone can help me on this. I have this scenario where my textfield has been set an error text since user click continue without choosing value. Supposedly, when i tap on the textfield, a dialog will show with a list of data that i should choose. When done choosing, the value is set to the textfield and the error text is reset to null. But currently the error text is not reset to null and it's still shown along the value that i chose. I'm thinking maybe it's because i need to rebuild MySearchList class by calling setstate. But i am not sure how to do this. Hope anyone can advise me on what is the best way to do this. Thanks in advance.

enter image description here

import 'package:flutter/material.dart';

// ignore: must_be_immutable
class MySearchList extends StatefulWidget {
  MySearchList(
      {required this.label,
      required this.controller,
      required this.dataList,
      this.errorText});

  final String label;
  final TextEditingController controller;
  final List<String> dataList;
  String? errorText;

  @override
  _MySearchListState createState() => _MySearchListState();
}

class _MySearchListState extends State<MySearchList> {
  @override
  Widget build(BuildContext context) {
    return Material(
      child: TextField(
          decoration: InputDecoration(
            labelText: widget.label,
            errorText: widget.errorText,
          ),
          readOnly: true,
          controller: widget.controller,
          onTap: () {
            showDialog(
                context: context,
                builder: (BuildContext context) {
                  return MySearchListDialog(
                    label: widget.label,
                    controller: widget.controller,
                    dataList: widget.dataList,
                  );
                });
          }),
    );
  }
}

// ignore: must_be_immutable
class MySearchListDialog extends StatefulWidget {
  MySearchListDialog(
      {required this.label,
      required this.controller,
      required this.dataList,
      this.errorText});

  final String label;
  final TextEditingController controller;
  final List<String> dataList;
  String? errorText;

  @override
  _MySearchListDialogState createState() => _MySearchListDialogState();
}

class _MySearchListDialogState extends State<MySearchListDialog> {
  TextEditingController _textController = TextEditingController();
  List<String> _mainDataList = [];
  List<String> _newDataList = [];

  @override
  void initState() {
    super.initState();
    _mainDataList = widget.dataList;
    _newDataList = List.from(_mainDataList);
  }

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

  onItemChanged(String value) {
    setState(() {
      _newDataList = _mainDataList
          .where((string) => string.toLowerCase().contains(value.toLowerCase()))
          .toList();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Dialog(
        child: Container(
      constraints: BoxConstraints(
        minHeight: 300.0,
        maxHeight: 400.0,
      ),
      child: Column(
        children: <Widget>[
          Padding(
            padding: EdgeInsets.fromLTRB(12.0, 10.0, 12.0, 0.0),
            child: MyTextField(
              hintLabel: "Search",
              controller: _textController,
              onChanged: onItemChanged,
              errorText: widget.errorText,
            ),
          ),
          Expanded(
            child: ListView(
              padding: EdgeInsets.all(12.0),
              children: _newDataList.map((data) {
                return ListTile(
                    title: Text(data),
                    onTap: () {
                      //this setstate will not rebuild class MySearchList
                      setState(() {
                        widget.errorText = null; //reset error on textfield
                        widget.controller.text = data.toString(); //set value to textfield
                        Navigator.pop(context); //close dialog
                      });
                    });
              }).toList(),
            ),
          )
        ],
      ),
    ));
  }
}
MRu
  • 1,225
  • 7
  • 20
  • 44
  • you are sending controller to the dialog and from dialog you are setting text to the controller but instead of that I think the correct way would be you send an event to the dialog like onTextSelected() and handle this in MySearchList and inside that even do the setState for setting text and for resetting error text. – Vivart May 13 '21 at 12:50
  • Why don't you use the callback mechanism explained in this [link](https://stackoverflow.com/questions/51798498/flutter-setstate-to-another-class). – RTXGamer May 13 '21 at 12:51
  • thank you for the feedback i will try to look at both solutions. thank you again – MRu May 13 '21 at 12:54

1 Answers1

0

Show your dialog like this:

_MySearchListState

onTap: () async {
  var data = await showDialog(
    context: context,
    builder: (BuildContext context) {
    return MySearchListDialog(
      label: widget.label,
      controller: widget.controller,
      dataList: widget.dataList,
     );
  });
  if(null != data){
    setState(() {
   
    });
  }
})

And close your dialog like this:

_MySearchListDialogState

onTap: () {
  //this setstate will not rebuild class MySearchList
  setState(() {
    widget.errorText = null; //reset error on textfield
    widget.controller.text = data.toString(); //set value to textfield
  });
  Navigator.of(context).pop(data); // <-- Pass data and close dialog
});
blokberg
  • 849
  • 8
  • 11