Use onPan GestureDetector inside a SingleChildScrollView
As @PatrickMahomes said this answer (by @Chris) will solve the problem. However, it will only check if the drag is in line with the GestureDetector. So a full solution would be this:
bool _dragOverMap = false;
GlobalKey _pointerKey = new GlobalKey();
_checkDrag(Offset position, bool up) {
if (!up) {
// find your widget
RenderBox box = _pointerKey.currentContext.findRenderObject();
//get offset
Offset boxOffset = box.localToGlobal(Offset.zero);
// check if your pointerdown event is inside the widget (you could do the same for the width, in this case I just used the height)
if (position.dy > boxOffset.dy &&
position.dy < boxOffset.dy + box.size.height) {
// check x dimension aswell
if (position.dx > boxOffset.dx &&
position.dx < boxOffset.dx + box.size.width) {
setState(() {
_dragOverMap = true;
});
}
}
} else {
setState(() {
_dragOverMap = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Scroll Test"),
),
body: new Listener(
onPointerUp: (ev) {
_checkDrag(ev.position, true);
},
onPointerDown: (ev) {
_checkDrag(ev.position, false);
},
child: ListView(
// if dragging over your widget, disable scroll, otherwise allow scrolling
physics:
_dragOverMap ? NeverScrollableScrollPhysics() : ScrollPhysics(),
children: [
ListTile(title: Text("Tile to scroll")),
Divider(),
ListTile(title: Text("Tile to scroll")),
Divider(),
ListTile(title: Text("Tile to scroll")),
Divider(),
// Your widget that you want to prevent to scroll the Listview
Container(
key: _pointerKey, // key for finding the widget
height: 300,
width: double.infinity,
child: FlutterMap(
// ... just as example, could be anything, in your case use the color picker widget
),
),
],
),
),
);
}
}
Comments
-
Joshlucpoll over 1 year
Problem
So I have a GestureDetector widget inside SingleChildScrollView (The scroll view is to accommodate smaller screens). The GestureDetector is listening for pan updates.
When the SingleChildScrollView is "in use" the GestureDetector cannot receive pan updates as the "dragging" input from the user is forwarded to the SingleChildScrollView.
What I want
Make the child GestureDetector have priority over the SingleChildScrollView when dragging on top of the GestureDetector -- but still have functionality of scrolling SingleChildScrollView outside GestureDetector.
Example
If you copy/paste this code into dart pad you can see what I mean. When the gradient container is large the SingleChildScrollView is not active -- you are able to drag the blue box and see the updates in the console. However, once you press the switch button the container becomes smaller and the SingleChildScrollView becomes active. You are now no longer able to get pan updates in the console only able to scroll the container.
Sidenote: It seems that if you drag on the blue box quickly you are able to get drag updates but slowly dragging it just scrolls the container. I'm not sure if that's a bug or a feature but I'm not able to reproduce the same result in my production app.
import 'package:flutter/material.dart'; final Color darkBlue = Color.fromARGB(255, 18, 32, 47); void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue), debugShowCheckedModeBanner: false, home: Scaffold( body: Center( child: MyWidget(), ), ), ); } } class MyWidget extends StatefulWidget { const MyWidget({Key? key}) : super(key: key); @override _MyWidgetState createState() => _MyWidgetState(); } class _MyWidgetState extends State<MyWidget> { bool enabled = false; @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( height: enabled ? 200 : 400, child: SingleChildScrollView( child: Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment(0.8, 0.0), colors: <Color>[Color(0xffee0000), Color(0xffeeee00)], tileMode: TileMode.repeated, ), ), height: 400, width: 200, child: Center( child: GestureDetector( onPanUpdate: (details) => print(details), child: Container( height: 100, width: 100, color: Colors.blue, child: Center( child: Text( "Drag\nme", textAlign: TextAlign.center, ), ), ), ), ), ), ), ), ElevatedButton( onPressed: () => setState(() { enabled = !enabled; }), child: Text("Switch")) ], ); } }
-
PatrickMahomes almost 3 yearsdoes this answer help?
-
Joshlucpoll almost 3 years@PatrickMahomes Thank you -- I bit hacky but seems to do the trick!
-