Custom Right-Aligned TextField in Flutter
Flutter’s built-in TextField works well for most use cases, but when using right alignment, I noticed a problem: when a user enters a space at the end of the text, the space does not appear immediately. It only shows up after the next character is typed. This creates a poor input experience, especially in fields where right alignment is required.
To solve this issue, I created a custom widget that behaves like a right-aligned input box but avoids the space-display bug.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../res/styles.dart';
class RightEditTextFieldWidget extends StatefulWidget {
RightEditTextFieldWidget(
{Key key,
@required this.textFieldName,
@required this.hintText,
@required this.maxLength,
@required this.alignWidth,
@required this.isPassword,
@required this.controller,
this.inputFormatters})
: super(key: key);
String textFieldName;
String hintText;
int maxLength;
double alignWidth;
List<TextInputFormatter> inputFormatters;
bool isPassword;
TextEditingController controller;
@override
_RightEditTextFieldWidgetState createState() =>
_RightEditTextFieldWidgetState(textFieldName, hintText, maxLength,
inputFormatters, alignWidth, isPassword, controller);
}
class _RightEditTextFieldWidgetState extends State<RightEditTextFieldWidget> {
// 外部变量
String _textfieldName;
String _hintText;
int _maxLength;
List<TextInputFormatter> _inputFormatters;
double _alignWidth;
bool _isPassword;
bool _isTextVisable;
TextEditingController _controller;
// 内部变量
FocusNode _focusNode;
String _index;
String _hintTextCopy;
_RightEditTextFieldWidgetState(
this._textfieldName,
this._hintText,
this._maxLength,
this._inputFormatters,
this._alignWidth,
this._isPassword,
this._controller);
@override
void initState() {
super.initState();
_hintTextCopy = _hintText;
_isTextVisable = !_isPassword;
_focusNode = FocusNode();
// _controller = TextEditingController();
}
@override
void dispose() {
_focusNode.dispose();
// _controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: _alignWidth),
Text(
_textfieldName,
style: TextStyles.text_b3_16,
),
Expanded(
child: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(_focusNode);
SystemChannels.textInput.invokeMethod<void>('TextInput.show');
},
child: Stack(
alignment: Alignment.centerRight,
children: <Widget>[
Container(
// color: Colors.red,
// height: double.infinity,
// width: double.infinity,
child: Text(_hintText,
style: TextStyles.text_66_16,
// textDirection: TextDirection(
// c
// )
),
),
IntrinsicWidth(
child: TextField(
style: TextStyles.text_b3_16,
focusNode: _focusNode,
// controller: _controller,
maxLength: _maxLength,
inputFormatters: _inputFormatters,
maxLines: 1,
readOnly: false,
autofocus: false,
obscureText: _isTextVisable,
decoration: InputDecoration(
// hintText: _hintText,
// hintStyle: TextStyles.text_66_16,
border: OutlineInputBorder(
// borderSide: BorderSide.none
),
counterText: "",
contentPadding: EdgeInsets.all(0),
),
onChanged: (v) {
setState(() {
_index = v;
if (v != null && v != "") {
_hintText = "";
} else {
_hintText = _hintTextCopy;
}
});
},
),
),
],
),
),
),
Visibility(
visible: _isPassword,
child: GestureDetector(
onTap: () {
setState(() {
_isTextVisable = !_isTextVisable;
});
},
child: Container(
width: 24,
height: 24,
child: Image.asset(
_isTextVisable
? "assets/images/comm/admin_pwd_no_see.png"
: "assets/images/comm/admin_pwd_see.png",
fit: BoxFit.fill)),
),
),
SizedBox(width: _alignWidth),
],
)
// )
;
}
@override
void didUpdateWidget(Widget oldWidget) {
print("didUpdateWidget");
}