fluttertexttextfieldhintsuffix

Flutter | How to show a Hint Text when a value is entered


I want the Hint Text to remain even when Text is entered in the TextField: I want to do the same

But if I give TextField something like "hintText:" in simple way, Hint Text disappears when TextField is entered: Current status.

What should I do so that the hintText doesn't disappear even when a value is entered in the TextField?

. . . I tried the following:

I. I tried using the suffix widget. But it appears from the end of the TextField. (If it was possible to make the suffix widget appear after the text, I think the problem would have been solved.) : Ignore the malfunction

II. Obviously Prefix Widget can't help here : with Prefix Widget

Any answers are welcome, thanks.


Solution

  • Answer:

    Then I looked into it and found that Telegram Messenger uses a more complex approach with custom rendering on a Canvas and animates the hint text by adjusting its opacity and position. You can check the implementation here.

    But I used a simpler solution with a Stack and a monospace font: enter image description here

    Here's a simple example:

    import 'package:flutter/material.dart';
    import 'package:mask_text_input_formatter/mask_text_input_formatter.dart';
    
    void main() => runApp(const MyApp());
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            body: Center(
              child: Padding(
                padding: const EdgeInsets.all(18),
                child: CustomMaskedTextField(
                  mask: '--- --- ----',
                  style: const TextStyle(fontSize: 24, letterSpacing: 1.0),
                  decoration: const InputDecoration(border: OutlineInputBorder()),
                  onChanged: (value) => print('Current value: $value'),
                ),
              ),
            ),
          ),
        );
      }
    }
    
    class CustomMaskedTextField extends StatefulWidget {
      final String mask;
      final TextStyle? style;
      final InputDecoration? decoration;
      final ValueChanged<String>? onChanged;
    
      const CustomMaskedTextField({
        super.key,
        required this.mask,
        this.style,
        this.decoration,
        this.onChanged,
      });
    
      @override
      _CustomMaskedTextFieldState createState() => _CustomMaskedTextFieldState();
    }
    
    class _CustomMaskedTextFieldState extends State<CustomMaskedTextField> {
      final _inputController = TextEditingController();
      final _hintController = TextEditingController();
      late final MaskTextInputFormatter maskFormatter;
    
      @override
      void initState() {
        super.initState();
        maskFormatter = MaskTextInputFormatter(mask: widget.mask, filter: {"#": RegExp(r'[0-9]')});
        _inputController.addListener(_updateHintText);
        _updateHintText();
      }
    
      void _updateHintText() {
        final text = _inputController.text;
        final remainingMask = widget.mask.substring(text.length);
        _hintController.text = text + remainingMask;
        widget.onChanged?.call(text);
      }
    
      @override
      void dispose() {
        _inputController.removeListener(_updateHintText);
        _inputController.dispose();
        _hintController.dispose();
        super.dispose();
      }
    
      TextStyle get _baseStyle => widget.style?.copyWith(fontFamily: 'Courier') ?? const TextStyle(fontFamily: 'Courier');
    
      @override
      Widget build(BuildContext context) {
        return Stack(
          children: [
            AbsorbPointer(
              child: TextField(
                controller: _hintController,
                enabled: false,
                style: _baseStyle.copyWith(color: Colors.grey),
                decoration: widget.decoration,
              ),
            ),
            TextField(
              controller: _inputController,
              inputFormatters: [maskFormatter],
              style: _baseStyle.copyWith(color: Colors.black),
              decoration: widget.decoration,
            ),
          ],
        );
      }
    }
    

    Feel free to customize it for your use case!