Skip to content

Instantly share code, notes, and snippets.

@jezell
Created December 23, 2024 17:55
Show Gist options
  • Save jezell/8aca2b2620edff6d4d9ed2599e18e11f to your computer and use it in GitHub Desktop.
Save jezell/8aca2b2620edff6d4d9ed2599e18e11f to your computer and use it in GitHub Desktop.
fix

Below is an example of how you could change the Flutter framework source to make GestureDetector require an explicit HitTestBehavior (or to change its default). Please note this is a breaking change and would require a carefully managed rollout to avoid breaking existing apps.


1. Locate the GestureDetector Source

In the Flutter repo, the relevant code lives in:

flutter/packages/flutter/lib/src/widgets/gesture_detector.dart

2. Remove the Default Value and Make behavior Required

Currently (as of Flutter 3.x+), GestureDetector has something like this (simplified):

class GestureDetector extends StatelessWidget {
  const GestureDetector({
    super.key,
    this.onTap,
    ...
    this.behavior = HitTestBehavior.deferToChild,
    ...
  });

  final GestureTapCallback? onTap;
  ...
  final HitTestBehavior behavior;
  ...

  @override
  Widget build(BuildContext context) {
    // ...
  }
}

To force developers to choose a hit-test behavior explicitly, remove the default value and mark it required. For example:

class GestureDetector extends StatelessWidget {
  const GestureDetector({
    super.key,
    this.onTap,
    ...
    required this.behavior, // <--- Make behavior required
    ...
  });

  final GestureTapCallback? onTap;
  ...
  final HitTestBehavior behavior;

  @override
  Widget build(BuildContext context) {
    // ...
  }
}

What this does

  1. No Default Value
    Removing = HitTestBehavior.deferToChild means you are no longer implicitly telling the system to defer hits to the child. Instead, if a developer doesn’t supply behavior, the code won’t compile, forcing them to specify a behavior.

  2. Required Parameter
    Adding required to the constructor parameter ensures that all code instantiating GestureDetector must provide a HitTestBehavior value (e.g., behavior: HitTestBehavior.translucent or behavior: HitTestBehavior.opaque, etc.).

This is the most “pure” way to force the developer’s hand and avoid the subtle bug you described. However, it is also a breaking change because it will break all existing GestureDetector usages that don’t specify behavior.


3. (Alternative) Change the Default to Translucent (or Opaque)

If you want to simply change the default instead of removing it entirely, you could do:

class GestureDetector extends StatelessWidget {
  const GestureDetector({
    super.key,
    this.onTap,
    ...
    this.behavior = HitTestBehavior.translucent, // or .opaque
    ...
  });

  final GestureTapCallback? onTap;
  ...
  final HitTestBehavior behavior;

  @override
  Widget build(BuildContext context) {
    // ...
  }
}

This would silently alter the default behavior from deferToChild to something else. Taps in padded areas would now register (if you choose translucent or opaque). This approach might cause fewer compile-time errors but more unexpected runtime changes in existing apps.


4. Update Documentation & Tests

After making either of the above changes, also:

  1. Update the Doc Comments in gesture_detector.dart to reflect the new requirement or the new default.
  2. Update the Flutter Tests that cover GestureDetector usage:
    • Search for “GestureDetector” in the packages/flutter/test directory and update any tests that rely on deferToChild being the default.
    • For a required parameter change, you’ll need to fix test code that previously omitted behavior.

5. Consider Upstream Discussion & Migration

Before merging a change like this into the Flutter framework, you’d typically:

  1. Open a GitHub Issue / PR in the Flutter GitHub repo describing your proposed change and rationale.
  2. Discuss with Flutter maintainers to see if they’re open to this as a default.
  3. If accepted, the change would require a major version or a migration guide so existing apps are not caught off-guard by the behavior shift.

Example Diff

Below is an illustrative diff snippet (removing the default and forcing a required behavior). Note that line numbers will differ based on the Flutter version.

--- a/packages/flutter/lib/src/widgets/gesture_detector.dart
+++ b/packages/flutter/lib/src/widgets/gesture_detector.dart
@@ -234,12 +234,12 @@ class GestureDetector extends StatelessWidget {
     ...
   }

-  const GestureDetector({
+  const GestureDetector({
     super.key,
     this.onTapDown,
     this.onTapUp,
     this.onTap,
     this.onTapCancel,
-    this.behavior = HitTestBehavior.deferToChild,
+    required this.behavior,
     ...
   }) : super();

-  final HitTestBehavior behavior;
+  final HitTestBehavior behavior; // now required

   // rest of the code...

Summary

  1. Option A (Most Explicit, Biggest Break)

    • Remove the default (deferToChild), make behavior required.
    • All developers must now consciously choose the hit-test behavior.
    • This is a big breaking change.
  2. Option B (Change the Default)

    • Keep behavior as an optional parameter but default it to HitTestBehavior.translucent or HitTestBehavior.opaque.
    • Fewer compile-time errors, but existing apps will see changes in tappable areas.

Either way, you’ll want to thoroughly discuss and test this change since it can have far-reaching impact on existing apps and libraries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment