resfandiari / flutter_side_menu

Flutter's full customizable side menu has been used as a directory for Related Pages, Navigation Items, and more.
https://pub.dev/packages/flutter_side_menu
BSD 3-Clause "New" or "Revised" License
43 stars 22 forks source link

Is Accordion menu possible with this library? #9

Open bybabek opened 11 months ago

bybabek commented 11 months ago

Is Accordion menu possible with this library?

srburton commented 1 week ago

@bybabek

SideMenuData(
                        header: MenuHeaderWidget(
                          controller: _sideMenuController
                        ),
                        items: [
                          SideMenuItemDataTile(
                            title: 'Dashboard',
                            isSelected: false,
                            titleStyle: TextStyle(color: Colors.white, fontWeight: FontWeight.w100),
                            onTap: () => _pageController.jumpToPage(0),
                            icon: const Icon(Icons.dashboard_outlined, color: Colors.white),
                            margin: const EdgeInsetsDirectional.only(
                              top: 30
                            ),
                          ),
                          SideMenuItemDataTile(
                            title: 'Investments',
                            isSelected: false,
                            titleStyle: TextStyle(color: Colors.white, fontWeight: FontWeight.w100),
                            onTap: () => _pageController.jumpToPage(1),
                            icon: const Icon(Icons.stacked_bar_chart_outlined, color: Colors.white)
                          ),
                          SideMenuItemDataAccordion(
                            isCollapsed: _sideMenuController.isCollapsed(),
                            controller: _sideMenuAccordionController,
                            items: [
                              SideMenuItemDataTile(
                              title: 'Sub menu',
                              titleStyle: TextStyle(color: Colors.white),
                              isSelected: false,
                                onTap: (){},
                              )
                            ]
                          ),
                        ]
                      )

class SideMenuAccordionController extends ChangeNotifier {
  bool _isOpened;

  SideMenuAccordionController({bool isInitiallyOpened = false})
      : _isOpened = isInitiallyOpened;

  bool get isOpened => _isOpened;

  void open() {
    if (!_isOpened) {
      _isOpened = true;
      notifyListeners();
    }
  }

  void close() {
    if (_isOpened) {
      _isOpened = false;
      notifyListeners();
    }
  }

  void toggle() {
    _isOpened = !_isOpened;
    notifyListeners();
  }
}

class SideMenuItemDataAccordion extends SideMenuItemDataDivider {
  final List<SideMenuItemData> items;
  final bool isCollapsed;
  final SideMenuAccordionController controller;

  SideMenuItemDataAccordion({
    required this.items,
    required this.isCollapsed,
    required this.controller,
  }) : super(divider: SizedBox()) {
    if (isCollapsed) controller.close();
  }

  @override
  Widget get divider => AnimatedBuilder(
    animation: controller,
    builder: (context, child) {
      return Container(
        decoration: BoxDecoration(
          color: controller.isOpened ? const Color(0xff1b1b1b) : Colors.transparent,
          borderRadius: const BorderRadius.all(Radius.circular(10)),
        ),
        child: Column(
          children: [
            ListTile(
              title: !isCollapsed
                  ? const Text(
                'My Submenu',
                style: TextStyle(color: Colors.white, fontSize: 14),
                maxLines: 1,
                softWrap: true,
                textScaler: TextScaler.noScaling,
                overflow: TextOverflow.fade,
              )
                  : null,
              leading: const Icon(Icons.monetization_on_outlined, color: Colors.white),
              contentPadding: const EdgeInsets.symmetric(horizontal: 10),
              trailing: expandControlWidget,
            ),
            subItemsWidget,
          ],
        ),
      );
    },
  );

  Widget? get expandControlWidget {
    if (!isCollapsed) {
      return IconButton(
        icon: Icon(controller.isOpened ? Icons.expand_less : Icons.expand_more),
        onPressed: controller.toggle,
      );
    }
    return null;
  }

  Widget get subItemsWidget {
    if (controller.isOpened && !isCollapsed) {
      return Column(
        children: items.map((subItem) {
          if (subItem is SideMenuItemDataTile) {
            return Padding(
              padding: const EdgeInsets.symmetric(horizontal: 33),
              child: SideMenuTile(item: subItem),
            );
          } else if (subItem is SideMenuItemDataTitle) {
            return Padding(
              padding: subItem.padding,
              child: Text(
                subItem.title,
                style: subItem.titleStyle,
                textAlign: subItem.textAlign,
              ),
            );
          } else if (subItem is SideMenuItemDataDivider) {
            return Padding(
              padding: subItem.padding,
              child: subItem.divider,
            );
          }
          return SizedBox.shrink();
        }).toList(),
      );
    }
    return SizedBox();
  }
}

class SideMenuTile extends StatelessWidget {
  final SideMenuItemDataTile item;

  const SideMenuTile({Key? key, required this.item}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(item.title ?? "",
        style: TextStyle(color: Colors.white, fontSize: 14),
        maxLines: 1,
        softWrap: true,
        textScaler: TextScaler.noScaling,
        overflow: TextOverflow.fade),
      leading: item.icon,
      onTap: item.onTap,
      selected: item.isSelected,
      titleTextStyle: item.titleStyle,
    );
  }
}