Unverified Commit 1f5fcb74 authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Speed up AnimatedSwitcher. (#17265)

This optimizes the AnimatedSwitcher so that it tags the right widget with its keyed subtree, and avoids rebuilding the transition unnecessarily.

This significantly improves the performance of Chips (which uses AnimatedSwitcher to swap out it's avatar and delete icon children).
parent 8a4db32b
......@@ -302,13 +302,6 @@ class _GalleryHomeState extends State<GalleryHome> with SingleTickerProviderStat
super.dispose();
}
static Widget _animatedSwitcherLayoutBuilder(List<Widget> children) {
return new Stack(
children: children,
alignment: Alignment.center,
);
}
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
......@@ -338,7 +331,6 @@ class _GalleryHomeState extends State<GalleryHome> with SingleTickerProviderStat
duration: _kFrontLayerSwitchDuration,
switchOutCurve: switchOutCurve,
switchInCurve: switchInCurve,
layoutBuilder: _animatedSwitcherLayoutBuilder,
child: _category == null
? const _FlutterLogo()
: new IconButton(
......@@ -358,7 +350,6 @@ class _GalleryHomeState extends State<GalleryHome> with SingleTickerProviderStat
duration: _kFrontLayerSwitchDuration,
switchOutCurve: switchOutCurve,
switchInCurve: switchInCurve,
layoutBuilder: _animatedSwitcherLayoutBuilder,
child: _category != null
? new _DemosPage(_category)
: new _CategoriesPage(
......
......@@ -1328,7 +1328,7 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
}
}
Widget _wrapWithTooltip(Widget child, String tooltip, VoidCallback callback) {
Widget _wrapWithTooltip(String tooltip, VoidCallback callback, Widget child) {
if (child == null || callback == null || tooltip == null) {
return child;
}
......@@ -1343,17 +1343,17 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
return null;
}
return _wrapWithTooltip(
widget.deleteButtonTooltipMessage ?? MaterialLocalizations.of(context)?.deleteButtonTooltip,
widget.onDeleted,
new InkResponse(
onTap: widget.isEnabled ? widget.onDeleted : null,
child: new IconTheme(
data: theme.iconTheme.copyWith(
color: (widget.deleteIconColor ?? chipTheme.deleteIconColor) ?? theme.iconTheme.color,
color: widget.deleteIconColor ?? chipTheme.deleteIconColor,
),
child: widget.deleteIcon,
),
),
widget.deleteButtonTooltipMessage ?? MaterialLocalizations.of(context)?.deleteButtonTooltip,
widget.onDeleted,
);
}
......@@ -1388,42 +1388,43 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
);
},
child: _wrapWithTooltip(
new _ChipRenderWidget(
theme: new _ChipRenderTheme(
label: new DefaultTextStyle(
overflow: TextOverflow.fade,
textAlign: TextAlign.start,
maxLines: 1,
softWrap: false,
style: widget.labelStyle ?? chipTheme.labelStyle,
child: widget.label,
),
avatar: new AnimatedSwitcher(
child: widget.avatar,
duration: _kDrawerDuration,
switchInCurve: Curves.fastOutSlowIn,
),
deleteIcon: new AnimatedSwitcher(
child: _buildDeleteIcon(context, theme, chipTheme),
duration: _kDrawerDuration,
switchInCurve: Curves.fastOutSlowIn,
),
brightness: chipTheme.brightness,
padding: (widget.padding ?? chipTheme.padding).resolve(textDirection),
labelPadding: (widget.labelPadding ?? chipTheme.labelPadding).resolve(textDirection),
showAvatar: hasAvatar,
showCheckmark: widget.showCheckmark,
canTapBody: canTap,
widget.tooltip,
widget.onPressed,
new _ChipRenderWidget(
theme: new _ChipRenderTheme(
label: new DefaultTextStyle(
overflow: TextOverflow.fade,
textAlign: TextAlign.start,
maxLines: 1,
softWrap: false,
style: widget.labelStyle ?? chipTheme.labelStyle,
child: widget.label,
),
value: widget.selected,
checkmarkAnimation: checkmarkAnimation,
enableAnimation: enableAnimation,
avatarDrawerAnimation: avatarDrawerAnimation,
deleteDrawerAnimation: deleteDrawerAnimation,
isEnabled: widget.isEnabled,
avatar: new AnimatedSwitcher(
child: widget.avatar,
duration: _kDrawerDuration,
switchInCurve: Curves.fastOutSlowIn,
),
deleteIcon: new AnimatedSwitcher(
child: _buildDeleteIcon(context, theme, chipTheme),
duration: _kDrawerDuration,
switchInCurve: Curves.fastOutSlowIn,
),
brightness: chipTheme.brightness,
padding: (widget.padding ?? chipTheme.padding).resolve(textDirection),
labelPadding: (widget.labelPadding ?? chipTheme.labelPadding).resolve(textDirection),
showAvatar: hasAvatar,
showCheckmark: widget.showCheckmark,
canTapBody: canTap,
),
widget.tooltip,
widget.onPressed),
value: widget.selected,
checkmarkAnimation: checkmarkAnimation,
enableAnimation: enableAnimation,
avatarDrawerAnimation: avatarDrawerAnimation,
deleteDrawerAnimation: deleteDrawerAnimation,
isEnabled: widget.isEnabled,
),
),
),
),
);
......
......@@ -1029,10 +1029,10 @@ void main() {
platform: TargetPlatform.android,
primarySwatch: Colors.blue,
);
final ChipThemeData chipTheme = themeData.chipTheme;
final ChipThemeData defaultChipTheme = themeData.chipTheme;
bool value = false;
Widget buildApp({
ChipThemeData theme,
ChipThemeData chipTheme,
Widget avatar,
Widget deleteIcon,
bool isSelectable: true,
......@@ -1040,12 +1040,12 @@ void main() {
bool isDeletable: true,
bool showCheckmark: true,
}) {
theme ??= chipTheme;
chipTheme ??= defaultChipTheme;
return _wrapForChip(
child: new Theme(
data: themeData,
child: new ChipTheme(
data: theme,
data: chipTheme,
child: new StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return new RawChip(
showCheckmark: showCheckmark,
......@@ -1054,7 +1054,7 @@ void main() {
avatar: avatar,
deleteIcon: deleteIcon,
isEnabled: isSelectable || isPressable,
shape: theme.shape,
shape: chipTheme.shape,
selected: isSelectable ? value : null,
label: new Text('$value'),
onSelected: isSelectable
......@@ -1085,13 +1085,13 @@ void main() {
DefaultTextStyle labelStyle = getLabelStyle(tester);
// Check default theme for enabled widget.
expect(materialBox, paints..path(color: chipTheme.backgroundColor));
expect(materialBox, paints..path(color: defaultChipTheme.backgroundColor));
expect(iconData.color, equals(const Color(0xde000000)));
expect(labelStyle.style.color, equals(Colors.black.withAlpha(0xde)));
await tester.tap(find.byType(RawChip));
await tester.pumpAndSettle();
materialBox = getMaterialBox(tester);
expect(materialBox, paints..path(color: chipTheme.selectedColor));
expect(materialBox, paints..path(color: defaultChipTheme.selectedColor));
await tester.tap(find.byType(RawChip));
await tester.pumpAndSettle();
......@@ -1100,7 +1100,7 @@ void main() {
await tester.pumpAndSettle();
materialBox = getMaterialBox(tester);
labelStyle = getLabelStyle(tester);
expect(materialBox, paints..path(color: chipTheme.disabledColor));
expect(materialBox, paints..path(color: defaultChipTheme.disabledColor));
expect(labelStyle.style.color, equals(Colors.black.withAlpha(0xde)));
// Apply a custom theme.
......@@ -1108,14 +1108,14 @@ void main() {
const Color customColor2 = const Color(0xdeadbeef);
const Color customColor3 = const Color(0xbeefcafe);
const Color customColor4 = const Color(0xaddedabe);
final ChipThemeData customTheme = chipTheme.copyWith(
final ChipThemeData customTheme = defaultChipTheme.copyWith(
brightness: Brightness.dark,
backgroundColor: customColor1,
disabledColor: customColor2,
selectedColor: customColor3,
deleteIconColor: customColor4,
);
await tester.pumpWidget(buildApp(theme: customTheme));
await tester.pumpWidget(buildApp(chipTheme: customTheme));
await tester.pumpAndSettle();
materialBox = getMaterialBox(tester);
iconData = getIconData(tester);
......@@ -1134,7 +1134,7 @@ void main() {
// Check custom theme with disabled widget.
await tester.pumpWidget(buildApp(
theme: customTheme,
chipTheme: customTheme,
isSelectable: false,
isPressable: false,
isDeletable: true,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment