flutter / flutter/flutter

[iOS] - UiKitView behind a full-screen BottomSheet receives taps on iOS

Open
#176,476 4 comments 1 reaction 0 assignees View on GitHub
a: platform-views customer: silver f: scrolling found in release: 3.35 found in release: 3.37 framework has reproducible steps P2 platform-ios team-ios triaged-ios
Dominant language
Dart
Stars
179k
Forks
31.1k
PR merge metrics
PR metrics pending

Description

### Steps to reproduce

1. Create a Flutter app with a CustomScrollView.

2. Add a SliverAppBar with a UiKitView in actions (e.g., right-side circle button).

3. Add a list below the app bar (e.g., SliverList with multiple items).

4. Add a bottom-aligned row of multiple UiKitViews.

5. Wrap list items with GestureDetector that shows a full-screen ModalBottomSheet (isScrollControlled: true).

6. Run on iOS (Simulator or device).

7. Tap the UiKitView inside the bottom sheet at a location overlapping the UiKitView behind.

### Expected results

No need to listen in the bottom sheet, and some rectangular shapes(Masked UikitView) should not be shown in the bottom sheet.

### Actual results

The UiKitView behind the bottom sheet also receives the tap.

Taps “pass through” the overlaying Flutter bottom sheet.

### Code sample

Main dart

```
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: SliverAppBarDemo(),
);
}
}

class SliverAppBarDemo extends StatelessWidget {
const SliverAppBarDemo({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(
children: [
CustomScrollView(
slivers: [
SliverAppBar(
toolbarHeight: 100,
backgroundColor: Colors.blue,
centerTitle: true,
floating: false,
pinned: false,
expandedHeight: 70,
leading: const SizedBox(width: 50),
title: const Text(
"Header Title",
style: TextStyle(
fontSize: 18,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
actions: [
Container(
margin: const EdgeInsets.only(right: 12),
width: 50,
height: 50,
color: Colors.red,
child: UiKitView(
key: const ValueKey('plusButton_glass_view'),
viewType: "circle_container_view",
creationParamsCodec: const StandardMessageCodec(),
),
),
],
),
SliverPadding(
padding: const EdgeInsetsDirectional.only(bottom: 80),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return GestureDetector(
onTap: () {
showModalBottomSheet(
useSafeArea: true,
context: context,
isScrollControlled: true, // makes full screen
backgroundColor: Colors.amber.shade50,
builder: (context) {
return SizedBox(
height: MediaQuery.of(context).size.height, // full height
child: Column(
children: [
AppBar(
automaticallyImplyLeading: false,
title: Text("Item $index Details"),
backgroundColor: Colors.blue,
),
Expanded(
child: Center(
child: Text(
"Full bottom sheet content for Item $index",
style: const TextStyle(fontSize: 18),
),
),
),
],
),
);
},
);
},
child: Container(
height: 50,
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 16),
color: index.isEven ? Colors.grey[200] : Colors.white,
child: Text("Item $index"),
),
);
},
childCount: 50,
),
),
),

// SliverPadding(
// padding: const EdgeInsetsDirectional.only(bottom: 80),
// sliver: SliverList(
// delegate: SliverChildBuilderDelegate((context, index) {
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Container(
// height: 50,
// alignment: Alignment.centerLeft,
// padding: const EdgeInsets.symmetric(horizontal: 16),
// color: index.isEven ? Colors.grey[200] : Colors.white,
// child: Text("Item $index"),
// ),
// ],
// );
// }, childCount: 50),
// ),
// ),
],
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
color: Colors.pink,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
margin: const EdgeInsets.only(right: 24),
width: 50,
height: 50,
color: Colors.orange,
child: UiKitView(
key: const ValueKey('plusButton_glass_view'),
viewType: "circle_container_view",
creationParamsCodec: const StandardMessageCodec(),
),
),
Container(
margin: const EdgeInsets.only(right: 24),
width: 50,
height: 50,
color: Colors.teal,
child: UiKitView(
key: const ValueKey('plusButton_glass_view'),
viewType: "circle_container_view",
creationParamsCodec: const StandardMessageCodec(),
),
),
Container(
margin: const EdgeInsets.only(right: 24),
width: 50,
height: 50,
color: Colors.greenAccent,
child: UiKitView(
key: const ValueKey('plusButton_glass_view'),
viewType: "circle_container_view",
creationParamsCodec: const StandardMessageCodec(),
),
),
],

),
),
)
],
),
),
);
}
}
```

AppDelgate

```
import Flutter
import UIKit

@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
let factory = PlusButtonViewFactory()
registrar(forPlugin: "circle_container_view_plugin")?
.register(factory, withId: "circle_container_view")


return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
```

ButtonCode
```
class PlusButtonViewFactory: NSObject, FlutterPlatformViewFactory {
func create(
withFrame frame: CGRect,
viewIdentifier viewId: Int64,
arguments args: Any?
) -> FlutterPlatformView {
return SwiftUIViewWrapper(rootView: PlusButtonView(), frame: frame)

}
}

class SwiftUIViewWrapper: NSObject, FlutterPlatformView {
private var hostingController: UIHostingController

init(rootView: T, frame: CGRect) {
hostingController = UIHostingController(rootView: AnyView(rootView))
hostingController.view.frame = frame
hostingController.view.backgroundColor = .clear
}

func view() -> UIView {
return hostingController.view
}
}
struct PlusButtonView: View {

var body: some View {
if #available(iOS 26.0, *) {
GlassEffectContainer {

Button(action: {}) {
Image(systemName: "plus")
.font(.system(size: 20)) // Increase icon size directly
.frame(width: 24, height: 24)
.foregroundColor( .white)
.padding()
}
.frame(width: 48, height: 48)
.background(
Color(hex: 0xFF15_8C5B)
)
.clipShape(Circle())
.glassEffect(
.regular.interactive()
)
}

} else {
// Fallback on earlier versions
}
}
}

extension Color {
init(hex: Int, opacity: Double = 1) {
self.init(
.sRGB,
red: Double((hex >> 16) & 0xff) / 255,
green: Double((hex >> 08) & 0xff) / 255,
blue: Double((hex >> 00) & 0xff) / 255,
opacity: opacity
)
}
}

```

### Screenshots or Video

https://github.com/user-attachments/assets/21770ef7-b32a-48a4-a939-0823711cceb6

### Logs

Logs

```console
[Paste your logs here]
```

### Flutter Doctor output

Doctor output

```console

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel beta, 3.37.0-0.1.pre, on macOS 26.0.1 25A362 darwin-arm64, locale en-IN)
[✓] Android toolchain - develop for Android devices (Android SDK version 36.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 26.0)
[✓] Chrome - develop for the web
[✓] Connected device (4 available)

```

Contributor guide

Open the contributing guide

Research direction

Start with the provided Main dart reproduction and the AppDelegate and ButtonCode platform-view implementations, then run the sample on iOS using the listed Flutter and Xcode versions. Trace the full-screen bottom sheet and overlapping UiKitView interaction; done means taps and rectangular platform-view content do not pass through or appear behind the sheet.

Written by the indexing model from the issue text.

Assessment

Tech stack
dart, ios, swift
Domain
mobile
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.