Skip to main content

TextFieldAppi

An advanced text input widget with built-in validation, styling, and enhanced user experience features.

Overview

TextFieldAppi is a comprehensive text input component that extends Flutter's TextFormField with additional features like advanced validation, custom styling, and better accessibility. It's designed to handle all your text input needs with a consistent API.

Features

  • Built-in Validation - Multiple validation rules
  • 🎨 Custom Styling - Extensive theming options
  • 🔍 Search Integration - Built-in search capabilities
  • Accessibility - Screen reader and keyboard support
  • 📱 Responsive - Adapts to different screen sizes
  • 🌙 Theme Support - Light and dark mode ready

Basic Usage

TextFieldAppi(
label: 'Email',
hintText: 'Enter your email address',
onChanged: (value) {
print('Email: $value');
},
)

Properties

PropertyTypeDefaultDescription
labelString?nullField label text
hintTextString?nullPlaceholder text
initialValueString?nullInitial field value
controllerTextEditingController?nullText controller
validatorString? Function(String?)?nullValidation function
onChangedValueChanged<String>?nullValue change callback
onSubmittedValueChanged<String>?nullSubmit callback
keyboardTypeTextInputType?nullKeyboard type
textInputActionTextInputAction?nullInput action
obscureTextboolfalseHide text (for passwords)
enabledbooltrueEnable/disable field
readOnlyboolfalseRead-only mode
maxLinesint?1Maximum lines
maxLengthint?nullMaximum character length
borderRadiusdouble?nullBorder radius
borderColorColor?nullBorder color
focusedBorderColorColor?nullFocused border color
fillColorColor?nullBackground fill color
textStyleTextStyle?nullText styling
labelStyleTextStyle?nullLabel styling
hintStyleTextStyle?nullHint text styling

Examples

Basic Text Field

TextFieldAppi(
label: 'Full Name',
hintText: 'Enter your full name',
onChanged: (value) {
setState(() {
fullName = value;
});
},
)

Email Field with Validation

TextFieldAppi(
label: 'Email Address',
hintText: 'example@domain.com',
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
return 'Please enter a valid email';
}
return null;
},
onChanged: (value) {
setState(() {
email = value;
});
},
)

Password Field

class PasswordField extends StatefulWidget {

_PasswordFieldState createState() => _PasswordFieldState();
}

class _PasswordFieldState extends State<PasswordField> {
bool _obscureText = true;


Widget build(BuildContext context) {
return TextFieldAppi(
label: 'Password',
hintText: 'Enter your password',
obscureText: _obscureText,
keyboardType: TextInputType.visiblePassword,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
if (value.length < 8) {
return 'Password must be at least 8 characters';
}
return null;
},
suffixIcon: IconButton(
icon: Icon(_obscureText ? Icons.visibility : Icons.visibility_off),
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
);
}
}

Styled Text Field

TextFieldAppi(
label: 'Styled Input',
hintText: 'Custom styled field',
borderRadius: 16,
borderColor: Colors.blue[300],
focusedBorderColor: Colors.blue,
fillColor: Colors.blue[50],
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
labelStyle: TextStyle(
color: Colors.blue[700],
fontWeight: FontWeight.w600,
),
)

Multiline Text Field

TextFieldAppi(
label: 'Description',
hintText: 'Enter a detailed description...',
maxLines: 4,
maxLength: 500,
keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.newline,
validator: (value) {
if (value != null && value.length > 500) {
return 'Description must be less than 500 characters';
}
return null;
},
)

Validation Examples

Required Field

TextFieldAppi(
label: 'Required Field',
validator: (value) {
if (value == null || value.isEmpty) {
return 'This field is required';
}
return null;
},
)

Phone Number Validation

TextFieldAppi(
label: 'Phone Number',
hintText: '+1 (555) 123-4567',
keyboardType: TextInputType.phone,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Phone number is required';
}
if (!RegExp(r'^\+?[\d\s\-\(\)]+$').hasMatch(value)) {
return 'Please enter a valid phone number';
}
return null;
},
)

Number Validation

TextFieldAppi(
label: 'Age',
hintText: 'Enter your age',
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Age is required';
}
final age = int.tryParse(value);
if (age == null) {
return 'Please enter a valid number';
}
if (age < 18 || age > 120) {
return 'Age must be between 18 and 120';
}
return null;
},
)

Custom Validation

TextFieldAppi(
label: 'Username',
hintText: 'Choose a username',
validator: (value) {
if (value == null || value.isEmpty) {
return 'Username is required';
}
if (value.length < 3) {
return 'Username must be at least 3 characters';
}
if (!RegExp(r'^[a-zA-Z0-9_]+$').hasMatch(value)) {
return 'Username can only contain letters, numbers, and underscores';
}
return null;
},
)

Advanced Features

Search Field

