am developing a Flutter application and using a CustomTextFormField
widget to handle user input. However, I am facing an issue with the validator message appearing inside the border of the field, and I want to position it outside the border.
Here is a snippet of the CustomTextFormField
widget that I created:
import 'package:flutter/material.dart';
class CustomTextFormField extends StatefulWidget {
final String label;
final String? Function(String?)? validator;
final void Function(String?)? onSaved;
final TextEditingController? controller;
final String? type;
final TextInputType keyboardType;
final IconData? suffixIcon;
final IconData? prefixIcon;
final bool isPassword; // Menambahkan parameter untuk kolom password
final bool giveBorder;
final bool isRequired;
const CustomTextFormField({
super.key,
required this.label,
required this.validator,
this.controller,
required this.type,
required this.keyboardType,
required this.onSaved,
this.suffixIcon,
this.prefixIcon,
this.isPassword = false, // Default tidak menggunakan password
this.giveBorder = false,
this.isRequired = false,
});
@override
State<CustomTextFormField> createState() {
return _CustomTextFormFieldState();
}
}
class _CustomTextFormFieldState extends State<CustomTextFormField> {
bool _obscureText = true; // Mengatur visibilitas password
@override
Widget build(BuildContext context) {
Widget content = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
widget.isRequired
? Row(
children: [
Text(
widget.label,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black54,
),
textAlign: TextAlign.start,
),
const Text(
' *',
style: TextStyle(color: Colors.red),
)
],
)
: Text(
widget.label,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black54,
),
textAlign: TextAlign.start,
),
widget.giveBorder
? Container(
margin: const EdgeInsets.symmetric(vertical: 8),
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
border:
Border.all(color: Colors.black12), // Border warna abu-abu
),
child: TextField(
controller: widget.controller,
keyboardType: widget.keyboardType,
obscureText: widget.isPassword ? _obscureText : false,
decoration: InputDecoration(
border: InputBorder.none, // Hilangkan border default
hintText: widget.label, // Placeholder
hintStyle: const TextStyle(color: Colors.grey),
prefixIcon: widget.prefixIcon != null
? Icon(widget.prefixIcon)
: null,
suffixIcon:
widget.isPassword // Menambahkan ikon untuk password
? IconButton(
icon: Icon(
_obscureText
? Icons
.visibility_off // Tampilkan ikon 'lihat'
: Icons
.visibility, // Tampilkan ikon 'sembunyikan'
),
onPressed: () {
setState(() {
_obscureText =
!_obscureText; // Toggle visibilitas
});
},
)
: widget.suffixIcon != null
? Icon(widget.suffixIcon)
: null,
),
),
)
: TextField(
controller: widget.controller,
keyboardType: widget.keyboardType,
obscureText: widget.isPassword ? _obscureText : false,
decoration: InputDecoration(
hintText: widget.label, // Placeholder
hintStyle: const TextStyle(color: Colors.grey),
prefixIcon: widget.prefixIcon != null
? Icon(widget.prefixIcon)
: null,
suffixIcon: widget
.isPassword // Menambahkan ikon untuk password
? IconButton(
icon: Icon(
_obscureText
? Icons.visibility_off // Tampilkan ikon 'lihat'
: Icons
.visibility, // Tampilkan ikon 'sembunyikan'
),
onPressed: () {
setState(() {
_obscureText =
!_obscureText; // Toggle visibilitas
});
},
)
: widget.suffixIcon != null
? Icon(widget.suffixIcon)
: null,
),
),
],
);
if (widget.type == 'form') {
content = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
widget.isRequired
? Row(
children: [
Text(
widget.label,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black54,
),
textAlign: TextAlign.start,
),
const Text(
' *',
style: TextStyle(color: Colors.red),
)
],
)
: Text(
widget.label,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black54,
),
textAlign: TextAlign.start,
),
Container(
alignment: Alignment.center,
margin: const EdgeInsets.symmetric(vertical: 8),
padding: EdgeInsets.only(
right: 4,
left: widget.prefixIcon != null ? 4 : 16,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
border: widget.giveBorder
? Border.all(color: Colors.grey)
: Border.all(color: Colors.black12), // Border warna abu-abu
),
child: TextFormField(
controller: widget.controller,
keyboardType: widget.keyboardType,
obscureText: widget.isPassword ? _obscureText : false,
decoration: InputDecoration(
border: InputBorder.none, // Hilangkan border default
hintText: widget.label, // Placeholder
hintStyle: const TextStyle(color: Colors.grey),
prefixIcon:
widget.prefixIcon != null ? Icon(widget.prefixIcon) : null,
contentPadding: widget.isPassword
? const EdgeInsets.symmetric(vertical: 12)
: null,
suffixIcon: widget.isPassword // Menambahkan ikon untuk password
? IconButton(
icon: Icon(
_obscureText
? Icons.visibility_off
: Icons.visibility,
),
padding: EdgeInsets.zero,
iconSize: 20,
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
)
: widget.suffixIcon != null
? Icon(widget.suffixIcon)
: null,
),
onSaved: widget.onSaved,
validator: widget.validator,
),
),
],
);
}
return content;
}
}
However, when validation fails, the error message appears inside the border of the TextFormField, as seen in the image below:
I would like to know how to move the validator message to appear outside the border, so it does not interfere with the input appearance. Is there a recommended way to achieve this? Thank you in advance
I want the validator on outside of border
Instead of using border: InputBorder.none in the decoration of your TextFormField, try this for a more defined border:
border: OutlineInputBorder( borderRadius: BorderRadius.circular(6), borderSide: const BorderSide( width: 1, color: borderColor, ), ),