GestureDetector is not working with Dismissible Widget - flutter

I am trying to get the drag offsets while using Dismissible widget. So tried to wrap it with GestureDetector but its onHorizontalDragStart is not working. Tried the either way ie by putting GestureDetector as child of Dismissible but then Dismissible stopped working.
How to solve this? Thnx...
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
var item = items[index];
return GestureDetector(
onHorizontalDragStart: (e) {
print(e);
},
child: Dismissible(
key: ValueKey(item),
background: Container(
color: Colors.teal,
),
child: ListTile(
title: Text("item $item"),
),
onDismissed: (d) {
items.remove(item);
},
),
);
},
);

Nested Gesture Widgets
The reason you are having this issue is because both of those widgets receive touch input and when you have two widgets that receive touch input, long story short the child wins that battle. Here is the long story. So both of your inputs from your Dismissible and GestureDetector are sent to what is called a GestureArena. There the arena takes into account multiple different factors but the end of the story is the child always wins. You can fix this issue by defining your own RawGestureDetector with its own GestureFactory which will change the way the arena performs.
ListView.builder(
physics: AlwaysScrollableScrollPhysics(),
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
var item = items[index];
return Listener(
child: Dismissible(
key: ValueKey(item),
child: ListTile(
title: Text('This is some text'),
),
onDismissed: (DismissDirection direction) {
items.remove(index);
},
),
onPointerMove: (PointerMoveEvent move) {
if (move.localDelta.dx > 1) {//Ideally this number whould be
//(move.localDelta.dx != 0) but that is too sensitive so you can play with
//this number till you find something you like.
print(move.position);
}
},
);
},
);
I want to give all credit to Nash the author of Flutter Deep Dive: Gestures this is a great article I highly recommend you check it out.

Related

Flutter List View Scrolling Only by Clicking on Edges

I'm Having a List view in my app, and the problem is it does not scroll when touching the middle of the list view but only when touching the edges of the list.
and here is my View Code.
Widget build(BuildContext context) {
const horizontalPadding = EdgeInsets.symmetric(horizontal: 10);
final controller = Get.put(UnitsListController());
return Scaffold(
appBar: AppBar(
),
body: Padding(
padding: horizontalPadding,
child: GetX<UnitsListController>(
builder: (controller) {
return controller.isBusy.value
?const Center(
child: CircularProgressIndicator(
backgroundColor: Colors.grey,
color: Colors.blue,
strokeWidth: 5,
),
): ListView.builder(
shrinkWrap: true,
itemCount: controller.unitsList.length,
itemBuilder: (context, index) {
var item = controller.unitsList[index];
String address ='${item.country},${item.state},${item.area},${item.block},${item.plot},'
'${item.lane},${item.buildingName},${item.buildingNumber}';
return GestureDetector(
onTap: () {
controller.selectedUnit = item;
controller.onUnitTap();
},
child: AppUnitCard(
type: item.type,
address: address,
rooms: item.roomsNum??0,
rent: item.rent,
bathrooms: item.bathsNum,
space: item.unitSpace,
),
);
},
);
}),
),
);
}
Note
that i was wrapping the Scaffold body with a SingleChildScrollView and removed it both ways it didn't work.
Wrap the listview with a SingleChildScrollView and add NeverScrollableScrollPhysics to the listview
SingleChildScrollView (
child : ListView.builder(
shrinkWrap: true,
physics : NeverScrollableScrollPhysics()
)
),
ListView doesn't scroll when wrapped by Column & SingleChildScrollView on all the browsers on Android. For further details and code examples check the link:
https://github.com/flutter/flutter/issues/80794#issuecomment-823961805
If this link didn't help you can find more information on this link:
All solutions for the problem

NestedScrollView No Scroll when List is Short with Float SliverAppBar

I am having a ListView, that is required to use pull to refresh function, and a floating header is needed on the design. Therefore I have tried to use the NestedScrollView to fulfill the task. However, I find that even if the list is short, as the SliverAppBar is floating, the body is always a bit longer to make the list able to scroll to hide the app bar which is not what expected on the our design flow.
The design flow considered is to
pin the appbar (not able to scroll) when the body is shorter than the available space
able to float app bar when list body is longer than the available space
I have tried to listen the scroll notification, but it seems not work as what i want to obtain. Please give me some suggestions if there is any way can solve this problem. Thank you!
The following is the code so far i have tried:
return Scaffold(
body: Builder(
builder: (context, ) {
return SafeArea(
child: NotificationListener<ScrollNotification>(
onNotification: (info){
debugPrint('scroll = ${info.metrics.pixels}');
bool isFloat = info.metrics.pixels>57.0;
if(_isFloat!=isFloat){
_isFloat= isFloat;
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
setState((){});
});
}
return true;
},
child: NestedScrollView(
floatHeaderSlivers: false,
headerSliverBuilder: (context, isInnerScroll) {
return [
SliverAppBar(
title: Text('Demo'),
floating: _isFloat,
pinned: !_isFloat,
),
];
},
body: SmartRefresher(
controller: _refreshController,
onRefresh: () {
Future.delayed(Duration(milliseconds: 1000), () {
_refreshController.refreshCompleted();
});
},
child: ListView.builder(
itemCount: 5,
itemBuilder: (context, index) {
return ListTile(
title: Text('$index'),
);
},
),
),
),
),
);
}
),
);

Flutter. Multiple heroes share same tag. ListView with buttons

