i am facing an issue when i use a stack and add a child that has a gesture detector and position it outside the parent widget using positioned it doesn't detect gestures anymore , if there is a fix please explain it thank you . my code for the problem :
...List<Widget>.generate(
images.length,
(index) {
return Stack(
clipBehavior: Clip.none,
children: [
Container(
height: deviceWidth / 3.5,
width: deviceWidth / 3.5,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10)),
child: GestureDetector(
onLongPressStart: (details) {},
onTap: () async {
var imageFile = await pickImage();
if (imageFile != null) {
images[index] = makeImage(
isPlaceholder: false,
imageData: await imageFile.readAsBytes());
setState(
() {},
);
}
},
child: images[index],
),
),
Positioned(
left: -10,
top: -15,
child: Container(
alignment: Alignment.center,
height: 30,
width: 30,
decoration: const ShapeDecoration(
shape: CircleBorder(), color: Colors.red),
child: InkWell(
splashColor: Colors.white,
onTap: () {
images.remove(images[index]);
setState(() {});
},
child: const IgnorePointer(
child: Icon(Icons.delete_forever)),
),
),
)
],
);
},
),
This behaviour is intentional. The framework hitTest
goes from parent to children, and by default widget don't allow child to receive hitTest
when it is outside of the parent's box.
To achieve "outside of box" gesture detection of an overflowed child. You could push the child that would overflow to the Overlay
with OverlayPortal
and position it with LayerLink
.
import 'package:flutter/material.dart';
main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('LayerLink'),
),
body: const Center(child: Home()),
),
);
}
}
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return const Padding(
padding: EdgeInsets.all(20.0),
child: Wrap(
spacing: 10.0,
runSpacing: 10.0,
children: [
MyImage(color: Colors.blue),
MyImage(color: Colors.green),
MyImage(color: Colors.yellow),
MyImage(color: Colors.orange),
MyImage(color: Colors.purple),
MyImage(color: Colors.red),
MyImage(color: Colors.pink),
MyImage(color: Colors.teal),
MyImage(color: Colors.brown),
MyImage(color: Colors.grey),
],
),
);
}
}
class MyImage extends StatefulWidget {
const MyImage({super.key, required this.color});
final Color color;
@override
State<MyImage> createState() => _MyImageState();
}
class _MyImageState extends State<MyImage> {
int count = 0;
final controller = OverlayPortalController();
final link = LayerLink();
@override
void initState() {
super.initState();
controller.show();
}
@override
Widget build(BuildContext context) {
return OverlayPortal(
controller: controller,
child: CompositedTransformTarget(
link: link,
child: Container(
color: widget.color,
height: 100,
width: 100,
alignment: Alignment.center,
child: Text(
"$count",
style: const TextStyle(fontSize: 30),
),
),
),
overlayChildBuilder: (context) {
return Center(
child: CompositedTransformFollower(
link: link,
followerAnchor: Alignment.center,
showWhenUnlinked: false,
child: Container(
alignment: Alignment.center,
height: 30,
width: 30,
decoration: const ShapeDecoration(shape: CircleBorder(), color: Colors.red),
child: InkWell(
splashColor: Colors.white,
onTap: () {
setState(() {
count++;
});
},
child: const IgnorePointer(child: Icon(Icons.delete_forever)),
),
),
),
);
},
);
}
}