Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to close slider from onWillDismiss? #84

Closed
warriorCoder opened this issue Jul 3, 2019 · 24 comments
Closed

How to close slider from onWillDismiss? #84

warriorCoder opened this issue Jul 3, 2019 · 24 comments

Comments

@warriorCoder
Copy link

I'm using the onWillDismiss to allow the users some choices and want to close the slider when this event is triggered however nothing seems to work. I've used a controller, tried to get the SlideState using Slidable.of and neither of those options work.

@kenthinson
Copy link

kenthinson commented Sep 5, 2019

You can't access Slidable.of from the same context you need to be inside a child context. So add a builder to push yourself one context lower in the stack.

secondaryActions: <Widget>[ Builder( builder: (context) { return IconSlideAction( caption: 'Delete', color: Colors.red, icon: Icons.delete, onTap: () { Slidable.of(context).close(); }, ); }, ), ],

@anthonysette
Copy link

I know this was old but @kenthinson your answer was very helpful, Thanks!

@ykaito21
Copy link

You can't access Slidable.of from the same context you need to be inside a child context. So add a builder to push yourself one context lower in the stack.

secondaryActions: <Widget>[ Builder( builder: (context) { return IconSlideAction( caption: 'Delete', color: Colors.red, icon: Icons.delete, onTap: () { Slidable.of(context).close(); }, ); }, ), ],

@kenthinson, is it possible to use this onWillDismiss? I'm not sure how to do it. as @warriorCoder, if user return false on onWillDismiss, I want to close slider.

@ghost
Copy link

ghost commented May 10, 2020

Any answer for onWillDismiss? Using the build context there returns null.

@kenthinson
Copy link

@anthonysette You are welcome.

@kenthinson
Copy link

@rbb091020 @ykaito21 I see no reason you would need to access the Slidable from inside onWillDismiss. Just return. Can you explain what you are trying to accomplish that I'm missing? Then I could advise you better about an idea.

@ghost
Copy link

ghost commented May 12, 2020

@kenthinson I want my slidable to close after it has been dismissed. Right now, after the user has dismissed it and fills out the form that the dismissible returns, the slidable stays open. It looks really tacky.

Here's the relevant code in my app.

