How to avoid scrolling listview when scrolling in map within listview - flutter

I have a ListView, at the top of which I have a map, I want the map to scroll out of view when the ListView is scrolled, but I also want the user to be able to interact with the map. So scrolling should only happen when the user scrolls on the other ListView widgets and not when they scroll on the map, then I want the gesture to be applied directly to the map.
Currently though, when the user scrolls on the map, it scrolls the whole ListView.
I have tried the other suggestion that I have come across on here
In Flutter, how can a child widget prevent the scrolling of its scrollable parent?
I added a GestureDetector as suggested in the answer to the post above wrapping the map container in the sample below, however this just blocked scrolling of both the ListView and the Map when scrolling on the map. Video link https://imgur.com/SeCRzUC
Here is the widget that is returned by my build method. This code depends on the google_maps_flutter plugin.
Container(
height: MediaQuery.of(context).size.height,
child:
ListView.builder(
itemCount: 12 + 1,
itemBuilder: (context, index) {
if (index == 0) return GestureDetector(
onVerticalDragUpdate: (_){},
child: Container(
height: MediaQuery.of(context).size.height / 2,
child: GoogleMap(initialCameraPosition: initalPosition),
),
);
else return ListTile(title: Text("$index"),);
}
)
),
I had hoped the map would capture gestures but it doesn't, the listview which contains it captures all. Could anyone suggest how I could force all gestures for this item in the list to be passed directly to the map, and still have the list scroll when other items in the list are scrolled?

The accepted answer (Eset Mehmet's answer was marked as excepted as of time of writing) is way to complicated and in my case it did not even work! It provided left-to-right scrolling, panning, and scaling, however top-to-bottom scrolling still scrolled the ListView.
A real solution is very simple, since GoogleMap by default has those gesture detectors. You must just specify that gesture detection must be prioritized by GoogleMap and not ListView. This is achieved by giving GoogleMap object an EagerGestureRecognizer in the following way for example.
ListView(
children: <Widget>[
Text('a'),
Text('b'),
GoogleMap(
...,
gestureRecognizers: {
Factory<OneSequenceGestureRecognizer>(
() => EagerGestureRecognizer(),
),
},
),
],
)
In this way, all gestures that happen on or over the GoogleMap object will be prioritized by GoogleMap instead of any other widget.

Edit: campovski 's answer down below is the updated answer.
Depreciated Answer:
Wrap everything with ListView if you want to move out GoogleMap widget from the screen when scrolling.
Override ListView scrolling phyics with GoogleMap gestureRecognizers.
Disable ListView.builder scrolling physics due to conflict between ListView physics.
First import the dependencies:
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
build method:
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: <Widget>[
SizedBox(
height: MediaQuery.of(context).size.height / 2,
child: GoogleMap(
initialCameraPosition:
CameraPosition(target: LatLng(41, 29), zoom: 10),
gestureRecognizers: Set()
..add(
Factory<PanGestureRecognizer>(() => PanGestureRecognizer()))
..add(
Factory<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer()),
)
..add(
Factory<HorizontalDragGestureRecognizer>(
() => HorizontalDragGestureRecognizer()),
)
..add(
Factory<ScaleGestureRecognizer>(
() => ScaleGestureRecognizer()),
),
),
),
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: 12,
itemBuilder: (context, index) {
return ListTile(
title: Text("$index"),
);
},
)
],
),
);
}

Related

Flutter ListView entries cut off when scrolled down

A screen in my app displays a list of "listings" the user has made. In the event there is not enough listings to fill the whole screen, and the user scrolls down, the listing they are scrolling will get cut off instead of being scrolled down.
example before scrolling
example after scrolling
As the user scrolls the list entry downwards I would like it not to disappear. I've tried wrapping the listview.builder in containers / sized boxes to define their size, although this behavior persists. I thought I could also put in some blank entries into the list that wouldn't respond to user clicks but that seems like an inefficient solution. Here is my code:
return Scaffold(
key: listing_home_scaffold_key,
resizeToAvoidBottomInset: false,
backgroundColor: BODY_BACKGROUND_COLOR,
appBar: returnAppBarForHomePages(context, "Listings", widget.local_user, updateUserState,
widget.local_user.usingDefaultImage, widget.local_user.getProfilePicURL, listing_home_scaffold_key),
drawer: settingsDrawer(widget.local_user),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
returnScrollableListView()
]),
);
Widget returnScrollableListView() {
return RefreshIndicator(
child: ListView.builder(
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
itemCount: user_listings.length,
shrinkWrap: true,
padding: const EdgeInsets.only(top: 0),
itemBuilder: (context, index) {
return UserListing(listing: user_listings[index]);
}),
onRefresh: () async {
var cancel_loading_indicator = showLoadingIndicatorAllowClick();
await reQueryData();
cancel_loading_indicator();
},
);
}
Edit: Having the refresh indicator be the direct parent to the listview caused this problem. Moving it up a level so it's instead the parent of the Column fixed this.
Having the refresh indicator be the direct parent to the listview caused this problem. Moving it up a level so it's instead the parent of the Column fixed this.

Keep button fixed in the bottom of screen, but only when the above list height is shorter than the device height

