Home > other >  Bounce Dismissible for helping the user
Bounce Dismissible for helping the user

Time:10-21

So we have a Dismissible for confirming/denying a item. However we have some users that are trying to click/tap on the item. Our UX team suggested that we then "bounce" the item to show that they have to swipe (and reveal the action fields). But I don't see any option to do so.

Does anybody have a suggestion what might work for this? The code I have for now is shown below:

Dismissible(
  key: const ValueKey(0),
  direction: DismissDirection.horizontal,
  child: Container(
    margin: EdgeInsets.symmetric(horizontal: 3, vertical: 3),
    child: card(),
  ),
  confirmDismiss: (direction) async {
    var newStatus = direction == DismissDirection.startToEnd
        ? OkNokNvt.OK
        : OkNokNvt.NOK;

    _changeStatus(newStatus);
    return false;
  },
  background: ok(),
  secondaryBackground: nok(),
),

CodePudding user response:

Here is my minimal example which does what you are looking for.

bouncing widget example gif

Basically, the GestureDetector onTap callback triggers the animation which has a bouncing-like effect by using a sin function on the _animation.value. The behaviour can be tweeked by changing the parameters cyclesPerAnimation and bounceOffset.

Simply put your Dismissible in the place of the Container and you should be good to go.

environment:
  sdk: ">=2.12.0 <3.0.0"
import 'dart:math';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark(),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late final AnimationController _animation = AnimationController(
    vsync: this,
    duration: const Duration(milliseconds: 500),
  );

  Offset _bounceOffset(double animationValue) {
    const cyclesPerAnimation = 2;
    const bounceOffset = 10;
    return Offset(
      0,
      sin(animationValue * pi * 2 * cyclesPerAnimation) * bounceOffset,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Bouncing Widget Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            AnimatedBuilder(
              animation: _animation,
              builder: (context, widget) => Transform.translate(
                offset: _bounceOffset(_animation.value),
                child: GestureDetector(
                  onTap: () {
                    _animation.reset();
                    _animation.forward();
                  },
                  child: Container(
                    color: Colors.grey,
                    height: 50,
                    width: 200,
                    child: const Center(child: Text('Tap to bounce')),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

CodePudding user response:

The Dismissable doesn't seeem to have this functionality.

Instead, you could use the dismissable widget

Here's the code:

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark(),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Bouncing Widget Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Slidable(
              key: const Key('key'),
              actionPane: const SlidableDrawerActionPane(),
              actionExtentRatio: 0.25,
              child: Builder(
                builder: (context) => GestureDetector(
                  onTap: () {
                    Slidable.of(context)
                        ?.open(actionType: SlideActionType.primary);
                  },
                  child: Container(
                    color: Colors.grey,
                    height: 50,
                    child: const Center(child: Text('Tap me')),
                  ),
                ),
              ),
              actions: [
                IconSlideAction(
                  caption: 'Delete',
                  color: Colors.red,
                  icon: Icons.delete,
                  onTap: () => print('remove me from list'),
                ),
              ],
              dismissal: SlidableDismissal(
                onDismissed: (_) => print('remove me from list'),
                dragDismissible: true,
                child: const SlidableDrawerDismissal(),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
  • Related