class _BarState extends State<Bar> { final SlidableController slidableController = SlidableController(); @override Widget build(BuildContext context) { return Slidable( direction: Axis.horizontal, controller: slidableController, key: widget.key, dismissal: SlidableDismissal( child: SlidableDrawerDismissal(), onWillDismiss: (actionType) { if (actionType == SlideActionType.primary) { return showDialog<bool>( context: context, builder: (cxt) { slidableController.activeState.close(); return AddExpenseForm(addExpenseHandler: addTransaction,); });

Instead of slidableController.activeState.close(); I've tried Slidable.of(context).close() but it returns the attached error.
Screen Shot 2020-05-12 at 2 20 54 PM

@kenthinson
Copy link

kenthinson commented May 12, 2020

@rbb091020 If the slidable was dismissed it shouldn’t be on the screen anymore. So do you mean close automatically when the dismissal was canceled? A video of what you are trying to explain might help clarify.

@ghost
Copy link

ghost commented May 13, 2020

Here is a quick video of the problem.

As you can see, when the user fills out the form returned from onWillDismiss, the slidable doesn't close automatically.

And yes, that's what I'm trying to do. Sorry, I thought dismissible just meant that you could slide the widget all the way in one direction and an action would happen.

@kenthinson
Copy link

kenthinson commented May 14, 2020

@rbb091020 I see what you mean. I think this is a functionality that needs to be programmed into the base slidable library. The way it's programmed now it's assuming that a full slide to the left or right is a dismissal. AKA remove from the list and screen. We have no easy way to access the Slidable from within the dismissible constructor. @letsar has said he's too busy to work on this project actively now. So forking might be your only option. I messaged him about taking over the project however as he's so busy I don't have any idea when he will respond or if he would be open to that.

@ghost
Copy link

ghost commented May 14, 2020

Ah, I see. Is there no work around that you can think of until then?

@kenthinson
Copy link

kenthinson commented May 14, 2020

@rbb091020 Ah I figured it out. You just create a SlidableController. Make sure all your slidables use it. Then call controller.activeState.close();

Here is an example

import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:flutter/cupertino.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Dismiss Close Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    var controller = SlidableController();
    return Scaffold(
      body: ListView.builder(
          itemCount: 10,
          itemBuilder: (context, index) {
            return Slidable(
              controller: controller,
              key: Key("$index"),
              dismissal: SlidableDismissal(
                child: SlidableDrawerDismissal(),
                onWillDismiss: (actionType) {
                  showCupertinoModalPopup(
                    context: context,
                    builder: (BuildContext context) => CupertinoActionSheet(
                      title: const Text('Choose Options'),
                      message: const Text('Your options are '),
                      actions: <Widget>[
                        CupertinoActionSheetAction(
                          child: const Text('Close the slidable'),
                          onPressed: () {
                            Navigator.pop(context, 'One');
                          },
                        ),
                        CupertinoActionSheetAction(
                          child: const Text('Two'),
                          onPressed: () {
                            Navigator.pop(context, 'Two');
                          },
                        )
                      ],
                    ),
                  ).then((onValue){
                    controller.activeState.close();
                  });
                },
              ),
              actionPane: SlidableDrawerActionPane(),
              actionExtentRatio: 0.25,
              child: Container(
                color: Colors.white,
                child: ListTile(
                  leading: CircleAvatar(
                    backgroundColor: Colors.indigoAccent,
                    child: Text('t'),
                    foregroundColor: Colors.white,
                  ),
                  title: Text('Tile n°'),
                  subtitle: Text('SlidableDrawerDelegate'),
                ),
              ),
              actions: <Widget>[
                IconSlideAction(
                  caption: 'Archive',
                  color: Colors.blue,
                  icon: Icons.archive,
                ),
                IconSlideAction(
                  caption: 'Share',
                  color: Colors.indigo,
                  icon: Icons.share,
                ),
              ],
              secondaryActions: <Widget>[
                IconSlideAction(
                  caption: 'More',
                  color: Colors.black45,
                  icon: Icons.more_horiz,
                ),
                IconSlideAction(
                  caption: 'Delete',
                  color: Colors.red,
                  icon: Icons.delete,
                ),
              ],
            );
          }),
      appBar: AppBar(
        title: Text(widget.title),
      ),
    );
  }
}

@ghost
Copy link

ghost commented May 14, 2020

Still not working... I tried to mimic your example exactly. Can you see where I am making the error?

Edit: Here is the full build method.

class _BarState extends State<Bar> {
  final SlidableController slidableController = SlidableController();
  @override
  Widget build(BuildContext context) {
    return Slidable(
      direction: Axis.horizontal,
      controller: slidableController,
      key: widget.key,
      dismissal: SlidableDismissal(
        child: SlidableDrawerDismissal(),
        onWillDismiss: (actionType) {
          if (actionType == SlideActionType.primary) {
            return showDialog<bool>(
                context: context,
                builder: (cxt) {
                  return AddExpenseForm(addExpenseHandler: addTransaction,);
                }).then((onValue){
                  slidableController.activeState.close();
                  return null;
                });
          } else {
            return showDialog<bool>(
              context: context,
              builder: (context) {
                return AreYouSureDelete(
                    widget.category, widget.removeCategoryHandler);
              },
            );
          }
        },
      ),
      actionPane: SlidableBehindActionPane(),
      actionExtentRatio: 0.25,
      child: CategoryItem(category: widget.category),
      actions: <Widget>[
        IconSlideAction(
          caption: "Add Expense",
          color: Theme.of(context).accentColor,
          icon: Icons.add_circle_outline,
          onTap: () {
            return showDialog<bool>(
              context: context,
              builder: (context) {
                return AddExpenseForm(addExpenseHandler: addTransaction,);
              },
            );
          },
        ),
        IconSlideAction(
          caption: "Edit Category",
          color: Color.fromRGBO(130, 207, 237, 1),
          icon: Icons.edit,
          onTap: () {
            widget.removeCategoryHandler(widget.category);
            Navigator.of(context).pop(true);
          },
        ),
      ],
      secondaryActions: <Widget>[
        IconSlideAction(
          caption: "Delete",
          color: Colors.red,
          icon: Icons.cancel,
          onTap: () {
            return showDialog<bool>(
              context: context,
              builder: (context) {
                return AreYouSureDelete(
                    widget.category, widget.removeCategoryHandler);
              },
            );
          },
        ),
      ],
    );
  }

@kenthinson
Copy link

kenthinson commented May 14, 2020

@rbb091020 Looks fine. You have tested to make sure that the .then() function is actually getting called with a debug print statement?

@ghost
Copy link

ghost commented May 14, 2020

Yup. Then is reached.

@kenthinson
Copy link

@rbb091020 sounds like some kind of scope problem. No error in the console?

@ghost
Copy link

ghost commented May 15, 2020

Nope. No error in the console.

@kenthinson
Copy link

@rbb091020 where are you declaring slidableController?

@ghost
Copy link

ghost commented May 15, 2020

@kenthinson I edited my code snippet above to include the entire stateful widget class. The slidableController is declared right before the build method.

@kenthinson
Copy link

kenthinson commented May 15, 2020

@rbb091020 are you having multiple bars / slidables in a list? If so you need to lift slidableController up so that all slidable share one slidableController instance not a new one for each slidable.

@ghost
Copy link

ghost commented May 15, 2020

Yes I do! So this is what I tried... I have a parent widget for my bar class called BudgetList. I tried lifting the state up by creating the controller there and a function that calls slidableController.activeState.close() and passing that function to my Bar widget. I get the "close" called on null error when I do that.

I also tried creating the SlidableController and passing it as a parameter to the Bar constructor, but that's just creating an instance for each slidable just like you said not to do.

I feel like I'm closer though! How can I properly lift the state up?

@ghost
Copy link

ghost commented May 15, 2020

I'm getting no error now, but it's still not closing automatically.

Here is my new BarState:

class Bar extends StatefulWidget {
  final Category category;
  final Key key;
  final Function removeCategoryHandler;
  final Function closeSlidableHandler;
  final Function getController;
  Bar({this.category, this.key, this.removeCategoryHandler, this.closeSlidableHandler, this.getController});

  @override
  _BarState createState() => _BarState();
}

class _BarState extends State<Bar> {
  //final SlidableController slidableController = SlidableController();
  @override
  Widget build(BuildContext context) {
    return Slidable(
      direction: Axis.horizontal,
      controller: widget.getController(),
      key: widget.key,
      dismissal: SlidableDismissal(
        child: SlidableDrawerDismissal(),
        onWillDismiss: (actionType) {
          if (actionType == SlideActionType.primary) {
            return showDialog<bool>(
                context: context,
                builder: (cxt) {
                  return AddExpenseForm(addExpenseHandler: addTransaction,);
                }).then((onValue){
                  widget.closeSlidableHandler();
                  return null;
                });

And here are the relevant portions of BudgetList:

class _BudgetListState extends State<BudgetList> {
  List<Category> _categories = [];
  String selectedOption;
  SlidableController slidableController = new SlidableController();

@override
  Widget build(BuildContext context) {
...
child: Column(
          children: <Widget>[
            Container(
              child: AppBar(
                actions: <Widget>[
                  AddButton(addOption),
                ],
                title: Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  children: <Widget>[
                    Text(
                      "Expenses",
                      textAlign: TextAlign.start,
                    ),
                  ],
                ),
              ),
            ),
            Expanded(
              child: _categories.length > 0
                  ? ReorderableListView(
                      onReorder: _onReorder,
                      children: _categories.map((category) {
                        return Bar(
                          category: category,
                          key: UniqueKey(),
                          removeCategoryHandler: removeCategory,
                          closeSlidableHandler: closeSlider,
                          getController: getController,
                        );
                      }).toList(),
                    )
...
  void closeSlider(){
    slidableController.activeState.close();
  }

  SlidableController getController(){
    return slidableController;
  }

@kenthinson
Copy link

@rbb091020 Sorry you are having so much trouble. I would be willing to fix the error for you for a small fee (currently unemployed and need to eat :). if you provide me all your source code I could figure it out. If you don’t want to go that route at least you know it’s possible(and not a issue with the library ) and just something wrong in your code.

@letsar
Copy link
Owner

letsar commented Nov 8, 2020

I'm closing this, since I think it's not an issue with Slidable as @kenthinson said.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants