首页 > 解决方案 > 在 Stack 内的底部小部件上设置顶部小部件的动画,并使用 Transform 导致问题

问题描述

我在为我的Body()小部件设置动画时遇到问题,我的CustomDrawer()两者都在Stack(). 我想要做的是为我的Body()小部件设置动画CustomDrawer()(就像Discord聊天页面一样)我已经实现了从左到右的滑动,但是我在从右到左制作动画时遇到了麻烦。正如您在下面的 gif 中看到的那样。

在此处输入图像描述

我使用Transform()andGestureDetector()将我的Body()小部件从左到右转换。但是当我想从右向左滑动时就会出现问题。

我想要什么:我希望当用户从左向右滑动时它会显示 Drawer1,当用户从右向左滑动时它会显示 Drawer2。抽屉 1 和 2 只不过是一个Column()内部有 2 的 SIngle 小部件Row()。我正在根据DragStartDetails使用来检测滑动,startDetails.globalPosition.dx这会给我x轴的位置。

问题:但是当我尝试改变从右到左滑动的逻辑时,它在 GIF 中给了我这种 janky 效果。

这是我的AppDrawer()小部件,所有事情都在其中发生。

  class AppDrawer extends StatefulWidget {
  final Widget child;
  AppDrawer({
    key,
    required this.child,
  }) : super(key: key);

  static _AppDrawerState? of(BuildContext context) =>
      context.findAncestorStateOfType<_AppDrawerState>();

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

class _AppDrawerState extends State<AppDrawer>
    with SingleTickerProviderStateMixin {
  static Duration duration = const Duration(milliseconds: 300);
  late AnimationController _controller;
  static double maxSlide = 255;
  static const dragRightStartVal = 260;
  static var dragLeftStartVal = maxSlide - 20;
  static bool shouldDrag = false;
  bool isDraggingFromRight = false;
  bool isDraggingFromLeft = false;

  @override
  void initState() {
    _controller =
        AnimationController(vsync: this, duration: _AppDrawerState.duration);
    super.initState();
  }

  void close() => _controller.reverse();

  void open() => _controller.forward();

  void toggle() {
    if (_controller.isCompleted) {
      close();
    } else {
      open();
    }
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _onDragStart(DragStartDetails startDetails) {
    // print("dragstart");
    // print("startdetails $startDetails");
    isDraggingFromLeft = _controller.isDismissed &&
        startDetails.globalPosition.dx < dragRightStartVal;
    // print("isDraggingFromLeft${isDraggingFromLeft}");
    isDraggingFromRight = _controller.isDismissed
        ? startDetails.globalPosition.dx > -dragLeftStartVal
        : _controller.isCompleted &&
            startDetails.globalPosition.dx > dragLeftStartVal;
    // print("isDraggingFromRight${isDraggingFromRight}");
    shouldDrag = isDraggingFromLeft || isDraggingFromRight;
    // print("shouldDrag${shouldDrag}");
    // print("controller${_controller.isDismissed}${_controller.isCompleted}");
  }

  void _onDragUpdate(DragUpdateDetails updateDetails) {
    if (shouldDrag == false) {
      return;
    }
    double delta = updateDetails.primaryDelta! / maxSlide;
    _controller.value += delta;
  }

  void _onDragEnd(DragEndDetails dragEndDetails) {
    if (_controller.isDismissed || _controller.isCompleted) {
      return;
    }

    double _kMinFlingVelocity = 365.0;
    double dragVelocity = dragEndDetails.velocity.pixelsPerSecond.dx.abs();

    if (dragVelocity >= _kMinFlingVelocity) {
      double visualVelocityInPx = dragEndDetails.velocity.pixelsPerSecond.dx /
          MediaQuery.of(context).size.width;
      _controller.fling(velocity: visualVelocityInPx);
    } else if (_controller.value < 0.5) {
      close();
    } else {
      open();
    }
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onHorizontalDragStart: _onDragStart,
      onHorizontalDragUpdate: _onDragUpdate,
      onHorizontalDragEnd: _onDragEnd,
      child: AnimatedBuilder(
        animation: _controller,
        builder: (BuildContext context, _) {
          double animationVal = isDraggingFromRight && _controller.isCompleted
              ? -_controller.value
              : _controller.value;
          double translateVal =
              animationVal * MediaQuery.of(context).size.width * 0.5;
          return Stack(
            children: <Widget>[
              CustomDrawer(),
              Transform(
                alignment: Alignment.centerLeft,
                transform: Matrix4.identity()..translate(translateVal),
                child: GestureDetector(
                  onTap: () {
                    if (_controller.isCompleted) {
                      close();
                    }
                  },
                  child: widget.child,
                ),
              ),
            ],
          );
        },
      ),
    );
  }
}

class CustomDrawer extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Material(
      color: Colors.black38,
      child: SafeArea(
        child: Theme(
          data: ThemeData(
            brightness: Brightness.dark,
          ),
          child: SizedBox(
            width: MediaQuery.of(context).size.width,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Column(
                  children: <Widget>[
                    Container(
                      padding: const EdgeInsets.all(16),
                      child: const Text('Drawer 1',
                          style: TextStyle(fontSize: 30, color: Colors.white)),
                    ),
                    Container(
                      width: MediaQuery.of(context).size.width * 0.5,
                      child: const ListTile(
                        leading: Icon(Icons.home),
                        title: Text('Home'),
                      ),
                    ),
                    Container(
                      width: MediaQuery.of(context).size.width * 0.5,
                      child: const ListTile(
                        leading: Icon(Icons.home),
                        title: Text('Favourite'),
                      ),
                    ),
                    Container(
                      width: MediaQuery.of(context).size.width * 0.5,
                      child: const ListTile(
                        leading: Icon(Icons.home),
                        title: Text('Store'),
                      ),
                    ),
                  ],
                ),
                Column(
                  children: <Widget>[
                    Container(
                      padding: const EdgeInsets.all(16),
                      child: const Text('Drawer 2',
                          style: TextStyle(fontSize: 30, color: Colors.white)),
                    ),
                    Container(
                      width: MediaQuery.of(context).size.width * 0.5,
                      child: const ListTile(
                        leading: Icon(Icons.home),
                        title: Text('Settings'),
                      ),
                    ),
                    Container(
                      width: MediaQuery.of(context).size.width * 0.5,
                      child: const ListTile(
                        leading: Icon(Icons.home),
                        title: Text('Socials'),
                      ),
                    ),
                    Container(
                      width: MediaQuery.of(context).size.width * 0.5,
                      child: const ListTile(
                        leading: Icon(Icons.home),
                        title: Text('Feedback'),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

任何人都可以请帮助或提供相同的提示吗?谢谢你!

标签: flutterflutter-layoutflutter-animation

解决方案


推荐阅读