How to Style TextField in Flutter: Complete Guide
•10 min read
<div style="text-align: center;">
<img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjIwMCIgdmlld0JveD0iMCAwIDMwMCAyMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPCEtLSBUZXh0RmllbGQgU3R5bGluZyBleGFtcGxlIC0tPgogIDxyZWN0IHdpZHRoPSIzMDAiIGhlaWdodD0iMjAwIiBmaWxsPSIjRkZGIiBzdHJva2U9IiMwMDAiLz4KICA8dGV4dCB4PSIxNTAiIHk9IjEwMCIgZm9udC1mYW1pbHk9IkFyaWFsIiBmb250LXNpemU9IjEyIiBmaWxsPSIjMjEyMTIxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5UZXh0RmllbGQgU3R5bGluZzwvdGV4dD4KPC9zdmc+" alt="TextField Styling Example" width="300" />
</div>
TextFields are essential components in any Flutter application. This guide will show you how to create beautifully styled text fields using various techniques and best practices.
Basic TextField Styling
1. Simple Styled TextField
TextField( decoration: InputDecoration( labelText: 'Username', hintText: 'Enter your username', prefixIcon: Icon(Icons.person), border: OutlineInputBorder(), filled: true, fillColor: Colors.grey[100], ), )
2. Custom Border Styling
TextField( decoration: InputDecoration( labelText: 'Email', border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide( color: Colors.blue, width: 2, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide( color: Colors.grey, width: 1, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide( color: Colors.blue, width: 2, ), ), ), )
Advanced Styling Techniques
1. Custom Input Decoration
class CustomInputDecoration extends InputDecoration { const CustomInputDecoration({ required String labelText, String? hintText, IconData? prefixIcon, }) : super( labelText: labelText, hintText: hintText, prefixIcon: prefixIcon != null ? Icon(prefixIcon) : null, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey[300]!), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.blue, width: 2), ), filled: true, fillColor: Colors.grey[50], contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), ); } // Usage TextField( decoration: CustomInputDecoration( labelText: 'Password', hintText: 'Enter your password', prefixIcon: Icons.lock, ), )
2. Animated TextField
class AnimatedTextField extends StatefulWidget { const AnimatedTextField({super.key}); @override State<AnimatedTextField> createState() => _AnimatedTextFieldState(); } class _AnimatedTextFieldState extends State<AnimatedTextField> { bool _isFocused = false; @override Widget build(BuildContext context) { return Focus( onFocusChange: (hasFocus) { setState(() { _isFocused = hasFocus; }); }, child: AnimatedContainer( duration: Duration(milliseconds: 300), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), boxShadow: _isFocused ? [ BoxShadow( color: Colors.blue.withOpacity(0.2), blurRadius: 8, offset: Offset(0, 2), ) ] : null, ), child: TextField( decoration: InputDecoration( labelText: 'Search', prefixIcon: Icon(Icons.search), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), ), ), ), ); } }
Theme-Based Styling
1. Global TextField Theme
MaterialApp( theme: ThemeData( inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: Colors.grey[100], border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.grey), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.grey), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.blue, width: 2), ), contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), ), ), home: MyHomePage(), )
2. Custom Theme Extension
class CustomTextFieldTheme extends ThemeExtension<CustomTextFieldTheme> { final Color borderColor; final Color focusedBorderColor; final double borderRadius; final EdgeInsets contentPadding; const CustomTextFieldTheme({ required this.borderColor, required this.focusedBorderColor, required this.borderRadius, required this.contentPadding, }); @override ThemeExtension<CustomTextFieldTheme> copyWith({ Color? borderColor, Color? focusedBorderColor, double? borderRadius, EdgeInsets? contentPadding, }) { return CustomTextFieldTheme( borderColor: borderColor ?? this.borderColor, focusedBorderColor: focusedBorderColor ?? this.focusedBorderColor, borderRadius: borderRadius ?? this.borderRadius, contentPadding: contentPadding ?? this.contentPadding, ); } @override ThemeExtension<CustomTextFieldTheme> lerp( ThemeExtension<CustomTextFieldTheme>? other, double t, ) { if (other is! CustomTextFieldTheme) return this; return CustomTextFieldTheme( borderColor: Color.lerp(borderColor, other.borderColor, t)!, focusedBorderColor: Color.lerp(focusedBorderColor, other.focusedBorderColor, t)!, borderRadius: lerpDouble(borderRadius, other.borderRadius, t)!, contentPadding: EdgeInsets.lerp(contentPadding, other.contentPadding, t)!, ); } } // Usage MaterialApp( theme: ThemeData( extensions: [ CustomTextFieldTheme( borderColor: Colors.grey, focusedBorderColor: Colors.blue, borderRadius: 12, contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), ), ], ), )
Specialized TextField Styles
1. Search Field
class SearchField extends StatelessWidget { const SearchField({super.key}); @override Widget build(BuildContext context) { return TextField( decoration: InputDecoration( hintText: 'Search...', prefixIcon: Icon(Icons.search), suffixIcon: IconButton( icon: Icon(Icons.clear), onPressed: () { // Clear text }, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(25), ), contentPadding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), ), ); } }
2. Password Field
class PasswordField extends StatefulWidget { const PasswordField({super.key}); @override State<PasswordField> createState() => _PasswordFieldState(); } class _PasswordFieldState extends State<PasswordField> { bool _obscureText = true; @override Widget build(BuildContext context) { return TextField( obscureText: _obscureText, decoration: InputDecoration( labelText: 'Password', prefixIcon: Icon(Icons.lock), suffixIcon: IconButton( icon: Icon(_obscureText ? Icons.visibility : Icons.visibility_off), onPressed: () { setState(() { _obscureText = !_obscureText; }); }, ), border: OutlineInputBorder(), ), ); } }
Best Practices
-
Consistency
- Use consistent styling across your app
- Follow Material Design guidelines
- Create reusable styles
-
Accessibility
- Provide clear labels
- Use appropriate contrast
- Include error messages
-
Performance
- Avoid unnecessary rebuilds
- Use const constructors
- Optimize animations
-
User Experience
- Clear visual feedback
- Proper error handling
- Intuitive interactions
Common Issues and Solutions
-
Overlapping Text
TextField( decoration: InputDecoration( contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), ), )
-
Custom Error Styling
TextField( decoration: InputDecoration( errorStyle: TextStyle(color: Colors.red), errorBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.red), ), ), )
-
Placeholder Styling
TextField( decoration: InputDecoration( hintStyle: TextStyle(color: Colors.grey), hintText: 'Enter text...', ), )
Conclusion
Styling TextFields in Flutter offers endless possibilities for creating beautiful and functional user interfaces. Remember to:
- Follow Material Design guidelines
- Ensure accessibility
- Maintain consistency
- Optimize performance
- Test thoroughly
Happy styling!