Why does the text disappear from the text field after clicking Done in flutter?

Viewed 78

I have a text field where the user enters text. But I ran into a problem when I enter text and press the Done button - the text disappears from the text field as if it never existed. But when the field is active and I enter characters, everything is visible. Why does the text disappear from the text field after clicking the Done button?

    class CommentWidget extends StatelessWidget {
  final Function(String) onChanged;
  final String? textForm;
  final bool isHelpPage;
  CommentWidget({Key? key, required this.onChanged, required this.textForm, required this.isHelpPage})
      : super(key: key);

  final TextEditingController controller = TextEditingController();
  String value = '';

  @override
  Widget build(BuildContext context) {
    final Size size = MediaQuery.of(context).size;
    controller.text = textForm ?? '';

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        isHelpPage
        ? const SizedBox()
        : const ResizedText(
          'Comments',
          textStyle: constants.Styles.smallTextStyleWhite,
        ),
        SizedBox(
          height: UiSize.getHeight(15, size),
        ),
        Container(
          height: UiSize.getHeight(isHelpPage ? 290 : 150, size),
          width: UiSize.getWidth(360, size),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(8),
            border: Border.all(
              color: constants.Colors.purpleMain,
              width: 0.5,
            ),
          ),
          child: Padding(
            padding: EdgeInsets.only(
              top: UiSize.getHeight(16, size),
              left: UiSize.getWidth(18, size),
              right: UiSize.getWidth(18, size),
              bottom: UiSize.getHeight(16, size),
            ),
            child: TextField(
              maxLines: isHelpPage ? 10 :4,
              maxLength: isHelpPage ? 1500 : 450,
              controller: controller,
              style: constants.Styles.smallerBookTextStyleWhiteOpacity,
              textInputAction: TextInputAction.done,
              onChanged: (text) {
                onChanged(text);
                value = text;
              },
              decoration: InputDecoration(
                border: InputBorder.none,
                helperStyle: constants.Styles.smallerBookTextStyleWhiteOpacity,
                hintStyle: constants.Styles.smallerBookTextStyleWhiteOpacity,
                hintText: 'Here you can describe the problem in more detail',
              ),
            ),
          ),
        ),
      ],
    );
  }
}

body

class ReportBody extends StatelessWidget {
  final bool isHelpPage;
  ReportBody({Key? key, required this.isHelpPage}) : super(key: key);

  final TextEditingController controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    String? comment;
    final Size size = MediaQuery.of(context).size;

    return Container(
      height: size.height,
      width: size.width,
      decoration: const BoxDecoration(
        image: DecorationImage(
          image: AssetImage('assets/images/background/main_background.png'),
          fit: BoxFit.cover,
        ),
      ),
      child: _child(size, comment),
    );
  }

  Widget _child(Size size, String? comment) =>
      BlocBuilder<ReportCubit, ReportState>(
        builder: (context, state) {
          final ReportCubit cubit = BlocProvider.of<ReportCubit>(context);
          if (state is ReportInitial) {
            return _part(cubit, context, size, state, comment);
          }

          return Container();
        },
      );

  Widget _part(
    ReportCubit cubit,
    BuildContext context,
    Size size,
    ReportInitial state,
    String? comment,
  ) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const SizedBox(
          height: 135,
        ),
        Padding(
          padding: const EdgeInsets.only(left: 30, right: 30),
          child: BackstepWidget(
            text: 'Report a problem',
            onBackPressed: () => Routemaster.of(context).pop(),
          ),
        ),
        state.isPhotoEnabled && state.reports[0]
            ? Expanded(
                child: SingleChildScrollView(
                  physics: const BouncingScrollPhysics(),
                  child: _reportBody(cubit, context, size, state, comment),
                ),
              )
            : Expanded(child: _reportBody(cubit, context, size, state, comment))
      ],
    );
  }

  Widget _reportBody(
    ReportCubit cubit,
    BuildContext context,
    Size size,
    ReportInitial state,
    String? comment,
  ) {
    return isHelpPage
    ? SingleChildScrollView(
      physics: const BouncingScrollPhysics(),
      child: ConstrainedBox(
        constraints: BoxConstraints(
          maxHeight: isHelpPage && state.isPhotoEnabled
              ? size.height * 0.98
              : size.height * .77,
        ),
        child: _reportWidget(cubit, context, size, state, comment),
      ),
    )
    : _reportWidget(cubit, context, size, state, comment);
  }

  Widget _reportWidget(
    ReportCubit cubit,
    BuildContext context,
    Size size,
    ReportInitial state,
    String? comment,
  ) {
    return Column(
      children: [
        if (state.reports[0] || isHelpPage)
          Padding(
            padding: EdgeInsets.only(
              top: isHelpPage ? 0 : UiSize.getHeight(48, size),
              bottom: UiSize.getHeight(30, size),
              right: UiSize.getWidth(28, size),
              left: UiSize.getWidth(28, size),
            ),
            child: CommentWidget(
              textForm: comment,
              onChanged: (text) {
                state.comment = text;
                comment = text;
                print(comment);
              },
              isHelpPage: isHelpPage,
            ),
          ),
2 Answers

My guess is that you are rebuilding the textfield in the parent widget by maybe calling setState(...).

controller.text = textForm ?? ''; then wipes your text because you are resetting the text every time you are building the widget.

Solution: Make the widget a StatefulWidget and let it cache the value of the TextField.

This could look something like this

class TextfieldWithCache extends StatefulWidget {
  const TextfieldWithCache(this.initialValue, this.onUpdate);
  
  final String initialValue;
  final Function(String) onUpdate;

  @override
  State<TextfieldWithCache> createState() => TextfieldWithCacheState();
}

class TextfieldWithCacheState extends State<TextfieldWithCache> {
  
  // if not used in this widget, the value can be deleted as well
  String _cachedValue;
  
  Widget build(BuildContext context) {
    return TextFormField(
      initialValue: widget.initialValue,
      onChanged: (String value) {
        widget.onUpdate(value);
        _cachedValue = value;
      },
    );
  }
}

Note: If you don't use the cached value in the TextfieldWithCache then you can remove it as well.

The core lies in using a StatefulWidget and getting rid of all helper methods involved in widget creation.

Try changing the ReportBody to Statefull Widget and declare the text editing controller as and intialize it in the initState(),

late final TextEditingController controller;

@override
  void initState() {
    controller = TextEditingController();
    super.initState();
  }

Then pass this controller to CommentWidget as a named parameter and use this controller to TextFormField.

Hope it works.

Related