class SearchField extends StatefulWidget {
final Function(String) onSearch;

const SearchField({Key? key, required this.onSearch}) : super(key: key);


_SearchFieldState createState() => _SearchFieldState();
}

class _SearchFieldState extends State<SearchField> {
final _controller = TextEditingController();
Timer? _debounce;


void dispose() {
_debounce?.cancel();
_controller.dispose();
super.dispose();
}

void _onSearchChanged(String query) {
if (_debounce?.isActive ?? false) _debounce!.cancel();
_debounce = Timer(const Duration(milliseconds: 500), () {
widget.onSearch(query);
});
}


Widget build(BuildContext context) {
return TextFieldAppi(
controller: _controller,
label: 'Search',
hintText: 'Search for items...',
prefixIcon: Icon(Icons.search),
suffixIcon: _controller.text.isNotEmpty
? IconButton(
icon: Icon(Icons.clear),
onPressed: () {
_controller.clear();
widget.onSearch('');
},
)
: null,
onChanged: _onSearchChanged,
);
}
}

Form Integration

class UserForm extends StatefulWidget {

_UserFormState createState() => _UserFormState();
}

class _UserFormState extends State<UserForm> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
final _emailController = TextEditingController();


Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFieldAppi(
controller: _nameController,
label: 'Full Name',
validator: (value) {
if (value == null || value.isEmpty) {
return 'Name is required';
}
return null;
},
),
SizedBox(height: 16),
TextFieldAppi(
controller: _emailController,
label: 'Email',
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
if (!value.contains('@')) {
return 'Please enter a valid email';
}
return null;
},
),
SizedBox(height: 24),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// Process form data
print('Name: ${_nameController.text}');
print('Email: ${_emailController.text}');
}
},
child: Text('Submit'),
),
],
),
);
}
}

Theming

Custom Theme

class CustomTextFieldTheme {
static InputDecorationTheme get theme => InputDecorationTheme(
filled: true,
fillColor: Colors.grey[100],
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey[300]!),
),
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),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.red),
),
labelStyle: TextStyle(
color: Colors.grey[700],
fontWeight: FontWeight.w500,
),
hintStyle: TextStyle(
color: Colors.grey[500],
),
);
}

Dark Theme Support

TextFieldAppi(
label: 'Dark Theme Field',
fillColor: Theme.of(context).brightness == Brightness.dark
? Colors.grey[800]
: Colors.grey[100],
borderColor: Theme.of(context).brightness == Brightness.dark
? Colors.grey[600]
: Colors.grey[300],
textStyle: TextStyle(
color: Theme.of(context).textTheme.bodyLarge?.color,
),
)

Best Practices

1. Use Proper Validation

// Good: Comprehensive validation
TextFieldAppi(
validator: (value) {
if (value == null || value.isEmpty) return 'Required';
if (value.length < 3) return 'Too short';
if (!RegExp(r'^[a-zA-Z\s]+$').hasMatch(value)) return 'Invalid format';
return null;
},
)

2. Provide Clear Labels and Hints

// Good: Clear and descriptive
TextFieldAppi(
label: 'Email Address',
hintText: 'example@domain.com',
)

// Avoid: Vague labels
TextFieldAppi(
label: 'Input',
hintText: 'Enter text',
)

3. Use Appropriate Keyboard Types

// Good: Specific keyboard types
TextFieldAppi(
keyboardType: TextInputType.emailAddress, // For email
)

TextFieldAppi(
keyboardType: TextInputType.phone, // For phone numbers
)

4. Handle State Properly

// Good: Proper state management
class MyForm extends StatefulWidget {

_MyFormState createState() => _MyFormState();
}

class _MyFormState extends State<MyForm> {
final _controller = TextEditingController();


void dispose() {
_controller.dispose();
super.dispose();
}


Widget build(BuildContext context) {
return TextFieldAppi(controller: _controller);
}
}

Common Use Cases

1. Login Form

Column(
children: [
TextFieldAppi(
label: 'Email',
keyboardType: TextInputType.emailAddress,
validator: emailValidator,
),
SizedBox(height: 16),
TextFieldAppi(
label: 'Password',
obscureText: true,
validator: passwordValidator,
),
],
)

2. Profile Form

Column(
children: [
TextFieldAppi(
label: 'First Name',
validator: requiredValidator,
),
SizedBox(height: 16),
TextFieldAppi(
label: 'Last Name',
validator: requiredValidator,
),
SizedBox(height: 16),
TextFieldAppi(
label: 'Bio',
maxLines: 3,
maxLength: 200,
),
],
)

3. Search Interface

TextFieldAppi(
label: 'Search Products',
prefixIcon: Icon(Icons.search),
onChanged: (query) => searchProducts(query),
)

Ready for advanced text input? Check out SearchableTextFieldAppi for search capabilities!