123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- // 修改了ExpansionPanelList的State的build方法
- // 并删去了ExpansionPane(已经在原文件中定义),防止重定义
- // 原版会在展开的项的上下添加及其难看的鸿沟
- import 'package:flutter/material.dart';
- import 'package:flutter/widgets.dart';
- const double _kPanelHeaderCollapsedHeight = kMinInteractiveDimension;
- const EdgeInsets _kPanelHeaderExpandedDefaultPadding = EdgeInsets.symmetric(
- vertical: 64.0 - _kPanelHeaderCollapsedHeight,
- );
- class _SaltedKey<S, V> extends LocalKey {
- const _SaltedKey(this.salt, this.value);
- final S salt;
- final V value;
- @override
- bool operator ==(Object other) {
- if (other.runtimeType != runtimeType) return false;
- return other is _SaltedKey<S, V> &&
- other.salt == salt &&
- other.value == value;
- }
- @override
- int get hashCode => hashValues(runtimeType, salt, value);
- @override
- String toString() {
- final String saltString = S == String ? "<'$salt'>" : '<$salt>';
- final String valueString = V == String ? "<'$value'>" : '<$value>';
- return '[$saltString $valueString]';
- }
- }
- typedef ExpansionPanelCallback = void Function(int panelIndex, bool isExpanded);
- typedef ExpansionPanelHeaderBuilder = Widget Function(
- BuildContext context, bool isExpanded);
- /// 折叠列表
- class ChznExpansionPanelList extends StatefulWidget {
- const ChznExpansionPanelList({
- Key? key,
- this.children = const <ExpansionPanel>[],
- this.expansionCallback,
- this.animationDuration = kThemeAnimationDuration,
- this.expandedHeaderPadding = _kPanelHeaderExpandedDefaultPadding,
- this.dividerColor,
- this.elevation = 2,
- }) : assert(children != null),
- assert(animationDuration != null),
- _allowOnlyOnePanelOpen = false,
- initialOpenPanelValue = null,
- super(key: key);
- const ChznExpansionPanelList.radio({
- Key? key,
- this.children = const <ExpansionPanelRadio>[],
- this.expansionCallback,
- this.animationDuration = kThemeAnimationDuration,
- this.initialOpenPanelValue,
- this.expandedHeaderPadding = _kPanelHeaderExpandedDefaultPadding,
- this.dividerColor,
- this.elevation = 2,
- }) : assert(children != null),
- assert(animationDuration != null),
- _allowOnlyOnePanelOpen = true,
- super(key: key);
- final List<ExpansionPanel> children;
- final ExpansionPanelCallback? expansionCallback;
- final Duration animationDuration;
- final bool _allowOnlyOnePanelOpen;
- final Object? initialOpenPanelValue;
- final EdgeInsets expandedHeaderPadding;
- final Color? dividerColor;
- final double elevation;
- @override
- State<StatefulWidget> createState() => _ExpansionPanelListState();
- }
- class _ExpansionPanelListState extends State<ChznExpansionPanelList> {
- ExpansionPanelRadio? _currentOpenPanel;
- @override
- void initState() {
- super.initState();
- if (widget._allowOnlyOnePanelOpen) {
- assert(_allIdentifiersUnique(),
- 'All ExpansionPanelRadio identifier values must be unique.');
- if (widget.initialOpenPanelValue != null) {
- _currentOpenPanel = searchPanelByValue(
- widget.children.cast<ExpansionPanelRadio>(),
- widget.initialOpenPanelValue);
- }
- }
- }
- @override
- void didUpdateWidget(ChznExpansionPanelList oldWidget) {
- super.didUpdateWidget(oldWidget);
- if (widget._allowOnlyOnePanelOpen) {
- assert(_allIdentifiersUnique(),
- 'All ExpansionPanelRadio identifier values must be unique.');
- if (!oldWidget._allowOnlyOnePanelOpen) {
- _currentOpenPanel = searchPanelByValue(
- widget.children.cast<ExpansionPanelRadio>(),
- widget.initialOpenPanelValue);
- }
- } else {
- _currentOpenPanel = null;
- }
- }
- bool _allIdentifiersUnique() {
- final Map<Object, bool> identifierMap = <Object, bool>{};
- for (final ExpansionPanelRadio child
- in widget.children.cast<ExpansionPanelRadio>()) {
- identifierMap[child.value] = true;
- }
- return identifierMap.length == widget.children.length;
- }
- bool _isChildExpanded(int index) {
- if (widget._allowOnlyOnePanelOpen) {
- final ExpansionPanelRadio radioWidget =
- widget.children[index] as ExpansionPanelRadio;
- return _currentOpenPanel?.value == radioWidget.value;
- }
- return widget.children[index].isExpanded;
- }
- void _handlePressed(bool isExpanded, int index) {
- widget.expansionCallback?.call(index, isExpanded);
- if (widget._allowOnlyOnePanelOpen) {
- final ExpansionPanelRadio pressedChild =
- widget.children[index] as ExpansionPanelRadio;
- // If another ExpansionPanelRadio was already open, apply its
- // expansionCallback (if any) to false, because it's closing.
- for (int childIndex = 0;
- childIndex < widget.children.length;
- childIndex += 1) {
- final ExpansionPanelRadio child =
- widget.children[childIndex] as ExpansionPanelRadio;
- if (widget.expansionCallback != null &&
- childIndex != index &&
- child.value == _currentOpenPanel?.value)
- widget.expansionCallback!(childIndex, false);
- }
- setState(() {
- _currentOpenPanel = isExpanded ? null : pressedChild;
- });
- }
- }
- ExpansionPanelRadio? searchPanelByValue(
- List<ExpansionPanelRadio> panels, Object? value) {
- for (final ExpansionPanelRadio panel in panels) {
- if (panel.value == value) return panel;
- }
- return null;
- }
- @override
- Widget build(BuildContext context) {
- assert(
- kElevationToShadow.containsKey(widget.elevation),
- 'Invalid value for elevation. See the kElevationToShadow constant for'
- ' possible elevation values.',
- );
- final List<MergeableMaterialItem> items = <MergeableMaterialItem>[];
- for (int index = 0; index < widget.children.length; index += 1) {
- // if (_isChildExpanded(index) && index != 0 && !_isChildExpanded(index - 1))
- // items.add(MaterialGap(key: _SaltedKey<BuildContext, int>(context, index * 2 - 1)));
- final ExpansionPanel child = widget.children[index];
- final Widget headerWidget = child.headerBuilder(
- context,
- _isChildExpanded(index),
- );
- Widget expandIconContainer = Container(
- margin: const EdgeInsetsDirectional.only(end: 8.0),
- child: ExpandIcon(
- isExpanded: _isChildExpanded(index),
- padding: const EdgeInsets.all(16.0),
- onPressed: !child.canTapOnHeader
- ? (bool isExpanded) => _handlePressed(isExpanded, index)
- : null,
- ),
- );
- if (!child.canTapOnHeader) {
- final MaterialLocalizations localizations =
- MaterialLocalizations.of(context);
- expandIconContainer = Semantics(
- label: _isChildExpanded(index)
- ? localizations.expandedIconTapHint
- : localizations.collapsedIconTapHint,
- container: true,
- child: expandIconContainer,
- );
- }
- Widget header = Row(
- children: <Widget>[
- Expanded(
- child: AnimatedContainer(
- duration: widget.animationDuration,
- curve: Curves.fastOutSlowIn,
- margin: _isChildExpanded(index)
- ? widget.expandedHeaderPadding
- : EdgeInsets.zero,
- child: ConstrainedBox(
- constraints: const BoxConstraints(
- minHeight: _kPanelHeaderCollapsedHeight),
- child: headerWidget,
- ),
- ),
- ),
- expandIconContainer,
- ],
- );
- if (child.canTapOnHeader) {
- header = MergeSemantics(
- child: InkWell(
- onTap: () => _handlePressed(_isChildExpanded(index), index),
- child: header,
- ),
- );
- }
- items.add(
- MaterialSlice(
- key: _SaltedKey<BuildContext, int>(context, index * 2),
- color: child.backgroundColor,
- child: Column(
- children: <Widget>[
- header,
- AnimatedCrossFade(
- firstChild: Container(height: 0.0),
- secondChild: child.body,
- firstCurve:
- const Interval(0.0, 0.6, curve: Curves.fastOutSlowIn),
- secondCurve:
- const Interval(0.4, 1.0, curve: Curves.fastOutSlowIn),
- sizeCurve: Curves.fastOutSlowIn,
- crossFadeState: _isChildExpanded(index)
- ? CrossFadeState.showSecond
- : CrossFadeState.showFirst,
- duration: widget.animationDuration,
- ),
- ],
- ),
- ),
- );
- }
- return MergeableMaterial(
- hasDividers: true,
- dividerColor: widget.dividerColor,
- elevation: widget.elevation.toInt(),
- children: items,
- );
- }
- }
|