Accordion

An expandable/collapsible component for showing and hiding content panels with support for multiple expansion modes

An accordion component that manages expansion state of content panels with min/max constraints.

When to use this

  • FAQ sections: Display frequently asked questions with expandable answers
  • Content organization: Organize large amounts of content in a compact, scannable format
  • Settings panels: Group related settings that can be shown/hidden
  • Navigation menus: Create collapsible menu structures with nested content

Basic implementation

Basic implementation
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';

class AccordionExample extends StatefulWidget {
  const AccordionExample({super.key});

  @override
  State<AccordionExample> createState() => _AccordionExampleState();
}

class _AccordionExampleState extends State<AccordionExample> {
  final controller = RemixAccordionController<String>(min: 0, max: 1);

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RemixAccordionGroup(
      controller: controller,
      child: ColumnBox(
        style: FlexBoxStyler().spacing(16),
        children: [
          RemixAccordion(
            value: 'accordion1',
            title: 'How do I update my account information?',
            leadingIcon: Icons.help_outline,
            style: itemStyle,
            child: const Text(
              'Insert the accordion description here. It would look better as two lines of text.',
            ),
          ),
          RemixAccordion(
            value: 'accordion2',
            title: 'What payment methods are accepted?',
            leadingIcon: Icons.help_outline,
            style: itemStyle,
            child: const Text(
                'Major credit and debit cards like Visa, MasterCard, and American Express, as well as digital payment options like PayPal and Apple Pay.'),
          ),
          RemixAccordion(
            value: 'accordion3',
            title: 'How can I track my order?',
            leadingIcon: Icons.help_outline,
            style: itemStyle,
            child: const Text(
                'You can track your order status in the "My Orders" section of your account.'),
          ),
        ],
      ),
    );
  }

  RemixAccordionStyle<String> get itemStyle {
    return RemixAccordionStyle<String>()
        .content(BoxStyler().paddingX(16).paddingTop(8))
        .wrapClipRRect(borderRadius: BorderRadius.circular(8))
        .paddingX(16)
        .paddingY(14)
        .borderRounded(8)
        .onHovered(RemixAccordionStyle<String>().color(Colors.grey.shade100))
        .decoration(
          BoxDecorationMix(
            color: Colors.white,
            border: BoxBorderMix.all(
              BorderSideMix().color(Colors.grey.shade300).width(1),
            ),
            borderRadius: BorderRadiusMix.circular(8),
          ),
        )
        .trigger(
          FlexBoxStyler()
              .direction(Axis.horizontal)
              .mainAxisAlignment(MainAxisAlignment.spaceBetween)
              .spacing(12),
        )
        .leadingIcon(IconStyler().color(Colors.grey.shade700).size(20))
        .title(
          TextStyler()
              .color(Colors.grey.shade900)
              .fontWeight(FontWeight.w500)
              .fontSize(14),
        )
        .trailingIcon(IconStyler().color(Colors.grey.shade700).size(20));
  }
}

Fortal styles

Remix includes Fortal-themed style helpers for this component:

Fortal base style
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';

class FortalAccordionExample extends StatefulWidget {
  const FortalAccordionExample({super.key});

  @override
  State<FortalAccordionExample> createState() => _FortalAccordionExampleState();
}

class _FortalAccordionExampleState extends State<FortalAccordionExample> {
  final controller = RemixAccordionController<String>();

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RemixAccordionGroup(
      controller: controller,
      child: Column(
        children: [
          RemixAccordion(
            value: 'item1',
            title: 'First Item',
            style: FortalAccordionStyle.base(),
            child: Text('First content'),
          ),
          RemixAccordion(
            value: 'item2',
            title: 'Second Item',
            style: FortalAccordionStyle.base(),
            child: Text('Second content'),
          ),
        ],
      ),
    );
  }
}

See the FortalAccordionStyle source code for all available options.

Constructor

Constructor
// Accordion Group
const RemixAccordionGroup({
  Key? key,
  required Widget child,
  required RemixAccordionController<T> controller,
  List<T> initialExpandedValues = const [],
})

// Accordion Item
const RemixAccordion({
  Key? key,
  required T value,
  required Widget child,
  String? title,
  IconData? leadingIcon,
  IconData? trailingIcon,
  NakedAccordionTriggerBuilder<T>? builder,
  bool enabled = true,
  MouseCursor mouseCursor = SystemMouseCursors.click,
  bool enableFeedback = true,
  bool autofocus = false,
  FocusNode? focusNode,
  ValueChanged<bool>? onFocusChange,
  ValueChanged<bool>? onHoverChange,
  ValueChanged<bool>? onPressChange,
  String? semanticLabel,
  RemixAccordionStyle style = const RemixAccordionStyle.create(),
  Widget Function(Widget, Animation<double>) transitionBuilder = defaultAccordionTransitionBuilder,
})

Properties

Widget Properties

keyKey?

Optional. Controls how one widget replaces another widget in the tree.

valueT

Required. Unique identifier tracked by the controller.

childWidget

Required. Content rendered while expanded.

titleString?

Optional. Title text for the trigger.

leadingIconIconData?

Optional. Optional leading icon for the trigger.

trailingIconIconData?

Optional. Optional trailing icon for the trigger.

builderNakedAccordionTriggerBuilder<T>?

Optional. Custom builder for the trigger.

enabledbool

Optional. Whether the accordion item is interactive.

mouseCursorMouseCursor

Optional. Mouse cursor to use when interactive.

enableFeedbackbool

Optional. Whether to provide platform feedback on interactions.

autofocusbool

Optional. Whether the header should autofocus.

focusNodeFocusNode?

Optional. Focus node associated with the header.

onFocusChangeValueChanged<bool>?

Optional. Called when the header's focus state changes.

onHoverChangeValueChanged<bool>?

Optional. Called when the header's hover state changes.

onPressChangeValueChanged<bool>?

Optional. Called when the header's pressed state changes.

semanticLabelString?

Optional. Semantic label announced for the header.

styleRemixAccordionStyle<T>

Optional. The style configuration for the accordion item.

transitionBuilderWidget Function(Widget, Animation<double>)

Optional. The transition builder for the accordion item.