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

Accordions

Ported from HyperUI’s Accordions — see Attribution.

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 aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>''';
const _answer =
'Lorem, ipsum dolor sit amet consectetur adipisicing elit. Deserunt '
'similique, quae hic dicta quo facere facilis praesentium a sunt, est '
'quia pariatur nam, modi aut minus iste odio consectetur molestias '
'iusto cupiditate ullam laborum veniam quos officia. Quos, temporibus '
'perspiciatis!';
class NeobrutalismAccordions1Example extends StatefulWidget {
const NeobrutalismAccordions1Example({super.key});
@override
State<NeobrutalismAccordions1Example> createState() =>
_NeobrutalismAccordions1ExampleState();
}
class _NeobrutalismAccordions1ExampleState
extends State<NeobrutalismAccordions1Example> {
var _open0 = false;
var _open1 = false;
var _open2 = false;
final _focusNode0 = FocusNode();
final _focusNode1 = FocusNode();
final _focusNode2 = FocusNode();
@override
void dispose() {
_focusNode0.dispose();
_focusNode1.dispose();
_focusNode2.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Style(
'mx-auto max-w-3xl p-6',
child: Style(
'space-y-3',
children: [
Style(
'group [&_summary::-webkit-details-marker]:hidden',
open: _open0,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
_focusNode0.requestFocus();
setState(() => _open0 = !_open0);
},
child: Focus(
focusNode: _focusNode0,
includeSemantics: false,
child: Style(
'flex cursor-pointer items-center justify-between gap-4 border-2 border-black bg-white px-4 py-3 font-medium text-gray-900 shadow-[4px_4px_0_0] shadow-black hover:bg-yellow-200 focus:bg-yellow-200 focus:outline-0 dark:border-white dark:bg-gray-900 dark:text-white dark:shadow-white dark:hover:bg-yellow-700 dark:focus:bg-yellow-700',
cursor: SystemMouseCursors.click,
children: [
const Style(
'font-semibold',
child: Text('What are the basic features?'),
),
Style(
'size-5 shrink-0 group-open:-rotate-180',
child: const CurrentColorIcon(_chevronDownSvg),
),
],
),
),
),
if (_open0)
Style(
'p-4',
child: const Style(
'text-black dark:text-white',
child: Text(_answer),
),
),
],
),
Style(
'group [&_summary::-webkit-details-marker]:hidden',
open: _open1,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
_focusNode1.requestFocus();
setState(() => _open1 = !_open1);
},
child: Focus(
focusNode: _focusNode1,
includeSemantics: false,
child: Style(
'flex cursor-pointer items-center justify-between gap-4 border-2 border-black bg-white px-4 py-3 font-medium text-gray-900 shadow-[4px_4px_0_0] shadow-black hover:bg-yellow-200 focus:bg-yellow-200 focus:outline-0 dark:border-white dark:bg-gray-900 dark:text-white dark:shadow-white dark:hover:bg-yellow-700 dark:focus:bg-yellow-700',
cursor: SystemMouseCursors.click,
children: [
const Style(
'font-semibold',
child: Text('How do I get started?'),
),
Style(
'size-5 shrink-0 group-open:-rotate-180',
child: const CurrentColorIcon(_chevronDownSvg),
),
],
),
),
),
if (_open1)
Style(
'p-4',
child: const Style(
'text-black dark:text-white',
child: Text(_answer),
),
),
],
),
Style(
'group [&_summary::-webkit-details-marker]:hidden',
open: _open2,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
_focusNode2.requestFocus();
setState(() => _open2 = !_open2);
},
child: Focus(
focusNode: _focusNode2,
includeSemantics: false,
child: Style(
'flex cursor-pointer items-center justify-between gap-4 border-2 border-black bg-white px-4 py-3 font-medium text-gray-900 shadow-[4px_4px_0_0] shadow-black hover:bg-yellow-200 focus:bg-yellow-200 focus:outline-0 dark:border-white dark:bg-gray-900 dark:text-white dark:shadow-white dark:hover:bg-yellow-700 dark:focus:bg-yellow-700',
cursor: SystemMouseCursors.click,
children: [
const Style(
'font-semibold',
child: Text('What support options are available?'),
),
Style(
'size-5 shrink-0 group-open:-rotate-180',
child: const CurrentColorIcon(_chevronDownSvg),
),
],
),
),
),
if (_open2)
Style(
'p-4',
child: const Style(
'text-black dark:text-white',
child: Text(_answer),
),
),
],
),
],
),
);
}
}

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 aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>''';
const _answer =
'Lorem, ipsum dolor sit amet consectetur adipisicing elit. Deserunt '
'similique, quae hic dicta quo facere facilis praesentium a sunt, est '
'quia pariatur nam, modi aut minus iste odio consectetur molestias '
'iusto cupiditate ullam laborum veniam quos officia. Quos, temporibus '
'perspiciatis!';
class NeobrutalismAccordions2Example extends StatefulWidget {
const NeobrutalismAccordions2Example({super.key});
@override
State<NeobrutalismAccordions2Example> createState() =>
_NeobrutalismAccordions2ExampleState();
}
class _NeobrutalismAccordions2ExampleState
extends State<NeobrutalismAccordions2Example> {
var _open0 = false;
var _open1 = false;
var _open2 = false;
final _focusNode0 = FocusNode();
final _focusNode1 = FocusNode();
final _focusNode2 = FocusNode();
@override
void dispose() {
_focusNode0.dispose();
_focusNode1.dispose();
_focusNode2.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Style(
'mx-auto max-w-3xl p-6',
child: Style(
'space-y-3',
children: [
Style(
'group border-2 border-black shadow-[4px_4px_0_0] shadow-black dark:border-white dark:shadow-white [&_summary::-webkit-details-marker]:hidden',
open: _open0,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
_focusNode0.requestFocus();
setState(() => _open0 = !_open0);
},
child: Focus(
focusNode: _focusNode0,
includeSemantics: false,
child: Style(
'flex cursor-pointer items-center justify-between gap-4 bg-white px-4 py-3 font-medium text-gray-900 hover:bg-yellow-200 focus:bg-yellow-200 focus:outline-0 dark:bg-gray-900 dark:text-white dark:hover:bg-yellow-700 dark:focus:bg-yellow-700',
cursor: SystemMouseCursors.click,
children: [
const Style(
'font-semibold',
child: Text('What are the basic features?'),
),
Style(
'size-5 shrink-0 group-open:-rotate-180',
child: const CurrentColorIcon(_chevronDownSvg),
),
],
),
),
),
if (_open0)
Style(
'border-t-2 border-black p-4 dark:border-white',
child: const Style(
'text-black dark:text-white',
child: Text(_answer),
),
),
],
),
Style(
'group border-2 border-black shadow-[4px_4px_0_0] shadow-black dark:border-white dark:shadow-white [&_summary::-webkit-details-marker]:hidden',
open: _open1,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => setState(() => _open1 = !_open1),
child: Style(
'flex cursor-pointer items-center justify-between gap-4 bg-white px-4 py-3 font-medium text-gray-900 hover:bg-yellow-200 focus:bg-yellow-200 focus:outline-0 dark:bg-gray-900 dark:text-white dark:hover:bg-yellow-700 dark:focus:bg-yellow-700',
cursor: SystemMouseCursors.click,
children: [
const Style(
'font-semibold',
child: Text('How do I get started?'),
),
Style(
'size-5 shrink-0 group-open:-rotate-180',
child: const CurrentColorIcon(_chevronDownSvg),
),
],
),
),
if (_open1)
Style(
'border-t-2 border-black p-4 dark:border-white',
child: const Style(
'text-black dark:text-white',
child: Text(_answer),
),
),
],
),
Style(
'group border-2 border-black shadow-[4px_4px_0_0] shadow-black dark:border-white dark:shadow-white [&_summary::-webkit-details-marker]:hidden',
open: _open2,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => setState(() => _open2 = !_open2),
child: Style(
'flex cursor-pointer items-center justify-between gap-4 bg-white px-4 py-3 font-medium text-gray-900 hover:bg-yellow-200 focus:bg-yellow-200 focus:outline-0 dark:bg-gray-900 dark:text-white dark:hover:bg-yellow-700 dark:focus:bg-yellow-700',
cursor: SystemMouseCursors.click,
children: [
const Style(
'font-semibold',
child: Text('What support options are available?'),
),
Style(
'size-5 shrink-0 group-open:-rotate-180',
child: const CurrentColorIcon(_chevronDownSvg),
),
],
),
),
if (_open2)
Style(
'border-t-2 border-black p-4 dark:border-white',
child: const Style(
'text-black dark:text-white',
child: Text(_answer),
),
),
],
),
],
),
);
}
}

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 aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>''';
const _answer =
'Lorem, ipsum dolor sit amet consectetur adipisicing elit. Deserunt '
'similique, quae hic dicta quo facere facilis praesentium a sunt, est '
'quia pariatur nam, modi aut minus iste odio consectetur molestias '
'iusto cupiditate ullam laborum veniam quos officia. Quos, temporibus '
'perspiciatis!';
class NeobrutalismAccordions3Example extends StatefulWidget {
const NeobrutalismAccordions3Example({super.key});
@override
State<NeobrutalismAccordions3Example> createState() =>
_NeobrutalismAccordions3ExampleState();
}
class _NeobrutalismAccordions3ExampleState
extends State<NeobrutalismAccordions3Example> {
var _open0 = false;
var _open1 = false;
var _open2 = false;
final _focusNode0 = FocusNode();
@override
void dispose() {
_focusNode0.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Style(
'mx-auto max-w-3xl p-6',
child: Style(
'divide-y-2 divide-black border-2 border-black shadow-[4px_4px_0_0] shadow-black dark:divide-white dark:border-white dark:shadow-white',
children: [
Style(
'group [&_summary::-webkit-details-marker]:hidden',
open: _open0,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
_focusNode0.requestFocus();
setState(() => _open0 = !_open0);
},
child: Focus(
focusNode: _focusNode0,
includeSemantics: false,
child: Style(
'flex cursor-pointer items-center justify-between gap-4 bg-white px-4 py-3 font-medium text-gray-900 hover:bg-yellow-200 focus:bg-yellow-200 focus:outline-0 dark:bg-gray-900 dark:text-white dark:hover:bg-yellow-700 dark:focus:bg-yellow-700',
cursor: SystemMouseCursors.click,
children: [
const Style(
'font-semibold',
child: Text('What are the basic features?'),
),
Style(
'size-5 shrink-0 group-open:-rotate-180',
child: const CurrentColorIcon(_chevronDownSvg),
),
],
),
),
),
if (_open0)
Style(
'border-t-2 border-black p-4 dark:border-white',
child: const Style(
'text-black dark:text-white',
child: Text(_answer),
),
),
],
),
Style(
'group [&_summary::-webkit-details-marker]:hidden',
open: _open1,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => setState(() => _open1 = !_open1),
child: Style(
'flex cursor-pointer items-center justify-between gap-4 bg-white px-4 py-3 font-medium text-gray-900 hover:bg-yellow-200 focus:bg-yellow-200 focus:outline-0 dark:bg-gray-900 dark:text-white dark:hover:bg-yellow-700 dark:focus:bg-yellow-700',
cursor: SystemMouseCursors.click,
children: [
const Style(
'font-semibold',
child: Text('How do I get started?'),
),
Style(
'size-5 shrink-0 group-open:-rotate-180',
child: const CurrentColorIcon(_chevronDownSvg),
),
],
),
),
if (_open1)
Style(
'border-t-2 border-black p-4 dark:border-white',
child: const Style(
'text-black dark:text-white',
child: Text(_answer),
),
),
],
),
Style(
'group [&_summary::-webkit-details-marker]:hidden',
open: _open2,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => setState(() => _open2 = !_open2),
child: Style(
'flex cursor-pointer items-center justify-between gap-4 bg-white px-4 py-3 font-medium text-gray-900 hover:bg-yellow-200 focus:bg-yellow-200 focus:outline-0 dark:bg-gray-900 dark:text-white dark:hover:bg-yellow-700 dark:focus:bg-yellow-700',
cursor: SystemMouseCursors.click,
children: [
const Style(
'font-semibold',
child: Text('What support options are available?'),
),
Style(
'size-5 shrink-0 group-open:-rotate-180',
child: const CurrentColorIcon(_chevronDownSvg),
),
],
),
),
if (_open2)
Style(
'border-t-2 border-black p-4 dark:border-white',
child: const Style(
'text-black dark:text-white',
child: Text(_answer),
),
),
],
),
],
),
);
}
}