Implementation of Flutter sliding deletion

Please indicate the source of Reprint: Wang Jiji's way to the big ox

Preface

I haven't written an article for a while. First, I'm busy with my work. Second, I have a lot of private affairs. After all, I'm two years old. Last year, I mainly did RN+Android + technology research. This year, we will focus on the multi terminal implementation of Flutter. I hope it can be done well.

Effect


A control that can do common interaction such as sliding deletion (sometimes write, change the source code, sometimes read others' implementation, sometimes build your own wheel and always hope you can use it when you need it)

Project structure


A relatively pure project, no advertising, no messy dependence, will not cause trouble to the user's dependent link.

How to use

 LeftScroll(
              buttonWidth: 80,
              child: Container(
                height: 60,
                color: Colors.white,
                alignment: Alignment.center,
                child: Text('👈 Try Scroll Left'),
              ),
              buttons: <Widget>[
                LeftScrollItem(
                  text: 'delete',
                  color: Colors.red,
                  onTap: () {
                    print('delete');
                  },
                ),
                LeftScrollItem(
                  text: 'Edit',
                  color: Colors.orange,
                  onTap: () {
                    print('edit');
                  },
                ),
              ],
              onTap: () {
                print('tap row');
              },
            )

It's also very simple to use. A buttons attribute is associated with several leftscrollitems. This custom control can realize the logic of starting after sliding

Question 1: is a normal button OK?
Question 2: how does hierarchy work?

LeftScrollListItem

class LeftScrollListItem {
  final String key;
  final Widget child;
  final List<Widget> buttons;
  final Function onTap;
  LeftScrollListItem({
    @required this.key,
    @required this.child,
    @required this.buttons,
    @required this.onTap,
  });
}

In fact, the definition of LeftScrollListItem is a little unexpected. It is not a Widget object, nor does it do any special processing. It is more like a tool for structured child controls.

LeftScroll

More than 200 lines. Let's analyze him in a paragraph

Constructor must pass child controls, processing buttons, and a bunch of callbacks. It is more important to pay attention to the closeOnPop property, which is a judgment flag for losing focus or other interactive closing and sliding. It is only supported after 1.20.

  LeftScroll({
    this.key,
    @required this.child,
    @required this.buttons,
    this.onSlideStarted,
    this.onSlideCompleted,
    this.onSlideCanceled,
    this.onTap,
    this.buttonWidth: 80.0,
    this.onScroll,
    this.closeOnPop: true,
  }) : super(key: key);

Initialization

 @override
  void initState() {
    super.initState();
    maxDragDistance = widget.buttonWidth * widget.buttons.length;//Calculate the sliding distance according to the width
    //Handle horizontal drag events
    gestures[HorizontalDragGestureRecognizer] =
        GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>(
      () => HorizontalDragGestureRecognizer(debugOwner: this),
      (HorizontalDragGestureRecognizer instance) {
        instance
          ..onDown = onHorizontalDragDown //Start drag trigger
          ..onUpdate = onHorizontalDragUpdate//Update progress
          ..onEnd = onHorizontalDragEnd; //Drag end trigger
      },
    );

	//Handle click events
    gestures[TapGestureRecognizer] =
        GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
      () => TapGestureRecognizer(debugOwner: this),
      (TapGestureRecognizer instance) {
        instance..onTap = widget.onTap;//Return to the external incoming click event
      },
    );

	//Refresh status based on animation progress
    animationController = AnimationController(
        lowerBound: -maxDragDistance,
        upperBound: 0,
        vsync: this,
        duration: Duration(milliseconds: 300))
      ..addListener(() {
        translateX = animationController.value;
        setState(() {});
      });
  }
 @override
 Widget build(BuildContext context) {
	......
	 return widget.closeOnPop
        ? WillPopScope(
            child: body,
            onWillPop: () async {
              if (translateX != 0) {
                close();
                return false;
              }
              return true;
            })
        : body;
 }

What is mounted is an onWillPop that accepts asynchronous methods. When the animation is finished, it will return to true to make the logic valid

Several logical methods

  void onHorizontalDragDown(DragDownDetails details) {
    //Slide horizontally to start calling back external incoming methods
    if (widget.onSlideStarted != null) widget.onSlideStarted.call();
  }

  void onHorizontalDragUpdate(DragUpdateDetails details) {
    translateX = (translateX + details.delta.dx).clamp(-maxDragDistance, 0.0);
    if (widget.onScroll != null) {
      //Return progress
      widget.onScroll.call(
        translateX / maxDragDistance * -1,
      );
    }
    setState(() {});
  }

void onHorizontalDragEnd(DragEndDetails details) {
    animationController.value = translateX;
    //Collect back
    if (details.velocity.pixelsPerSecond.dx > 200) {
      close();
    } else if (details.velocity.pixelsPerSecond.dx < -200) {
    //Sliding distance over 200(Pull over threshold),open
      open();
    } else {
       //More than half
      if (translateX.abs() > maxDragDistance / 2) {
        open();
      } else {
        close();
      }
    }
  }

//Width of opening button
void open() {
    if (translateX != -maxDragDistance)
      animationController.animateTo(-maxDragDistance).then((_) {
        if (widget.onSlideCompleted != null) widget.onSlideCompleted.call();
      });
  }

  //Close to 0
  void close() {
    if (translateX != 0)
      animationController.animateTo(0).then((_) {
        if (widget.onSlideCanceled != null) widget.onSlideCanceled.call();
      });
  }

Summary - implementation process

  1. Building a child control object to standardize the incoming child control and business callback
  2. Build an animation process of opening and closing through gesture processing + AnimationController
  3. The combination of Stack+RawGestureDetector+WillPopScope realizes the final effect of layout + animation effect + asynchronous shutdown

Source address: https://github.com/mjl0602/left-scroll-actions

227 original articles published, 616 praised and 1 million 50 thousand visited+
His message board follow

Tags: Android Attribute github

Posted on Tue, 14 Jan 2020 03:20:12 -0800 by bcoffin