Atoms
Input Auto Complete
How to use?
Assign the lable you wanna show and pass the array from which user can choose one value.
InputAutoComplete(
label: 'Select Continent',
hints: const [
'Asia',
'Africa',
'North America',
'South America',
'Antarctica',
'Europe',
'Australia',
],
),
use formattor to parse specific kind of values only
InputAutoComplete(
label: 'Select Continent',
hints: const [
'Asia',
'Africa',
'North America',
'South America',
'Antarctica',
'Europe',
'Australia',
],
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp('[a-zA-Z ]')),
],
),
Use padding, borderRadius, isEnabled, size, iconBackgroundColor, iconColor to change field visually.
InputAutoComplete(
label: 'Select Continent',
hints: const [
'Asia',
'Africa',
'North America',
'South America',
'Antarctica',
'Europe',
'Australia',
],
padding: allPadding16,
borderRadius: 16,
isEnabled: false,
iconBackgroundColor: Colors.pink,
iconColor: Colors.white,
),
Use isBorderEnabled and isShadowEnabled to change field shadow and border of options.
InputAutoComplete(
label: 'Select Continent',
hints: const [
'Asia',
'Africa',
'North America',
'South America',
'Antarctica',
'Europe',
'Australia',
],
isBorderEnabled: false,
isShadowEnabled: true,
),
Use icon to pass custom icon and optionsBackgroundColor to change background color of options.
InputAutoComplete(
label: 'Select Continent',
hints: const [
'Asia',
'Africa',
'North America',
'South America',
'Antarctica',
'Europe',
'Australia',
],
icon: FontAwesomeIcons.flag,
optionsBackgroundColor: Color(0XFFFFBAD7),
),
Use onSelected to perform action when user selects input. TODO: pass the controller value back.
const continents = [
'Asia',
'Africa',
'North America',
'South America',
'Antarctica',
'Europe',
'Australia',
];
const InputAutoComplete(
label: 'Select Continent',
hints: continents,
onSelected: (value) {
print(value);
},
),
Source Code
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:team/vaahextendflutter/app_theme.dart';
import 'package:team/vaahextendflutter/helpers/constants.dart';
import 'package:team/vaahextendflutter/helpers/enums.dart';
class InputAutoComplete extends StatefulWidget {
final String label;
final EdgeInsets padding;
final double borderRadius;
final bool isEnabled;
final bool isBorderEnabled;
final InputSize size;
final double height;
final Color? optionsBackgroundColor;
final bool isShadowEnabled;
final IconData? icon;
final Color? iconBackgroundColor;
final Color? iconColor;
final List<String> hints;
final String? Function(String?)? validator;
final AutovalidateMode? autoValidateMode;
final List<TextInputFormatter>? inputFormatters;
final Function(dynamic)? onSelected;
const InputAutoComplete({
super.key,
required this.label,
this.padding = allPadding12,
this.borderRadius = defaultPadding / 2,
this.isBorderEnabled = true,
this.isEnabled = true,
this.size = InputSize.medium,
this.height = 250,
this.optionsBackgroundColor,
this.isShadowEnabled = false,
this.icon,
this.iconBackgroundColor,
this.iconColor,
required this.hints,
this.validator,
this.autoValidateMode,
this.inputFormatters,
this.onSelected,
});
@override
State<InputAutoComplete> createState() => _InputAutoCompleteState();
}
class _InputAutoCompleteState extends State<InputAutoComplete> {
final controller = TextEditingController();
bool _optionsAvailable = false;
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (ctx, constraints) {
return Autocomplete<String>(
optionsBuilder: (TextEditingValue textEditingValue) {
List<String> hints = widget.hints
.where(
(String hint) => hint.toLowerCase().contains(textEditingValue.text.toLowerCase()),
)
.toList();
setState(() {
_optionsAvailable = hints.isNotEmpty && textEditingValue.text != hints.first;
});
return hints;
},
displayStringForOption: (String hint) => hint,
onSelected: (String selection) {
if (widget.onSelected != null) {
widget.onSelected!(selection);
}
},
optionsViewBuilder: (BuildContext context, onSelected, options) {
return Align(
alignment: Alignment.topLeft,
child: Material(
child: Container(
width: constraints.maxWidth,
height: widget.height,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(widget.borderRadius),
bottomRight: Radius.circular(widget.borderRadius),
),
border: widget.isBorderEnabled
? Border.all(color: AppTheme.colors['black']!.shade300)
: null,
boxShadow: !widget.isShadowEnabled
? null
: const [
BoxShadow(
color: Color(0x44000000),
offset: Offset(0, 1),
blurRadius: 8.0,
spreadRadius: 0.0,
),
],
color: widget.optionsBackgroundColor ?? AppTheme.colors['primary']!.shade50,
),
child: ListView.builder(
padding: const EdgeInsets.all(10.0),
itemCount: options.length,
itemBuilder: (BuildContext context, int index) {
final String option = options.elementAt(index);
return Column(
children: [
GestureDetector(
onTap: () {
setState(() {});
FocusScope.of(context).focusedChild?.unfocus();
onSelected(option);
},
child: ListTile(
title: Text(option),
),
),
],
);
},
),
),
),
);
},
fieldViewBuilder: (
BuildContext context,
TextEditingController fieldTextEditingController,
FocusNode fieldFocusNode,
VoidCallback onFieldSubmitted,
) {
return TextFormField(
onTap: () {
setState(() {});
if (fieldFocusNode.hasFocus) fieldFocusNode.unfocus();
},
decoration: InputDecoration(
contentPadding: widget.padding,
border: border(AppTheme.colors['black']!.shade400),
enabledBorder: border(AppTheme.colors['black']!.shade400),
disabledBorder: border(AppTheme.colors['black']!.shade300),
focusedBorder: !_optionsAvailable
? border(AppTheme.colors['black']!.shade400)
: OutlineInputBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(widget.borderRadius),
topRight: Radius.circular(widget.borderRadius),
),
borderSide: BorderSide(
width: 1,
color: AppTheme.colors['black']!.shade400,
),
),
errorBorder: border(AppTheme.colors['danger']!.shade400),
focusedErrorBorder: border(AppTheme.colors['danger']!.shade400),
errorStyle: TextStyle(color: AppTheme.colors['danger']!.shade400),
hintText: widget.label,
hintStyle: TextStyle(
fontSize: getFontSize(),
color: widget.isEnabled
? AppTheme.colors['black']!.shade400
: AppTheme.colors['black']!.shade300,
),
suffixIcon: InkWell(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(widget.borderRadius),
bottomRight: fieldFocusNode.hasFocus && _optionsAvailable
? Radius.zero
: Radius.circular(widget.borderRadius),
),
color: widget.iconBackgroundColor ?? AppTheme.colors['primary'],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FaIcon(
widget.icon ?? FontAwesomeIcons.angleDown,
size: getFontSize() * 1.3,
color: widget.iconColor ?? AppTheme.colors['white'],
),
],
),
),
),
),
controller: fieldTextEditingController,
focusNode: fieldFocusNode,
style: TextStyle(
fontSize: getFontSize(),
color: AppTheme.colors['black']!.shade400,
),
enabled: widget.isEnabled,
validator: widget.validator,
autovalidateMode: widget.autoValidateMode,
inputFormatters: widget.inputFormatters,
);
},
);
},
);
}
OutlineInputBorder border(Color color) {
return OutlineInputBorder(
borderRadius: BorderRadius.circular(widget.borderRadius),
borderSide: BorderSide(
width: 1,
color: color,
),
);
}
double getFontSize() {
switch (widget.size) {
case InputSize.extraSmall:
return AppTheme.extraSmall;
case InputSize.small:
return AppTheme.small;
case InputSize.large:
return AppTheme.large;
case InputSize.extraLarge:
return AppTheme.extraLarge;
default:
return AppTheme.medium;
}
}
}