// This file has been literally copied from https://github.com/ubuntu/yaru.dart/blob/main/lib/src/widgets/yaru_expansion_panel.dart // and https://github.com/ubuntu/yaru.dart/blob/main/lib/src/widgets/yaru_expandable.dart // Just want to make some change to it for specific needs instead of contributing to it, because I'm too lazy for that and I think they don't want these change import 'package:flutter/material.dart'; import 'package:yaru/constants.dart'; import 'package:yaru/widgets.dart'; class SectionController extends ChangeNotifier { int? selectedItem; Function()? onChange; SectionController({required this.selectedItem}); void updateSection(int? newSelectedItem) { selectedItem = newSelectedItem; notifyListeners(); if (onChange != null) { onChange!(); } } } class SelectableYaruExpansionPanel extends StatefulWidget { const SelectableYaruExpansionPanel({ super.key, required this.controller, required this.children, this.borderRadius = const BorderRadius.all(Radius.circular(kYaruContainerRadius)), this.border, required this.headers, this.width, this.height, this.padding, this.margin, this.expandIconPadding = const EdgeInsets.all(10), this.headerPadding = const EdgeInsets.only(left: 20), this.color, this.placeDividers = true, this.expandIcon, this.shrinkWrap = true, this.scrollPhysics = const ClampingScrollPhysics(), this.collapseOnExpand = true, }) : assert(headers.length == children.length); final SectionController controller; final List children; final List headers; final BorderRadius borderRadius; final BoxBorder? border; final double? width; final double? height; final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? margin; final EdgeInsetsGeometry expandIconPadding; final EdgeInsetsGeometry headerPadding; final Color? color; final bool placeDividers; final Widget? expandIcon; final bool shrinkWrap; final ScrollPhysics scrollPhysics; final bool collapseOnExpand; @override State createState() => _SelectableYaruExpansionPanelState(); } class _SelectableYaruExpansionPanelState extends State { late List _expandedStore; @override void initState() { super.initState(); widget.controller.onChange = widget.collapseOnExpand ? () { if (widget.controller.selectedItem != null) { _expandedStore[widget.controller.selectedItem!]; for (var n = 0; n < _expandedStore.length; n++) { if (_expandedStore[n]) { setState(() => _expandedStore[n] = false); } } } else { for (var n = 0; n < _expandedStore.length; n++) { setState(() => _expandedStore[n] = false); } } } : null; _expandedStore = List.generate(widget.children.length, (index) => false); } @override Widget build(BuildContext context) { assert(widget.children.length == widget.headers.length); if (widget.controller.selectedItem != null) { _expandedStore[widget.controller.selectedItem!] = true; } return YaruBorderContainer( border: widget.border, borderRadius: widget.borderRadius, color: widget.color, width: widget.width, height: widget.height, padding: widget.padding, margin: widget.margin, child: widget.placeDividers ? ListView.separated( shrinkWrap: widget.shrinkWrap, physics: widget.scrollPhysics, itemCount: widget.children.length, itemBuilder: _itemBuilder, separatorBuilder: (context, index) { if (index != widget.children.length - 1) { return const Padding( padding: EdgeInsets.symmetric(vertical: 1), child: Divider(), ); } else { return const SizedBox.shrink(); } }, ) : ListView.builder( shrinkWrap: widget.shrinkWrap, physics: widget.scrollPhysics, itemCount: widget.children.length, itemBuilder: _itemBuilder, ), ); } Widget? _itemBuilder(context, index) { return YaruExpandable( expandIconPadding: widget.expandIconPadding, isExpanded: _expandedStore[index], header: Padding( padding: widget.headerPadding, child: widget.headers[index], ), child: widget.children[index], ); } } const _kAnimationDuration = Duration(milliseconds: 250); const _kAnimationCurve = Curves.easeInOutCubic; class YaruExpandable extends StatefulWidget { const YaruExpandable({ super.key, required this.header, this.expandIconPadding = EdgeInsets.zero, required this.child, this.collapsedChild, this.gapHeight = 4.0, this.isExpanded = false, this.onChange, }); final Widget header; final EdgeInsetsGeometry expandIconPadding; final Widget child; final Widget? collapsedChild; final double gapHeight; final bool isExpanded; final ValueChanged? onChange; @override State createState() => _YaruExpandableState(); } class _YaruExpandableState extends State { late bool _isExpanded; @override void initState() { _isExpanded = widget.isExpanded; super.initState(); } @override void didUpdateWidget(YaruExpandable oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.isExpanded != widget.isExpanded) { _isExpanded = widget.isExpanded; } } @override Widget build(BuildContext context) { return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ widget.header, Padding( padding: widget.expandIconPadding, child: const SizedBox( height: 36, width: 36, ), ), ], ), AnimatedCrossFade( firstChild: _buildChild(widget.child), secondChild: widget.collapsedChild != null ? _buildChild(widget.collapsedChild!) : Container(), crossFadeState: _isExpanded ? CrossFadeState.showFirst : CrossFadeState.showSecond, sizeCurve: _kAnimationCurve, duration: _kAnimationDuration, ), ], ); } Widget _buildChild(Widget child) { return Column( children: [ SizedBox(height: widget.gapHeight), child, ], ); } }