Skip to content
HyperUI for Flutter requires the Style package — this site itself ships no pub package.

Inputs

Ported from HyperUI’s Inputs — see Attribution.

Includes a dark: variant — toggle the site theme (top right) to preview it.

class Inputs1Example extends StatelessWidget {
const Inputs1Example({super.key});
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final isSmUp = MediaQuery.sizeOf(context).width >= 640;
final fontSize = isSmUp ? 14.0 : 16.0;
final lineHeight = isSmUp ? 20.0 : 24.0;
final baseStyle = Theme.of(context).textTheme.bodyLarge ?? const TextStyle();
return Style(
'mx-auto max-w-sm p-6',
children: [
const Style(
'text-sm font-medium text-gray-700 dark:text-gray-200',
display: DisplayType.inline,
child: Text('Email'),
),
Style(
'mt-0.5 w-full rounded border-gray-300 shadow-sm sm:text-sm dark:border-gray-600 dark:bg-gray-900 dark:text-white',
child: TextFormField(
keyboardType: TextInputType.emailAddress,
style: baseStyle.copyWith(
fontSize: fontSize,
height: lineHeight / fontSize,
color: isDark ? Colors.white : Colors.black,
),
),
),
],
);
}
}
class CurrentColorIcon extends StatelessWidget
implements StyleReplacedElementWrapper {
const CurrentColorIcon(this.svg, {super.key});
final String svg;
@override
Widget build(BuildContext context) {
final color = IconTheme.of(context).color ?? const Color(0xFF000000);
return SvgPicture.string(svg, theme: SvgTheme(currentColor: color));
}
}
const _atSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 12a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0Zm0 0c0 1.657 1.007 3 2.25 3S21 13.657 21 12a9 9 0 1 0-2.636 6.364M16.5 12V8.25"/></svg>''';
class Inputs2Example extends StatelessWidget {
const Inputs2Example({super.key});
@override
Widget build(BuildContext context) {
final isSmUp = MediaQuery.sizeOf(context).width >= 640;
final fontSize = isSmUp ? 14.0 : 16.0;
final lineHeight = isSmUp ? 20.0 : 24.0;
final baseStyle = Theme.of(context).textTheme.bodyLarge ?? const TextStyle();
return Style(
'mx-auto max-w-sm p-6',
children: [
const Style(
'text-sm font-medium text-gray-700',
display: DisplayType.inline,
child: Text('Email'),
),
Style(
'relative',
children: [
Style(
'mt-0.5 w-full rounded border-gray-300 pe-8 shadow-sm sm:text-sm',
child: TextFormField(
keyboardType: TextInputType.emailAddress,
style: baseStyle.copyWith(
fontSize: fontSize,
height: lineHeight / fontSize,
color: Colors.black,
),
),
),
Style(
'absolute inset-y-0 right-0 grid w-8 place-content-center text-gray-700',
child: Style('size-4', child: CurrentColorIcon(_atSvg)),
),
],
),
],
);
}
}
class CurrentColorIcon extends StatelessWidget
implements StyleReplacedElementWrapper {
const CurrentColorIcon(this.svg, {super.key});
final String svg;
@override
Widget build(BuildContext context) {
final color = IconTheme.of(context).color ?? const Color(0xFF000000);
return SvgPicture.string(svg, theme: SvgTheme(currentColor: color));
}
}
const _searchSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/></svg>''';
class Inputs3Example extends StatelessWidget {
const Inputs3Example({super.key});
@override
Widget build(BuildContext context) {
final isSmUp = MediaQuery.sizeOf(context).width >= 640;
final fontSize = isSmUp ? 14.0 : 16.0;
final lineHeight = isSmUp ? 20.0 : 24.0;
final baseStyle = Theme.of(context).textTheme.bodyLarge ?? const TextStyle();
return Style(
'mx-auto max-w-sm p-6',
children: [
const Style(
'text-sm font-medium text-gray-700',
display: DisplayType.inline,
child: Text('Search'),
),
Style(
'relative',
children: [
Style(
'mt-0.5 w-full rounded border-gray-300 pe-10 shadow-sm sm:text-sm',
child: TextFormField(
style: baseStyle.copyWith(
fontSize: fontSize,
height: lineHeight / fontSize,
color: Colors.black,
),
),
),
Style(
'absolute inset-y-0 right-2 grid w-8 place-content-center',
child: Style(
'rounded-full p-1.5 text-gray-700 transition-colors hover:bg-gray-100',
child: Style('size-4', child: CurrentColorIcon(_searchSvg)),
),
),
],
),
],
);
}
}

Includes a dark: variant — toggle the site theme (top right) to preview it.

class Inputs4Example extends StatelessWidget {
const Inputs4Example({super.key});
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final isSmUp = MediaQuery.sizeOf(context).width >= 640;
final fontSize = isSmUp ? 14.0 : 16.0;
final lineHeight = isSmUp ? 20.0 : 24.0;
final baseStyle = Theme.of(context).textTheme.bodyLarge ?? const TextStyle();
final borderColor = isDark
? tailwindColors['gray']![600]!
: tailwindColors['gray']![300]!;
final labelStyle = TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: isDark ? Colors.white : tailwindColors['gray']![700],
);
final border = OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(color: borderColor),
);
return Style(
'mx-auto max-w-sm p-6',
child: Style(
'relative',
children: [
Style(
'peer mt-0.5 w-full rounded border-gray-300 shadow-sm sm:text-sm dark:border-gray-600 dark:bg-gray-900 dark:text-white',
child: Builder(
builder: (context) => TextFormField(
keyboardType: TextInputType.emailAddress,
style: baseStyle.copyWith(
fontSize: fontSize,
height: lineHeight / fontSize,
color: isDark ? Colors.white : Colors.black,
),
decoration: InputDecoration(
labelText: 'Email',
labelStyle: labelStyle,
floatingLabelStyle: labelStyle,
floatingLabelBehavior: FloatingLabelBehavior.auto,
isDense: true,
filled: true,
fillColor: isDark ? tailwindColors['gray']![900] : Colors.white,
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 9),
border: border,
enabledBorder: border,
focusedBorder: border,
),
),
),
),
Visibility(
visible: false,
child: const Style(
'absolute inset-y-0 start-3 -translate-y-5 bg-white px-0.5 text-sm font-medium text-gray-700 transition-transform peer-placeholder-shown:translate-y-0 peer-focus:-translate-y-5 dark:bg-gray-900 dark:text-white',
display: DisplayType.inline,
child: Text('Email'),
),
),
],
),
);
}
}