flutter_slidable icon indicating copy to clipboard operation
flutter_slidable copied to clipboard

Migration from 0.6.0 -> 1.0.0, how to share 1 SlidableController between multiple Slidable widgets

Open mathschy opened this issue 4 years ago • 16 comments

Hello, thank you for this package that is super useful!

I have a question when doing a migration from 0.6.0 to 1.0.0

I have a list of Slidable widgets. Inside each of them, I have a GestureRecognizer so that when the user taps on any of the Slidable in the list, it closes any Slidable (potentially one that is not the one tapped) that was open. In 0.6.0 I achieved this by having all the Slidable widgets in the list share the same SlidableController that I would store at the list level.

Now we cannot pass a SlidableController to a Slidable. Is there a way to achieve this behavior ?

I know we can do Slidable.of(context) but that gives us the SlidableController of the closest Slidable. So I can achieve closing the Slidable when tapping the Slidable. But I want to close any Slidable when tapping on any other Slidable in the list.

Should I use SlidableGestureDetector ?

// INTERNAL USE
// ignore_for_file: public_member_api_docs

class SlidableGestureDetector extends StatefulWidget {
  const SlidableGestureDetector({
    Key? key,
    this.enabled = true,
    required this.controller,
    required this.direction,
    required this.child,
    this.dragStartBehavior = DragStartBehavior.start,
  }) : super(key: key);

Or is it only for internal use ?

Thank you for your help! Have a nice day

mathschy avatar Nov 11 '21 17:11 mathschy

Hello! Oh ok I see. The easiest way is to have a SlidableNotificationListener as explained here and to dispatch a SlidableRatioNotification inside the tap callback of your GestureDetector:

final slidableController = Slidable.of(context);
if (slidableController != null) {
  SlidableRatioNotification(
    tag: tag,
    ratio: slidableController.ratio,
  ).dispatch(
    context,
    slidableController,
  );
}

If you want something more specific, you can mimic the logic inside the notification.dart, but I'll have to add an easier way to get the groupTag from a child.

letsar avatar Nov 12 '21 07:11 letsar

Thank you, I actually tried this (putting the ListView of Slidable in a SlidableNotificationListener, and doing Slidable.of(context) to reach the controller) but it does not work. What it allows me to do is each tile can control its own Slidable. But suppose I open an action pane on the first Slidable in the list, then I tap on the second one in the list. In this context, I get the SlidableController of the second Slidable in the list. So I can't e.g. check if there is any open Slidable in the list. Even though the first Slidable has an opened action pane, I get a ratio of 0 when taping on the second Slidable (because the second Slidable is not opened).

In version 0.6.0 I could make all the Slidable in the list share the same controller so that when tapping on one of them I could e.g.

if (widget.slidableController.activeState != null) {
  widget.slidableController.activeState!.close();
  return;
}

So that would close any opened Slidable in the list. If the user tapped again, the slidable would not be active and thus the if clause would be false and I could continue with the onTap logic (i.e. go to a detail screen).

How could I implement the same behavior in 1.0.0 ?

EDIT: I guess what I would like is a kind of SlidableNotificationListenerController that would be a controller global to all the children Slidable so that I could check if any one of them is opened, then close it, otherwise do custom logic. I guess I could listen for all notifications of all the children and update a boolean value accordingly, and that would allow me to check if any of the Slidable is open. And I could also store a reference to the controller of the opened Slidable so that I could close it if needed. In 0.6.0 I didn't have to implement all this logic though. Is that something you are planning to support ? I am not in a hurry of upgrading. (In my opinion that seems like something that could be part of the library. I may be wrong but at least it was doable in 0.6.0)

mathschy avatar Nov 12 '21 20:11 mathschy

Yes I should add a better way to do this. I have an idea, but it will possibly deprecate the whole SlidableNotificationListener and thus making a 2.0 release, if only I've seen this before. I'll work on it this afternoon, I'll keep you posted!

letsar avatar Nov 13 '21 12:11 letsar

I will soon release a new version and you'll need two things:

  • Instead of SlidableNotificationListener, you can now use SlidableAutoCloseBehavior
  • Dispatch a SlidableAutoClose from your GestureDetector to make it work:
 SlidableGroupNotification.dispatch(
  context,
  SlidableAutoCloseNotification(
    groupTag: '0',
    controller: Slidable.of(context)!,
  ),
);

letsar avatar Nov 13 '21 16:11 letsar

Thanks, that sounds interesting!

Is there a way to test if any of the Slidable in the list is open ? Because my logic of the onTap is: "if any of the slidable is open, close it and don't go to the detail screen. else (i.e. none of the slidable is open), go to the detail screen"

My understanding is you are adding a mechanism to do the "close any opened slidable" part, but there is still a lack of mechanism to do the "if any of the slidable is open" test. (i.e. the equivalent of the if (widget.slidableController.activeState != null) in my previous message)

mathschy avatar Nov 13 '21 16:11 mathschy

Oh ok. So this is very specific I think, we can totally do this with the API but it will require extra work.

The idea is to have a parent widget that keep in memory the controllers of the slidable and to wrap each Slidable's child with a widget that will provide the parent widget, the controllers. Then in parent widget, we can provide whether there are any Slidable open and use this information in the onTap logic.

I'll post you how I would implement it, this evening.

letsar avatar Nov 13 '21 18:11 letsar

OK thank you,

Yes it sounds like something harder to do if we can't reuse the same controller for multiple Slidable widget like we could do in 0.6.0. Is adding a SlidableController as constructor parameter to Slidable widget something no longer possible with the 1.0.0 design ? Because if it is possible, it is then trivial to implement what I want. That being said, maybe you had good reasons to no longer provide this ability to specify a SlidableController as constructor parameter of Slidable. (Overall the 1.0.0 design is much cleaner)

mathschy avatar Nov 13 '21 18:11 mathschy

In 0.6 the controller wasn't really a controller, I redesigned this package with in mind having a real controller capable to act on the Slidable.

But I see what you mean, and it would be simpler if we could pass a SlidableController to the Slidable directly, like for the ListView and its ScrollController. I didn't provide the ability to do so, because the SlidableController is really intended to be used by one Slidable only. Mainly because one Slidable can have only a startActionPane and another only an endActionPane, and it would not make sense in my opinion if they could share the same controller.

That being said, I could also throw an exception if the controller is attached to another Slidable. I need to think about it 🤔 .

letsar avatar Nov 13 '21 20:11 letsar

I see, thanks for the explanations!

OK well in that case, if one controller can't be shared, it won't help me to pass it as an argument to the constructor.

I guess my implementation with 0.6.0 was a bit hacky (passing the same controller to every Slidable) and a cleaner solution would be what you suggested previously: https://github.com/letsar/flutter_slidable/issues/260#issuecomment-968111635

That said, I think the behavior I want is not too much specific, and it would make sense for it to be implemented at the lib level. In fact, I want the same behavior as in e.g. Apple Mail application: if you scroll to the left on one message, it opens an endActionPane. If you tap on any of the button of the action pane, it does something. If you tap anywhere else (on the message with the opened action pane, or any other message) it just closes the action pane and does nothing else.

Would it make sense to implement this behavior in the SlidableNotificationListener ? I imagine with a gesture detector that listen to taps, and handle them to close any opened action pane if any is opened, and pass the tap along to child gesture detectors if no action pane is opened. (I genuinely don't know if that's possible). Does it make sense ?

mathschy avatar Nov 13 '21 20:11 mathschy

You convinced me, it's indeed the default behavior in iOS (in Reminder app also). I'll see how I can make it easy!

letsar avatar Nov 13 '21 20:11 letsar

Hi @mathschy, I pushed this feature on master.

From your side, you just have to add SlidableAutoCloseBehavior above the Slidable widgets (see main_demo.dart) and the feature you requested will be automatically available. You can disable it by setting its closeWhenTapped parameter to false.

I'm not completely happy with my implementation, it's way too complicated I think. I'll take some time before publishing a new version on pub with that feature, to think if I can simplify it. If you have any ideas, let me know 😉 .

In the meantime, can you test it and tell me if there are bugs or edge cases I didn't think about? It would be really helping.

letsar avatar Nov 14 '21 20:11 letsar

Hi thank you for the update! Sorry for the delay, I just tested with SlidableAutoCloseBehavior, it has the behavior I expected, and I could not find any bug. I will let you know if I find one with more usage, but so far so good! (disclaimer: I did not test with the parameters set to false)

As for the implementation, I don't know how to make it simpler sorry. At least from a user perspective the API looks simple.

I think I will use the commit

flutter_slidable:
    git:
      url: git://github.com/letsar/flutter_slidable.git
      ref: 9d398ed

in my pubspec meanwhile

Have a nice day

mathschy avatar Nov 20 '21 16:11 mathschy

so, when will next release?

lionnner avatar Nov 22 '21 10:11 lionnner

Thanks @mathschy. I'll make a new release soon then :-)

letsar avatar Nov 24 '21 16:11 letsar

Thanks for this! We're waiting for the new release as well :) Your package is awesome!

tigrenok00 avatar Dec 09 '21 07:12 tigrenok00

@letsar is this released?

pushpendraKh avatar Dec 13 '21 06:12 pushpendraKh