为了解决这个问题,我在自定义小部件中创建了一个新的 .dart 文件,并插入了这段代码,它是上面代码的修改副本。
我的代码
ColoredExpansionPanelList.radio(
disabledColor: Colors.black,
children: [
ExpansionPanelRadio(
value: value
headerBuilder: (context, isExpanded) => Container(),
body: Container(),
)
],
),
组件代码
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'dart:math' as math;
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 CustomExpansionPanel {
CustomExpansionPanel({
required this.headerBuilder,
required this.body,
this.isExpanded = false,
this.canTapOnHeader = false,
this.backgroundColor,
});
final ExpansionPanelHeaderBuilder headerBuilder;
final Widget body;
final bool isExpanded;
final bool canTapOnHeader;
final Color? backgroundColor;
}
class CustomExpansionPanelRadio extends CustomExpansionPanel {
CustomExpansionPanelRadio({
required this.value,
required super.headerBuilder,
required super.body,
super.canTapOnHeader,
super.backgroundColor,
});
final Object value;
}
class CustomExpansionPanelList extends StatefulWidget {
const CustomExpansionPanelList({
Key? key,
this.children = const <CustomExpansionPanel>[],
this.expansionCallback,
this.animationDuration = kThemeAnimationDuration,
this.expandedHeaderPadding = _kPanelHeaderExpandedDefaultPadding,
this.dividerColor,
this.color,
this.disabledColor,
this.expandedColor,
this.elevation = 2,
}) : _allowOnlyOnePanelOpen = false,
initialOpenPanelValue = null,
super(key: key);
final Color? color;
final Color? disabledColor;
final Color? expandedColor;
const CustomExpansionPanelList.radio({
Key? key,
this.children = const <CustomExpansionPanelRadio>[],
this.expansionCallback,
this.animationDuration = kThemeAnimationDuration,
this.initialOpenPanelValue,
this.expandedHeaderPadding = _kPanelHeaderExpandedDefaultPadding,
this.dividerColor,
this.color,
this.disabledColor,
this.expandedColor,
this.elevation = 2,
}) : _allowOnlyOnePanelOpen = true,
super(key: key);
final List<CustomExpansionPanel> 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() => _CustomExpansionPanelListState();
}
class _CustomExpansionPanelListState extends State<CustomExpansionPanelList> {
CustomExpansionPanelRadio? _currentOpenPanel;
@override
void initState() {
super.initState();
if (widget._allowOnlyOnePanelOpen) {
assert(_allIdentifiersUnique(),
'All CustomExpansionPanelRadio identifier values must be unique.');
if (widget.initialOpenPanelValue != null) {
_currentOpenPanel = searchPanelByValue(
widget.children.cast<CustomExpansionPanelRadio>(),
widget.initialOpenPanelValue);
}
}
}
@override
void didUpdateWidget(CustomExpansionPanelList oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget._allowOnlyOnePanelOpen) {
assert(_allIdentifiersUnique(),
'All CustomExpansionPanelRadio identifier values must be unique.');
if (!oldWidget._allowOnlyOnePanelOpen) {
_currentOpenPanel = searchPanelByValue(
widget.children.cast<CustomExpansionPanelRadio>(),
widget.initialOpenPanelValue);
}
} else {
_currentOpenPanel = null;
}
}
bool _allIdentifiersUnique() {
final Map<Object, bool> identifierMap = <Object, bool>{};
for (final CustomExpansionPanelRadio child
in widget.children.cast<CustomExpansionPanelRadio>()) {
identifierMap[child.value] = true;
}
return identifierMap.length == widget.children.length;
}
bool _isChildExpanded(int index) {
if (widget._allowOnlyOnePanelOpen) {
final CustomExpansionPanelRadio radioWidget =
widget.children[index] as CustomExpansionPanelRadio;
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 CustomExpansionPanelRadio pressedChild =
widget.children[index] as CustomExpansionPanelRadio;
for (int childIndex = 0;
childIndex < widget.children.length;
childIndex += 1) {
final CustomExpansionPanelRadio child =
widget.children[childIndex] as CustomExpansionPanelRadio;
if (widget.expansionCallback != null &&
childIndex != index &&
child.value == _currentOpenPanel?.value) {
widget.expansionCallback!(childIndex, false);
}
}
setState(() {
_currentOpenPanel = isExpanded ? null : pressedChild;
});
}
}
CustomExpansionPanelRadio? searchPanelByValue(
List<CustomExpansionPanelRadio> panels, Object? value) {
for (final CustomExpansionPanelRadio 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 CustomExpansionPanel child = widget.children[index];
final Widget headerWidget = child.headerBuilder(
context,
_isChildExpanded(index),
);
Widget expandIconContainer = CustomExpandIcon(
color: widget.color,
disabledColor: widget.disabledColor,
expandedColor: widget.expandedColor,
isExpanded: _isChildExpanded(index),
size: 24.0,
padding: EdgeInsets.zero,
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,
),
],
),
),
);
if (_isChildExpanded(index) && index != widget.children.length - 1) {
items.add(MaterialGap(
key: _SaltedKey<BuildContext, int>(context, index * 2 + 1)));
}
}
return MergeableMaterial(
hasDividers: true,
dividerColor: widget.dividerColor,
elevation: widget.elevation,
children: items,
);
}
}
class CustomExpandIcon extends StatefulWidget {
const CustomExpandIcon({
super.key,
this.isExpanded = false,
this.size = 24.0,
required this.onPressed,
this.padding = const EdgeInsets.all(8.0),
this.color,
this.disabledColor,
this.expandedColor,
});
final bool isExpanded;
final double size;
final ValueChanged<bool>? onPressed;
final EdgeInsetsGeometry padding;
final Color? color;
final Color? disabledColor;
final Color? expandedColor;
@override
State<CustomExpandIcon> createState() => _CustomExpandIconState();
}
class _CustomExpandIconState extends State<CustomExpandIcon>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _iconTurns;
static final Animatable<double> _iconTurnTween =
Tween<double>(begin: 0.0, end: 0.5)
.chain(CurveTween(curve: Curves.fastOutSlowIn));
@override
void initState() {
super.initState();
_controller =
AnimationController(duration: kThemeAnimationDuration, vsync: this);
_iconTurns = _controller.drive(_iconTurnTween);
if (widget.isExpanded) {
_controller.value = math.pi;
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
void didUpdateWidget(CustomExpandIcon oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.isExpanded != oldWidget.isExpanded) {
if (widget.isExpanded) {
_controller.forward();
} else {
_controller.reverse();
}
}
}
void _handlePressed() {
widget.onPressed?.call(widget.isExpanded);
}
Color get _iconColor {
if (widget.isExpanded && widget.expandedColor != null) {
return widget.expandedColor!;
}
if (widget.color != null) {
return widget.color!;
}
switch (Theme.of(context).brightness) {
case Brightness.light:
return Colors.black54;
case Brightness.dark:
return Colors.white60;
}
}
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
assert(debugCheckHasMaterialLocalizations(context));
final MaterialLocalizations localizations =
MaterialLocalizations.of(context);
final String onTapHint = widget.isExpanded
? localizations.expandedIconTapHint
: localizations.collapsedIconTapHint;
return Semantics(
onTapHint: widget.onPressed == null ? null : onTapHint,
child: IconButton(
splashRadius: 1,
padding: widget.padding,
iconSize: widget.size,
color: _iconColor,
disabledColor: widget.disabledColor,
onPressed: widget.onPressed == null ? null : _handlePressed,
icon: RotationTransition(
turns: _iconTurns,
child: const Icon(Icons.expand_more),
),
),
);
}
}
this.color
而不是widget.color
从状态中访问它们。 - croxx5f