expandable_panel 0.1.0 copy "expandable_panel: ^0.1.0" to clipboard
expandable_panel: ^0.1.0 copied to clipboard

Expandable and collapsible panels for Flutter with accordion groups, accessibility, keyboard and RTL support, reduced motion and state restoration.

example/lib/main.dart

import 'package:expandable_panel/expandable_panel.dart';
import 'package:flutter/material.dart';

void main() => runApp(const DemoApp());

/// Demo application for `expandable_panel`.
class DemoApp extends StatefulWidget {
  const DemoApp({super.key});

  @override
  State<DemoApp> createState() => _DemoAppState();
}

class _DemoAppState extends State<DemoApp> {
  bool _rtl = false;
  bool _reduceMotion = false;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'expandable_panel',
      restorationScopeId: 'app',
      theme: ThemeData(colorSchemeSeed: Colors.teal),
      darkTheme: ThemeData(
        colorSchemeSeed: Colors.teal,
        brightness: Brightness.dark,
      ),
      builder: (context, child) => MediaQuery(
        data: MediaQuery.of(context).copyWith(disableAnimations: _reduceMotion),
        child: Directionality(
          textDirection: _rtl ? TextDirection.rtl : TextDirection.ltr,
          child: child!,
        ),
      ),
      home: DefaultTabController(
        length: 3,
        child: Scaffold(
          appBar: AppBar(
            title: const Text('expandable_panel'),
            actions: [
              IconButton(
                tooltip: 'Toggle right-to-left',
                icon: const Icon(Icons.format_textdirection_r_to_l),
                isSelected: _rtl,
                onPressed: () => setState(() => _rtl = !_rtl),
              ),
              IconButton(
                tooltip: 'Toggle reduced motion',
                icon: const Icon(Icons.motion_photos_off_outlined),
                isSelected: _reduceMotion,
                onPressed: () => setState(() => _reduceMotion = !_reduceMotion),
              ),
            ],
            bottom: const TabBar(tabs: [
              Tab(text: 'Cards'),
              Tab(text: 'Accordion'),
              Tab(text: 'Custom'),
            ]),
          ),
          body: const TabBarView(children: [
            CardsPage(),
            AccordionPage(),
            CustomPage(),
          ]),
        ),
      ),
    );
  }
}

const _lorem = 'Expandable panels show a short summary and reveal the full '
    'text on demand. The header is a single accessible button: screen readers '
    'announce whether it is expanded, and it responds to Enter and Space. '
    'While the panel is open, its content can grow or shrink and the panel '
    'follows immediately. ';

/// Article cards that scroll into view when expanded.
class CardsPage extends StatelessWidget {
  const CardsPage({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      padding: const EdgeInsets.all(8),
      itemCount: 12,
      itemBuilder: (context, i) => ArticleCard(key: ValueKey(i), index: i),
    );
  }
}

/// One card: header, a two-line summary, and the full text when expanded.
class ArticleCard extends StatelessWidget {
  const ArticleCard({super.key, required this.index});

  final int index;

  @override
  Widget build(BuildContext context) {
    final text = _lorem * (index % 3 + 2);
    final textTheme = Theme.of(context).textTheme;
    return ExpandableNotifier(
      restorationId: 'card-$index',
      child: ScrollOnExpand(
        child: Card(
          clipBehavior: Clip.antiAlias,
          child: ExpandablePanel(
            theme: const ExpandableThemeData(tapBodyToCollapse: true),
            header: Padding(
              padding: const EdgeInsets.all(16),
              child: Text('Article ${index + 1}', style: textTheme.titleMedium),
            ),
            collapsed: Padding(
              padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
              child: Text(
                text,
                maxLines: 2,
                overflow: TextOverflow.ellipsis,
              ),
            ),
            expanded: Padding(
              padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
              child: Text(text),
            ),
          ),
        ),
      ),
    );
  }
}

/// Only one question open at a time.
class AccordionPage extends StatelessWidget {
  const AccordionPage({super.key});

  static const _faq = {
    'Can several panels be open?':
        'Set maxExpanded on ExpandableGroup, or null for no limit.',
    'Does the expanded content keep its state?':
        'Set maintainState: true on the panel. Otherwise it is rebuilt.',
    'Is the expanded content built up front?':
        'No. It is built on first expansion, or use expandedBuilder.',
    'Does it respect reduced motion?':
        'Yes. With MediaQuery.disableAnimations the panel snaps.',
  };

  @override
  Widget build(BuildContext context) {
    return ExpandableGroup(
      child: ListView(
        padding: const EdgeInsets.all(8),
        children: [
          for (final entry in _faq.entries)
            Card(
              clipBehavior: Clip.antiAlias,
              child: ExpandablePanel(
                header: ListTile(title: Text(entry.key)),
                expanded: Padding(
                  padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
                  child: Text(entry.value),
                ),
              ),
            ),
        ],
      ),
    );
  }
}

/// A custom layout built from Expandable and ExpandableButton, driven by an
/// external controller.
class CustomPage extends StatefulWidget {
  const CustomPage({super.key});

  @override
  State<CustomPage> createState() => _CustomPageState();
}

class _CustomPageState extends State<CustomPage> {
  final _controller = ExpandableController();
  int _items = 3;

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

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        ValueListenableBuilder<bool>(
          valueListenable: _controller,
          builder: (context, expanded, _) => SwitchListTile(
            title: const Text('Expanded (controller)'),
            value: expanded,
            onChanged: (value) => _controller.expanded = value,
          ),
        ),
        ExpandableNotifier(
          controller: _controller,
          child: Card(
            clipBehavior: Clip.antiAlias,
            child: Expandable(
              maintainState: true,
              collapsed: ExpandableButton(
                child: ListTile(
                  leading: const Icon(Icons.photo_library_outlined),
                  title: Text('$_items photos'),
                  trailing: const ExpandableIcon(),
                ),
              ),
              expanded: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  for (var i = 0; i < _items; i++)
                    ListTile(
                      leading: const Icon(Icons.photo_outlined),
                      title: Text('Photo ${i + 1}'),
                    ),
                  OverflowBar(
                    alignment: MainAxisAlignment.end,
                    children: [
                      TextButton(
                        onPressed: () => setState(() => _items++),
                        child: const Text('Add photo'),
                      ),
                      const ExpandableButton(
                        child: Padding(
                          padding: EdgeInsets.all(12),
                          child: Text('Close'),
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
        ),
      ],
    );
  }
}
0
likes
160
points
331
downloads

Documentation

API reference

Publisher

verified publisherabdeldjalilchougui.biz

Weekly Downloads

Expandable and collapsible panels for Flutter with accordion groups, accessibility, keyboard and RTL support, reduced motion and state restoration.

Homepage
Repository (GitHub)
View/report issues

Topics

#expandable #accordion #collapsible #widget #accessibility

License

MIT (license)

Dependencies

flutter

More

Packages that depend on expandable_panel