flutter - 在 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'),
),
),
],
),
],
),
),
),
),
);
}
}
任何人都可以请帮助或提供相同的提示吗?谢谢你!
解决方案
推荐阅读
- jql - 如何从特定用户那里获取所有故事和子任务以及在 JIRA 中的每个项目上花费的时间
- react-native - 有什么方法可以在本机应用程序中生成 pdf 吗?
- java - 'Run As' Cucumber 功能没有为 Eclipse 中的 Cucumber 功能文件生成胶水代码
- node.js - 使用 NodeJS API 将移动应用程序数据(如步骤和所有数据)保存到 mongo dB
- npm - node-sass 是否支持 @use 和 @forward
- python - 类型注释但跳过 auto_attribs 类中的类属性
- html - HTML 通过导航列中的链接跳转到内容
- python - python windows:当cmd窗口关闭时退出循环
- typeorm - IN 运算符似乎不适用于主键字段
- asp.net - 无法在 IIS 下使用 dotnet 配置分层设置