Can't figure it out how to solve this problem. So, here is the simplest code which represents my problem:
Scaffold(
body: Stack(
children: <Widget>[
...
Scrollbar(
child: ListView.builder(
primary: false,
shrinkWrap: true,
itemCount: _mapBloc?.mapData?.companies?.count ?? 0,
itemBuilder: (context, index) {
final company = _mapBloc?.mapData?.companies?.data[index];
return InkWell(
child: Hero(
tag: company.id,
child: Card(
child: Container(
height: 50,
width: double.infinity,
),
),
),
onTap: () {
Navigator.of(context)
.pushNamed('/company', arguments: company)
.then(
(results) {
if (results is PopWithResults) {
PopWithResults popResult = results;
}
},
);
},
);
},
),
)
],
),
);
And stack trace:
The following assertion was thrown during a scheduler callback:
There are multiple heroes that share the same tag within a subtree.
Within each subtree for which heroes are to be animated (i.e. a PageRoute subtree), each Hero must
have a unique non-null tag.
...
The items count in ListView changes with every database request. If the size is up to 5 widgets in ListView, then clicking works correctly, as soon as the list expands to, for example, 8 elements, I get the error written above. With what it can be connected? I tried to use unique Hero tags, but this does not solve the problem.
I need some advice and hope you can help me. If you need more information, please, write the comment.
Thanks for your attention!

How to pass a listview.builder in flutter_slidable (Slidable)

This is just a simple todo list app. I want to implement a feature which lets me mark a task as complete when swiping right and delete the task when swiping left. This app uses a database and stores an added task in a list of type database (List<TodoItem> Itemlist= <TodoItem>[];).
Is it possible to use flutter_slidable?.
I have tried using Dismissible but i could only get it to delete the task.
List<TodoItem> Itemlist= <TodoItem>[];
SingleChildScrollView(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: Itemlist.length,
itemBuilder: (BuildContext context, int index){
String item = Itemlist[index].toString();
return Dismissible(
key: Key(UniqueKey().toString()),
onDismissed: (direction){
setState((){
deleteItem(Itemlist[index].id, index);
}
);
},
background: Container(
child: Icon(Icons.delete),
color: Colors.red,
alignment: Alignment.centerLeft,
),
child: Itemlist[index],
);
}
),
)
I want to get the same result as below but am unsure on how to pass the listview.builder in Slidable.
Wrap your tile widget with Slidable
Setup "actionPane" and "actions" parameters for Slidable

Refresh indicator doesn't work when list doesn't fill the whole page

I have a page the has a list with refresh indicator. When I have many elements in the list (filling the whole view and more, i.e. I can scroll), the refresh indicator works.
However, when I have only one or two elements in the list (nothing to scroll), then the refresh indicator doesn't work. Is this the expected behaviour? Is there a way to get the indicator to work also for short lists?
I tried wrapping the list view (child of refresh indicator) with a scroll bar but the problem still persists.
Future<void> _onRefresh() async {
_getPeople();
}
#override
Widget build(BuildContext context) {
super.build(context);
return new Container(
child: _items.length == 0
? Center(child: CircularProgressIndicator())
: new RefreshIndicator(
onRefresh: _onRefresh,
child: Scrollbar(
child: new ListView.builder(
controller: controller,
itemBuilder: (context, index) {
return PeopleCard(
People: _items[index],
source: 2,
);
},
itemCount: _items.length,
),
)),
);
}
If the length of the items list is 1 or 2 or any amount that doesn't need scrolling, refresh indicator should also work.
Set the physics to AlwaysScrollableScrollPhysics just like the below code.
new RefreshIndicator(
key: _refreshIndicatorKey,
color: Colors.blue,
onRefresh: (){
setState(() {
_future = fetchPosts(http.Client());
_getAvalailableSlots();
});
},
child: ListView.builder(
physics: AlwaysScrollableScrollPhysics(),
itemCount: data.length,
itemBuilder: (context, i) {
//do something
}
)
)
Case 1:
Use AlwaysScrollableScrollPhysics() in your Listview or SingleChildScrollView.
physics: const AlwaysScrollableScrollPhysics(),
Case 2:
If you want to use other scroll physics, use this way...
physics: BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics(),
),
Case 3:
Make sure you didn't make shrinkWrap, true. ShrinkWrap has to be false like default.
shrinkWrap: true. ❌
shrinkWrap: false. ✅
shrinkWrap: false,
Case 4:
If you have any custom widgets other than a listview, use stack and listview this way...
RefreshIndicator(
onRefresh: () async {
print('refreshing');
},
Stack(
children: [
YourWidget(),
ListView(
physics: const AlwaysScrollableScrollPhysics(),
),
],
),
),
To use with BouncingScrollPhysics:
RefreshIndicator(
onRefresh: () async {
},
child: ListView.builder(
physics: BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
itemBuilder: (context, index) {
return Text("${index}");
}),
To make RefreshIndicator always appear, set physics: const AlwaysScrollableScrollPhysics() for the scrollable child like below.
ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: <Widget>[widget.bodyWidget]
)
If you still have some errors, the below might help.
ListView widget needs to have height value to be "always" scrollable vertically. So wrap it with SizedBox or some other proper widget and set the height.
How to fix the height? Get the height of viewport using MediaQuery.of(context).size.height and, if needed, subtract the value of non-scrollable part.
If you use SingleChildScrollView and it's not working with AlwaysScrollableScrollPhysics(), it is because the child doesn't have height. fix the child's height or just use ListView.
To sum up, the following worked for me.
sizes.bottomNavigationBar and sizes.appBar are the constant variables I made to customize the appbar and navbar.
RefreshIndicator(
onRefresh: widget.onRefresh,
child: SizedBox(
height: MediaQuery.of(context).size.height -
sizes.bottomNavigationBar -
sizes.appBar,
child: ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: <Widget>[widget.bodyWidget]
)),
)
If you're finding any reliability, Refresh indicator does not show up part of the official doc would help.
If you're using ListView inside of Column with Expanded.
Try Removing
shrinkWrap:true