I am developing a Flutter app and would like to keep my button always in the bottom of the screen UNLESS what is in top of it is high enough to make the screen scroll. Examples:
Example A (what is on top of the button does not have height enough to scroll the screen)
Example B (what is on top of the button has height enough to scroll the screen, so the button will just go offscreen and allow the screen to scroll)
So summarizing: the button should be forced to stay at the bottom of the screen when the rest of the list is shorter than the device height, but if the list height is greater than the device height the button should behave normally and stay below the list.
What I tried:
Using a ListView, which will normally scroll if necessary, but I couldn't find a way to send the button to the botton of the screen;
Using a Column. With the help of a Spacer I could make the button go to the bottom of the screen, but a Column will not scroll, and if I add a SingleChildScrollViewto wrap it, the Spacer will no longer work because SingleChildScrollView has the potential to have infinite height;
Thanks.
I got it. The right way of doing this is using LayoutBuilder, ConstrainedBox, IntrinsicHeight and Expanded, like this:
(In this example, _widgets is a list of widgets that I want it to be on top. _buttons is a list of buttons that I want to be in the bottom)
return Scaffold(
appBar: buildAppBar(),
body: LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: IntrinsicHeight(
child: Column(
children: <Widget>[
Expanded(
child: Column(
children: _widgets,
),
),
Column(
children: _buttons,
)
],
),
),
),
);
},
),
);
The documentation says to avoid this, though, as it can be expensive. You can read more about this solution and other details in the documentation.
Stack(
children:[
//Use list widget,
yourList.length<7? Postioned(
bottom :10, margin from bottom
child: //button widget):Container()
])
In listbuilder
ListView.builder(
physics: ScrollPhysics(),
itemCount: yourList+1,
itemBuilder: (BuildContext ctxt, int index) {
if (index== yourList.length) {
return yourList.length >7? //your button widget:Container();
} else {
return //list item}
});

Nested ScrollView inside Slidable panel widget

I have a panel widget that can be dragged vertically in and out from the bottom of the screen. In that panel widget, there is a ListView that is scrollable.
What I'm trying to achieve is, having the panel handle the drag for opening and closing without the nested listview interfering. Once, the panel is open, the listview become scrollable and if the listview is scrolled down while already at the top, the panel handle the gesture instead and closes.
Like so:
I tried to enable/disable scrolling physics on the ListView based on the Panel position but turned out not to be possible that way.
Any ideas ? :)
You can achieve that with DraggableScrollableSheet.
Here is a quick example of how you can use it:
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Center(child: Text('Some content')),
DraggableScrollableSheet(
minChildSize: 0.2,
initialChildSize: 0.2,
builder: (context, scrollController) => Container(
color: Colors.lightBlueAccent,
child: ListView.builder(
controller: scrollController,
itemCount: 20,
itemBuilder: (context, index) => SizedBox(
height: 200,
child: Text('Item $index'),
),
),
),
),
],
),
);
}

Persistent draggable bottom sheet in Flutter

I'm using Rubber library at the moment, do you know an approach without using 3rd parts libraries?
The Bottom Sheet must be persistent (not dismissable, not triggered by any button instead, always displayed) and draggable (It must be expanded and collapsed by dragging gestures)
Perhaps DraggableScrollableSheet could work?
I haven't yet tried it out myself, but maybe you could fiddle with a listview to make it work.
I'm guessing something like having it's child be a listview, and then limit both the max child size and the maximum scroll extent
If you do not care that the bottom sheet must snap to different positions you can use the widget from the following package (snapping_sheet) I made.
Or, if you do not want to use it as a 3rd part library, you can copy the code and use it as your own widget from repository here: Github - Snapping sheet
Use DraggableScrollableSheet. Here's an example:
Stack(
children: [
Container(), //the page under the DraggableScrollableSheet goes here
Container(
height: MediaQuery.of(context).size.height,
child: DraggableScrollableSheet(
builder: (BuildContext context, myscrollController) {
return Container(
color: Colors.blue,
child: ListView.builder(
controller: myscrollController,
itemCount: 40,
itemBuilder:(BuildContext context, int index) {
return ListTile(title: Text('Item $index',
style: TextStyle(color: Colors.black),
));
},
),
);
},
),
),
],),

How can I add extra bottom spacing in ListView?

When using FloatingActionButton in conjunction with a ListView, it hides part of the UI of the ListView entries, as seen in the screenshot.
Is there a way to automatically add extra spacing at the bottom of a ListView so that the + button does not hide any content?
Assuming you are using ListView, there is a very nice and simple solution to this.
You probably do not want to add padding around the ListView itself as that would make some area of your UI unused.
ListView has apadding parameter for exactly this use case. You can essentially add some padding to the bottom of your ListView that is part of the scrollable area. This means that you will only start seeing this padding once you have scrolled all the way to the bottom. This will allow you to drag the last few items above the FloatingActionButton.
To find an appropriate value for the padding, you can make use of kFloatingActionButtonMargin and _kExtendedSizeConstraints, which is not accessible, thus, I will just use 48 like this.
This means that you will want to add the following to your ListView:
ListView(
padding: const EdgeInsets.only(bottom: kFloatingActionButtonMargin + 48),
..
)
Here is a working example:
import 'package:flutter/material.dart';
main() {
runApp(MaterialApp(
home: Scaffold(
body: ListView.builder(
padding: const EdgeInsets.only(bottom: kFloatingActionButtonMargin + 48),
itemCount: 23,
itemBuilder: (context, index) => ListTile(
trailing: Text('$index'),
)),
floatingActionButton: FloatingActionButton(
onPressed: () {},
),
)));
}
Expandable(
ListView.builder(
...
padding: EdgeInsets.only(bottom: 200),
...
)
)