Skip to content

Add drag handle to CupertinoSheet - #179962

Merged
auto-submit[bot] merged 6 commits into
flutter:masterfrom
MitchellGoodwin:sheet-grabber
Jan 8, 2026
Merged

auto-submit[bot] merged 6 commits into
flutter:masterfrom
MitchellGoodwin:sheet-grabber

Conversation

@MitchellGoodwin

@MitchellGoodwin MitchellGoodwin commented Dec 16, 2025 •

Copy link
Copy Markdown
Contributor

Fixes #179358.

Adds the option to add a native styled drag handle to the top of a Cupertino sheet through the showDragHandle property. By default this is false, the same way it is for the native SwiftUI sheet.

Styling for the handle is based off of native versions of the handle, and Apple's Figma files.

From this PR:
Screenshot 2025-12-16 at 12 53 06 PM

From the iOS Maps app (this sheet has a slight transparency)
Screenshot 2025-12-16 at 12 53 15 PM

From the Figma files:
Screenshot 2025-12-16 at 12 53 36 PM

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

@github-actions github-actions Bot added framework flutter/packages/flutter repository. See also f: labels. a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) p: cupertino_ui cupertino_ui package in flutter/packages labels Dec 16, 2025

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds a drag handle to CupertinoSheet. The implementation looks good overall. I've added a couple of comments to improve the code. One is a minor documentation fix. The other is a more significant refactoring of the _dragHandleBuilder method to improve its robustness and maintainability by replacing magic numbers with constants, using Padding instead of a MediaQuery hack, and removing commented-out code.

I am having trouble creating individual review comments. Click here to see my feedback.

packages/flutter/lib/src/cupertino/sheet.dart (582-619)

high

This method has a few areas for improvement:

  1. Commented-out code: There's a block of commented-out code that should be removed.
  2. Magic numbers: The dimensions and padding for the drag handle are hardcoded. It's better to define them as named constants for clarity and maintainability.
  3. Content padding: Using MediaQuery with viewInsets to push down the content is fragile, as not all widgets respect viewInsets. A Padding widget is a more robust way to create space for the drag handle.
  4. Incorrect const: The const keyword on Align is incorrect because its child widget tree contains non-constant values.

Here's a suggested refactoring that addresses these points. You could define the constants at the top of the file or as static members of the class.

  Widget _dragHandleBuilder(BuildContext context) {
    if (!showDragHandle) {
      return builder(context);
    }

    const double dragHandleSheetVerticalInset = 30.0;
    const double dragHandleTopPadding = 5.0;
    const double dragHandleHeight = 5.0;
    const double dragHandleWidth = 36.0;
    const double dragHandleBorderRadius = 18.0;

    return Stack(
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.only(top: dragHandleSheetVerticalInset),
          child: builder(context),
        ),
        Align(
          alignment: Alignment.topCenter,
          child: Semantics(
            properties: const SemanticsProperties(role: SemanticsRole.dragHandle),
            child: Padding(
              padding: const EdgeInsets.only(top: dragHandleTopPadding),
              child: DecoratedBox(
                decoration: ShapeDecoration(
                  shape: RoundedSuperellipseBorder(
                    borderRadius: const BorderRadius.all(Radius.circular(dragHandleBorderRadius)),
                  ),
                  color: CupertinoColors.tertiaryLabel,
                ),
                child: const SizedBox(height: dragHandleHeight, width: dragHandleWidth),
              ),
            ),
          ),
        ),
      ],
    );
  }

packages/flutter/lib/src/cupertino/sheet.dart (124-126)

medium

There's a small typo ("a an") and the property name is incorrect. It should be showDragHandle. Please also format property names as code using backticks.

/// When `showDragHandle` is set to `true`, then an iOS-style drag handle will be placed
/// at the top of the sheet. If a value is not provided, then it will default to
/// false.

@MitchellGoodwin

Copy link
Copy Markdown
Contributor Author

Regarding bot comments, using viewInsets is intentional, so that content within a SafeArea will move out of the way of the grabber, but other content, like putting a close button inline with the grabber, can still be done.

@github-actions github-actions Bot removed the a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) label Dec 16, 2025
@dkwingsmt
dkwingsmt self-requested a review December 17, 2025 19:23
@MitchellGoodwin
MitchellGoodwin removed the request for review from chunhtai December 17, 2025 21:12
@MitchellGoodwin

Copy link
Copy Markdown
Contributor Author

Originally this PR wrapped the handle with the drag handle SemanticsRole. After talking with @chunhtai offline, that has been removed from this PR. The SemanticsRole calls out that tapping on the drag handle will trigger behavior that is normally triggered by the drag. Currently, nothing happens on tap for the drag handle. The native drag handle will trigger switching between detents, if those are used in the sheet, which is a feature we do not have yet #169832.

Once that issue is added, we will want to conditionally wrap the handle with the SemanticsRole, depending on if detents are used or not. If there is no behavior on tap, we do not want to have a semantics label.

For now, there is nothing to do.

@dkwingsmt dkwingsmt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM otherwise

/// means the sheet takes up only the bottom 10% of the screen. If not provided, defaults
/// to 0.08 (8% of screen height).
///
/// When `showDragHandle` is set to `true`, then an iOS-style drag handle will

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't everything in this widget in iOS style?

Suggested change
/// When `showDragHandle` is set to `true`, then an iOS-style drag handle will
/// When `showDragHandle` is set to `true`, then a drag handle will

Comment on lines +128 to +129
/// be placed at the top of the sheet. If a value is not provided, then it will
/// default to false.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// be placed at the top of the sheet. If a value is not provided, then it will
/// default to false.
/// be placed at the top of the sheet. This flag defaults to false.

Comment thread packages/flutter/test/cupertino/sheet_test.dart
/// Defaults to false.
final bool showDragHandle;

Widget _dragHandleBuilder(BuildContext context) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe

Suggested change
Widget _dragHandleBuilder(BuildContext context) {
Widget _sheetWithDragHandle(BuildContext context) {

return Stack(
children: <Widget>[
MediaQuery(
data: MediaQuery.of(context).copyWith(viewInsets: const EdgeInsets.only(top: 30)),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a const for the 30 and maybe explain how you got it

@MitchellGoodwin

Copy link
Copy Markdown
Contributor Author

Google failure looks unrelated

@dkwingsmt dkwingsmt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@MitchellGoodwin MitchellGoodwin added the autosubmit Merge PR when tree becomes green via auto submit App label Jan 7, 2026
@auto-submit

auto-submit Bot commented Jan 7, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/179962, because - The status or check suite Google testing has failed. Please fix the issues identified (or deflake) before re-applying this label.

@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Jan 7, 2026
@MitchellGoodwin MitchellGoodwin added the autosubmit Merge PR when tree becomes green via auto submit App label Jan 7, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Jan 7, 2026
Merged via the queue into flutter:master with commit 1a86af3 Jan 8, 2026
71 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Jan 8, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jan 8, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jan 8, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jan 8, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jan 8, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jan 9, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jan 9, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jan 9, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jan 9, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jan 10, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jan 10, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Jan 10, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

framework flutter/packages/flutter repository. See also f: labels. p: cupertino_ui cupertino_ui package in flutter/packages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CupertinoSheet] Add native styled drag handles

2 participants