forked from firka/flutter
[Reland] Introduce ChipAnimationStyle to override default chips animations durations (#149876)
Reland - https://github.com/flutter/flutter/pull/149245 --- fixes [Custom backgroundColor on Chip makes it flicker between state transitions](https://github.com/flutter/flutter/issues/146730) ### Example preview  https://github.com/flutter/flutter/assets/48603081/a4949ce7-f38b-4251-8201-ecc570ec447c
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Flutter code sample for [ChipAttributes.chipAnimationStyle].
|
||||
|
||||
void main() => runApp(const ChipAnimationStyleExampleApp());
|
||||
|
||||
class ChipAnimationStyleExampleApp extends StatelessWidget {
|
||||
const ChipAnimationStyleExampleApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: ChipAnimationStyleExample(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChipAnimationStyleExample extends StatefulWidget {
|
||||
const ChipAnimationStyleExample({super.key});
|
||||
|
||||
@override
|
||||
State<ChipAnimationStyleExample> createState() =>
|
||||
_ChipAnimationStyleExampleState();
|
||||
}
|
||||
|
||||
class _ChipAnimationStyleExampleState extends State<ChipAnimationStyleExample> {
|
||||
bool enabled = true;
|
||||
bool selected = false;
|
||||
bool showCheckmark = true;
|
||||
bool showDeleteIcon = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
FilterChip.elevated(
|
||||
chipAnimationStyle: ChipAnimationStyle(
|
||||
enableAnimation: AnimationStyle(
|
||||
duration: const Duration(seconds: 3),
|
||||
reverseDuration: const Duration(seconds: 1),
|
||||
),
|
||||
),
|
||||
onSelected: !enabled ? null : (bool value) {},
|
||||
disabledColor: Colors.red.withOpacity(0.12),
|
||||
backgroundColor: Colors.amber,
|
||||
label: Text(enabled ? 'Enabled' : 'Disabled'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
enabled = !enabled;
|
||||
});
|
||||
},
|
||||
child: Text(enabled ? 'Disable' : 'Enable'),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
FilterChip.elevated(
|
||||
chipAnimationStyle: ChipAnimationStyle(
|
||||
selectAnimation: AnimationStyle(
|
||||
duration: const Duration(seconds: 3),
|
||||
reverseDuration: const Duration(seconds: 1),
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.amber,
|
||||
selectedColor: Colors.blue,
|
||||
selected: selected,
|
||||
showCheckmark: false,
|
||||
onSelected: (bool value) {},
|
||||
label: Text(selected ? 'Selected' : 'Unselected'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
selected = !selected;
|
||||
});
|
||||
},
|
||||
child: Text(selected ? 'Unselect' : 'Select'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
FilterChip.elevated(
|
||||
chipAnimationStyle: ChipAnimationStyle(
|
||||
avatarDrawerAnimation: AnimationStyle(
|
||||
duration: const Duration(seconds: 2),
|
||||
reverseDuration: const Duration(seconds: 1),
|
||||
),
|
||||
),
|
||||
selected: showCheckmark,
|
||||
onSelected: (bool value) {},
|
||||
label: Text(showCheckmark ? 'Checked' : 'Unchecked'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
showCheckmark = !showCheckmark;
|
||||
});
|
||||
},
|
||||
child:
|
||||
Text(showCheckmark ? 'Hide checkmark' : 'Show checkmark'),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
FilterChip.elevated(
|
||||
chipAnimationStyle: ChipAnimationStyle(
|
||||
deleteDrawerAnimation: AnimationStyle(
|
||||
duration: const Duration(seconds: 2),
|
||||
reverseDuration: const Duration(seconds: 1),
|
||||
),
|
||||
),
|
||||
onDeleted: showDeleteIcon ? () {} : null,
|
||||
onSelected: (bool value) {},
|
||||
label: Text(showDeleteIcon ? 'Deletable' : 'Undeletable'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
showDeleteIcon = !showDeleteIcon;
|
||||
});
|
||||
},
|
||||
child: Text(
|
||||
showDeleteIcon ? 'Hide delete icon' : 'Show delete icon'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_api_samples/material/chip/chip_attributes.chip_animation_style.0.dart' as example;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('ChipAnimationStyle.enableAnimation overrides chip enable animation', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.ChipAnimationStyleExampleApp(),
|
||||
);
|
||||
|
||||
final RenderBox materialBox = tester.firstRenderObject<RenderBox>(
|
||||
find.descendant(
|
||||
of: find.widgetWithText(RawChip, 'Enabled'),
|
||||
matching: find.byType(CustomPaint),
|
||||
),
|
||||
);
|
||||
|
||||
expect(materialBox, paints..rrect(color: const Color(0xffffc107)));
|
||||
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Disable'));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 500)); // Advance enable animation by 500ms.
|
||||
|
||||
expect(materialBox, paints..rrect(color: const Color(0x1f882f2b)));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500)); // Advance enable animation by 500ms.
|
||||
|
||||
expect(materialBox, paints..rrect(color: const Color(0x1ff44336)));
|
||||
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Enable'));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 1500)); // Advance enable animation by 1500ms.
|
||||
|
||||
expect(materialBox, paints..rrect(color: const Color(0xfffbd980)));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 1500)); // Advance enable animation by 1500ms.
|
||||
|
||||
expect(materialBox, paints..rrect(color: const Color(0xffffc107)));
|
||||
});
|
||||
|
||||
testWidgets('ChipAnimationStyle.selectAnimation overrides chip select animation', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.ChipAnimationStyleExampleApp(),
|
||||
);
|
||||
|
||||
final RenderBox materialBox = tester.firstRenderObject<RenderBox>(
|
||||
find.descendant(
|
||||
of: find.widgetWithText(RawChip, 'Unselected'),
|
||||
matching: find.byType(CustomPaint),
|
||||
),
|
||||
);
|
||||
|
||||
expect(materialBox, paints..rrect(color: const Color(0xffffc107)));
|
||||
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Select'));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 1500)); // Advance select animation by 1500ms.
|
||||
|
||||
expect(materialBox, paints..rrect(color: const Color(0xff4da6f4)));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 1500)); // Advance select animation by 1500ms.
|
||||
|
||||
expect(materialBox, paints..rrect(color: const Color(0xff2196f3)));
|
||||
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Unselect'));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 500)); // Advance select animation by 500ms.
|
||||
|
||||
expect(materialBox, paints..rrect(color: const Color(0xfff8e7c3)));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500)); // Advance select animation by 500ms.
|
||||
|
||||
expect(materialBox, paints..rrect(color: const Color(0xffffc107)));
|
||||
});
|
||||
|
||||
testWidgets('ChipAnimationStyle.avatarDrawerAnimation overrides chip checkmark animation', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.ChipAnimationStyleExampleApp(),
|
||||
);
|
||||
|
||||
expect(tester.getSize(find.widgetWithText(RawChip, 'Checked')).width, closeTo(152.6, 0.1));
|
||||
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Hide checkmark'));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 500)); // Advance avatar animation by 500ms.
|
||||
|
||||
expect(tester.getSize(find.widgetWithText(RawChip, 'Unchecked')).width, closeTo(160.9, 0.1));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500)); // Advance avatar animation by 500ms.
|
||||
|
||||
expect(tester.getSize(find.widgetWithText(RawChip, 'Unchecked')).width, closeTo(160.9, 0.1));
|
||||
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Show checkmark'));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(seconds: 1)); // Advance avatar animation by 1sec.
|
||||
|
||||
expect(tester.getSize(find.widgetWithText(RawChip, 'Checked')).width, closeTo(132.7, 0.1));
|
||||
|
||||
await tester.pump(const Duration(seconds: 1)); // Advance avatar animation by 1sec.
|
||||
|
||||
expect(tester.getSize(find.widgetWithText(RawChip, 'Checked')).width, closeTo(152.6, 0.1));
|
||||
});
|
||||
|
||||
testWidgets('ChipAnimationStyle.deleteDrawerAnimation overrides chip delete icon animation', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.ChipAnimationStyleExampleApp(),
|
||||
);
|
||||
|
||||
expect(tester.getSize(find.widgetWithText(RawChip, 'Deletable')).width, closeTo(180.9, 0.1));
|
||||
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Hide delete icon'));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 500)); // Advance delete icon animation by 500ms.
|
||||
|
||||
expect(tester.getSize(find.widgetWithText(RawChip, 'Undeletable')).width, closeTo(204.6, 0.1));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500)); // Advance delete icon animation by 500ms.
|
||||
|
||||
expect(tester.getSize(find.widgetWithText(RawChip, 'Undeletable')).width, closeTo(189.1, 0.1));
|
||||
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Show delete icon'));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(seconds: 1)); // Advance delete icon animation by 1sec.
|
||||
|
||||
expect(tester.getSize(find.widgetWithText(RawChip, 'Deletable')).width, closeTo(176.4, 0.1));
|
||||
|
||||
await tester.pump(const Duration(seconds: 1)); // Advance delete icon animation by 1sec.
|
||||
|
||||
expect(tester.getSize(find.widgetWithText(RawChip, 'Deletable')).width, closeTo(180.9, 0.1));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user