Bounce Dismissible 帮助用户

Bounce Dismissible for helping the user

所以我们有一个 confirming/denying 项目的 Dismissible。 但是,我们有一些用户正试图 click/tap 该项目。 我们的 UX 团队建议我们“反弹”该项目以表明他们必须滑动(并显示操作字段)。 但我看不到任何选择。

有没有人有什么建议可以解决这个问题? 我现在的代码如下所示:

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(),
),

这是我的最小示例,它可以满足您的需求。

基本上,GestureDetector onTap 回调通过在 _animation.value 上使用 sin 函数触发具有类似弹跳效果的动画。可以通过更改参数 cyclesPerAnimationbounceOffset.

来调整行为

只需将 Dismissible 放在 Container 的位置,就可以开始了。

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')),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Dismissable好像没有这个功能。

相反,您可以使用 flutter_slidable 包。

在这里,您可以通过调用 Slideable.of(context)?.open() 以编程方式打开基础操作。虽然没有花哨的弹跳动画。

代码如下:

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(),
              ),
            ),
          ],
        ),
      ),
    );
  }
}