I want the Hint Text to remain even when Text is entered in the TextField:
But if I give TextField something like "hintText:" in simple way, Hint Text disappears when TextField is entered:
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.) :
II. Obviously Prefix Widget can't help here :
Any answers are welcome, thanks.
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:
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!