Skip to content

Commit 5e6d2db

Browse files
button: Replace GestureDetector with Listener for AnimatedScaleOnTap
Fixes #1953.
1 parent fa947a1 commit 5e6d2db

File tree

3 files changed

+48
-14
lines changed

3 files changed

+48
-14
lines changed

lib/widgets/button.dart

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:flutter/gestures.dart';
12
import 'package:flutter/material.dart';
23

34
import 'color.dart';
@@ -159,7 +160,7 @@ class ZulipWebUiKitButton extends StatelessWidget {
159160

160161
final labelColor = _labelColor(designVariables);
161162

162-
return AnimatedScaleOnTap(
163+
return AnimatedScaleOnPrimaryPointerDown(
163164
scaleEnd: 0.96,
164165
duration: Duration(milliseconds: 100),
165166
child: TextButton.icon(
@@ -273,10 +274,10 @@ class ZulipIconButton extends StatelessWidget {
273274
}
274275
}
275276

276-
/// Apply [Transform.scale] to the child widget when tapped, and reset its scale
277-
/// when released, while animating the transitions.
278-
class AnimatedScaleOnTap extends StatefulWidget {
279-
const AnimatedScaleOnTap({
277+
/// Apply [Transform.scale] to the child widget on primary pointer-down,
278+
/// and reset its scale on -up or -cancel, with animated transitions.
279+
class AnimatedScaleOnPrimaryPointerDown extends StatefulWidget {
280+
const AnimatedScaleOnPrimaryPointerDown({
280281
super.key,
281282
required this.scaleEnd,
282283
required this.duration,
@@ -292,10 +293,10 @@ class AnimatedScaleOnTap extends StatefulWidget {
292293
final Widget child;
293294

294295
@override
295-
State<AnimatedScaleOnTap> createState() => _AnimatedScaleOnTapState();
296+
State<AnimatedScaleOnPrimaryPointerDown> createState() => _AnimatedScaleOnPrimaryPointerDownState();
296297
}
297298

298-
class _AnimatedScaleOnTapState extends State<AnimatedScaleOnTap> {
299+
class _AnimatedScaleOnPrimaryPointerDownState extends State<AnimatedScaleOnPrimaryPointerDown> {
299300
double _scale = 1;
300301

301302
void _changeScale(double scale) {
@@ -306,11 +307,13 @@ class _AnimatedScaleOnTapState extends State<AnimatedScaleOnTap> {
306307

307308
@override
308309
Widget build(BuildContext context) {
309-
return GestureDetector(
310+
return Listener(
310311
behavior: HitTestBehavior.translucent,
311-
onTapDown: (_) => _changeScale(widget.scaleEnd),
312-
onTapUp: (_) => _changeScale(1),
313-
onTapCancel: () => _changeScale(1),
312+
onPointerDown: (PointerDownEvent pointerDownEvent) {
313+
if((pointerDownEvent.buttons & kPrimaryButton) != 0) _changeScale(widget.scaleEnd);
314+
},
315+
onPointerUp: (_) => _changeScale(1),
316+
onPointerCancel: (_) => _changeScale(1),
314317
child: AnimatedScale(
315318
scale: _scale,
316319
duration: widget.duration,

lib/widgets/home.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ class _NavigationBarButton extends StatelessWidget {
289289
final designVariables = DesignVariables.of(context);
290290
final color = selected ? designVariables.iconSelected : designVariables.icon;
291291

292-
Widget result = AnimatedScaleOnTap(
292+
Widget result = AnimatedScaleOnPrimaryPointerDown(
293293
scaleEnd: 0.875,
294294
duration: const Duration(milliseconds: 100),
295295
child: Material(
@@ -410,7 +410,7 @@ class _MainMenu extends StatelessWidget {
410410
child: Column(children: menuItems)))),
411411
const Padding(
412412
padding: EdgeInsets.symmetric(horizontal: 16),
413-
child: AnimatedScaleOnTap(
413+
child: AnimatedScaleOnPrimaryPointerDown(
414414
scaleEnd: 0.95,
415415
duration: Duration(milliseconds: 100),
416416
child: BottomSheetDismissButton(
@@ -551,7 +551,7 @@ abstract class _MenuButton extends StatelessWidget {
551551

552552
final trailing = buildTrailing(context);
553553

554-
return AnimatedScaleOnTap(
554+
return AnimatedScaleOnPrimaryPointerDown(
555555
duration: const Duration(milliseconds: 100),
556556
scaleEnd: 0.95,
557557
child: ConstrainedBox(

test/widgets/button_test.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,37 @@ void main() {
114114
check(renderObject).size.equals(Size.square(40));
115115
});
116116

117+
group('AnimatedScaleOnPrimaryPointerDown', () {
118+
void checkScale(WidgetTester tester, Finder finder, double expectedScale) {
119+
final scale = tester.widget<AnimatedScale>(finder).scale;
120+
check(scale).equals(expectedScale);
121+
}
122+
123+
testWidgets('Animation happen instantly when pointer down', (tester) async {
124+
addTearDown(testBinding.reset);
125+
126+
await tester.pumpWidget(TestZulipApp(
127+
child: AnimatedScaleOnPrimaryPointerDown(
128+
scaleEnd: 0.95,
129+
duration: Duration(milliseconds: 100),
130+
child: UnconstrainedBox(
131+
child: ZulipIconButton(
132+
icon: ZulipIcons.follow,
133+
onPressed: () {})))));
134+
await tester.pump();
135+
136+
final animatedScaleFinder = find.byType(AnimatedScale);
137+
138+
final gesture = await tester.startGesture(tester.getCenter(find.byType(ZulipIconButton)));
139+
await tester.pump();
140+
checkScale(tester, animatedScaleFinder, 0.95);
141+
142+
await gesture.up();
143+
await tester.pump();
144+
checkScale(tester, animatedScaleFinder, 1.0);
145+
});
146+
});
147+
117148
// TODO test that the touch feedback fills the whole square
118149
});
119150
}

0 commit comments

Comments
 (0)