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

Side Menu

Ported from HyperUI’s Side Menu — see Attribution.

Logo, links with accordion menu and profile details

Section titled “Logo, links with accordion menu and profile details”

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

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 _chevronDownSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/></svg>''';
class SideMenu1Example extends StatefulWidget {
const SideMenu1Example({super.key});
@override
State<SideMenu1Example> createState() => _SideMenu1ExampleState();
}
class _SideMenu1ExampleState extends State<SideMenu1Example> {
var _teamsOpen = false;
var _accountOpen = false;
@override
Widget build(BuildContext context) {
return Style(
'max-w-xs',
child: Style(
'flex h-screen flex-col justify-between border-e border-gray-100 bg-white dark:border-gray-800 dark:bg-gray-900',
children: [
Style(
'p-4',
children: [
const Style(
'grid h-12 w-32 place-content-center rounded-lg bg-gray-100 text-sm text-gray-500 dark:bg-gray-800 dark:text-gray-400',
child: Text('Logo'),
),
Style(
'mt-4 space-y-1',
children: [
Style(
'block rounded-lg bg-gray-100 px-4 py-2 text-sm font-medium text-gray-900 dark:bg-gray-800 dark:text-white',
cursor: SystemMouseCursors.click,
child: const Text('General'),
),
Style(
'group [&_summary::-webkit-details-marker]:hidden',
open: _teamsOpen,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => setState(() => _teamsOpen = !_teamsOpen),
child: Style(
'flex items-center justify-between rounded-lg px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-gray-800 dark:hover:text-white',
cursor: SystemMouseCursors.click,
children: [
const Text('Teams'),
Style(
'shrink-0 transition duration-300 group-open:-rotate-180',
child: const Style(
'size-5',
child: CurrentColorIcon(_chevronDownSvg),
),
),
],
),
),
if (_teamsOpen)
Style(
'mt-2 space-y-1 px-4',
children: [
Style(
'block rounded-lg px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-gray-800 dark:hover:text-white',
cursor: SystemMouseCursors.click,
child: const Text('Banned Users'),
),
Style(
'block rounded-lg px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-gray-800 dark:hover:text-white',
cursor: SystemMouseCursors.click,
child: const Text('Calendar'),
),
],
),
],
),
Style(
'block rounded-lg px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-gray-800 dark:hover:text-white',
cursor: SystemMouseCursors.click,
child: const Text('Billing'),
),
Style(
'block rounded-lg px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-gray-800 dark:hover:text-white',
cursor: SystemMouseCursors.click,
child: const Text('Invoices'),
),
Style(
'group [&_summary::-webkit-details-marker]:hidden',
open: _accountOpen,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () =>
setState(() => _accountOpen = !_accountOpen),
child: Style(
'flex items-center justify-between rounded-lg px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-gray-800 dark:hover:text-white',
cursor: SystemMouseCursors.click,
children: [
const Text('Account'),
Style(
'shrink-0 transition duration-300 group-open:-rotate-180',
child: const Style(
'size-5',
child: CurrentColorIcon(_chevronDownSvg),
),
),
],
),
),
if (_accountOpen)
Style(
'mt-2 space-y-1 px-4',
children: [
Style(
'block rounded-lg px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-gray-800 dark:hover:text-white',
cursor: SystemMouseCursors.click,
child: const Text('Details'),
),
Style(
'block rounded-lg px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-gray-800 dark:hover:text-white',
cursor: SystemMouseCursors.click,
child: const Text('Security'),
),
Style(
'w-full rounded-lg px-4 py-2 [text-align:inherit] text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200',
cursor: SystemMouseCursors.click,
child: const Text('Logout'),
),
],
),
],
),
],
),
],
),
Style(
'sticky inset-x-0 bottom-0 border-t border-gray-100 dark:border-gray-800',
child: Style(
'flex items-center gap-2 bg-white p-4 hover:bg-gray-50 hover:transition-colors dark:bg-gray-900 dark:hover:bg-gray-800',
cursor: SystemMouseCursors.click,
children: [
Style(
'size-10 rounded-full object-cover',
child: Image.network(
'https://images.unsplash.com/photo-1600486913747-55e5470d6f40?auto=format&fit=crop&q=80&w=1160',
fit: BoxFit.cover,
),
),
Style(
'text-xs text-gray-900 dark:text-white',
children: const [
Style(
'block font-medium',
child: Text('Eric Frusciante'),
),
Text('eric@frusciante.com'),
],
),
],
),
),
],
),
);
}
}

Icon, links with tooltips and logout action

Section titled “Icon, links with tooltips and logout action”

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

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 _generalSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg>''';
const _teamsSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"/></svg>''';
const _billingSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/></svg>''';
const _invoicesSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 11h.01M12 11h.01M9 11h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2z"/></svg>''';
const _accountSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg>''';
const _logoutSvg =
'''<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/></svg>''';
class SideMenu2Example extends StatelessWidget {
const SideMenu2Example({super.key});
@override
Widget build(BuildContext context) {
return Style(
'max-w-xs',
child: Style(
'flex h-screen w-16 flex-col justify-between border-e border-gray-100 bg-white dark:border-gray-800 dark:bg-gray-900',
children: [
Style.empty(
children: [
Style(
'inline-flex size-16 items-center justify-center',
child: const Style(
'grid size-12 place-content-center rounded-lg bg-gray-100 text-sm text-gray-500 dark:bg-gray-800 dark:text-gray-400',
child: Text('L'),
),
),
Style(
'space-y-1 border-t border-gray-100 px-2 pt-4 dark:border-gray-800',
children: [
Style(
'group relative grid place-content-center rounded-lg bg-gray-100 px-4 py-2 text-gray-900 dark:bg-gray-800 dark:text-white',
cursor: SystemMouseCursors.click,
children: [
const Style(
'size-5',
child: CurrentColorIcon(_generalSvg),
),
const Style(
'invisible absolute inset-s-full top-1/2 ms-4 -translate-y-1/2 rounded-sm bg-gray-200 px-2 py-1.5 text-xs font-medium text-gray-700 group-hover:visible dark:bg-gray-700 dark:text-gray-200',
child: Text('General'),
),
],
),
Style(
'group relative grid place-content-center rounded-lg px-4 py-2 text-gray-700 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-gray-800 dark:hover:text-white',
cursor: SystemMouseCursors.click,
children: [
const Style(
'size-5',
child: CurrentColorIcon(_teamsSvg),
),
const Style(
'invisible absolute inset-s-full top-1/2 ms-4 -translate-y-1/2 rounded-sm bg-gray-200 px-2 py-1.5 text-xs font-medium text-gray-700 group-hover:visible dark:bg-gray-700 dark:text-gray-200',
child: Text('Teams'),
),
],
),
Style(
'group relative grid place-content-center rounded-lg px-4 py-2 text-gray-700 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-gray-800 dark:hover:text-white',
cursor: SystemMouseCursors.click,
children: [
const Style(
'size-5',
child: CurrentColorIcon(_billingSvg),
),
const Style(
'invisible absolute inset-s-full top-1/2 ms-4 -translate-y-1/2 rounded-sm bg-gray-200 px-2 py-1.5 text-xs font-medium text-gray-700 group-hover:visible dark:bg-gray-700 dark:text-gray-200',
child: Text('Billing'),
),
],
),
Style(
'group relative grid place-content-center rounded-lg px-4 py-2 text-gray-700 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-gray-800 dark:hover:text-white',
cursor: SystemMouseCursors.click,
children: [
const Style(
'size-5',
child: CurrentColorIcon(_invoicesSvg),
),
const Style(
'invisible absolute inset-s-full top-1/2 ms-4 -translate-y-1/2 rounded-sm bg-gray-200 px-2 py-1.5 text-xs font-medium text-gray-700 group-hover:visible dark:bg-gray-700 dark:text-gray-200',
child: Text('Invoices'),
),
],
),
Style(
'group relative grid place-content-center rounded-lg px-4 py-2 text-gray-700 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-gray-800 dark:hover:text-white',
cursor: SystemMouseCursors.click,
children: [
const Style(
'size-5',
child: CurrentColorIcon(_accountSvg),
),
const Style(
'invisible absolute inset-s-full top-1/2 ms-4 -translate-y-1/2 rounded-sm bg-gray-200 px-2 py-1.5 text-xs font-medium text-gray-700 group-hover:visible dark:bg-gray-700 dark:text-gray-200',
child: Text('Account'),
),
],
),
],
),
],
),
Style(
'sticky inset-x-0 bottom-0 border-t border-gray-100 bg-white p-2 dark:border-gray-800 dark:bg-gray-900',
child: Style(
'group relative grid place-content-center rounded-lg px-4 py-2 text-gray-700 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:text-gray-200 dark:hover:bg-gray-800 dark:hover:text-white',
cursor: SystemMouseCursors.click,
children: [
const Style('size-5', child: CurrentColorIcon(_logoutSvg)),
const Style(
'invisible absolute inset-s-full top-1/2 ms-4 -translate-y-1/2 rounded-sm bg-gray-200 px-2 py-1.5 text-xs font-medium text-gray-700 group-hover:visible dark:bg-gray-700 dark:text-gray-200',
child: Text('Logout'),
),
],
),
),
],
),
);
}
}