-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Description
- Run the attached app
- Tap on the button and quickly drag the pointer outside the bounds of the button
The button will continue rendering in the pressed state. It should instead revert to its original state after the pointer moves beyond the slop region or after the pointer is released.
This behavior regressed with #161731
The button's drag gesture and the ListView's drag gesture are both in the arena. After the pointer is dragged outside the button, the arena does a _resolveInFavorOf, selects the ListView gesture, and rejects the button gesture.
But BaseTapGestureRecognizer.rejectGesture does not cancel the tap because _sentTapDown is false. If the pointer was not inside the button long enough to send a tap down event, then rejectGesture does not send a cancel.
With #161731 the BaseTapGestureRecognizer is sending tap move events to the CupertinoButton. The CupertinoButton transitions to the pressed state based on the move events. If no cancel event is sent, then the button will remain pressed.
Code sample:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: false,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView(
children: <Widget>[
CupertinoButton.filled(
child: const Text('Test'),
onPressed: () => print('onPressed'),
),
],
),
);
}
}