#flutter #dart #flutter-animation #gesturedetector
#трепетание #дротик #флаттер-анимация #детектор жестов
Вопрос:
Я хотел бы иметь возможность провести пальцем вниз, чтобы отклонить, но с Hero-Animation
.
Я пытался использовать GestureDetector
подобное:
body: GestureDetector(
onVerticalDragDown: (details) {
Navigator.pop(context);
},
child:
Анимация выглядит нормально с этим, но проблема в том, что она присутствует poping
практически при любом жесте. Что я хотел бы иметь, так это то, что он появляется только в том случае, если пользователь действительно проводит пальцем вниз.
Также должно быть какое finished
-то свойство, чтобы анимация отменялась, если пользователь не полностью проводит пальцем вниз. Возможно ли это, и если да, то как? Я ничего не смог найти по этому поводу..
Желаемый результат должен выглядеть следующим образом:
Как вы можете видеть, я могу провести пальцем вниз, а также отменить pop
, не полностью проведя пальцем вниз.
Текущая анимация:
При нажатии кнопки закрытия анимация работает просто отлично. Однако, если я начну перетаскивать, анимация должна начаться, и если я ее закончу, она должна pop
начаться, или я мог бы также отменить анимацию, и анимация будет восстановлена на обычном экране.
Это мой код, если это полный код, если это помогает:
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onVerticalDragDown: (details) {
Navigator.pop(context);
},
child: Stack(
children: [
Hero(
tag: month.name 'background',
// transitionOnUserGestures: true,
child: Container(
// color: CustomColors.darkCustom,
decoration: BoxDecoration(
color: CustomColors.darkCustom,
borderRadius: BorderRadius.circular(30.0),
),
),
),
Positioned(
right: 30,
top: 15,
child: SafeArea(
child: Hero(
// transitionOnUserGestures: true,
tag: month.name 'close',
child: Container(
height: 45,
width: 45,
child: RawMaterialButton(
fillColor: CustomColors.lightGreyCustom,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
// elevation: 10,
shape: CircleBorder(),
onPressed: () {
Navigator.of(context).pop();
},
child: SvgPicture.asset('assets/images/close.white.svg',
height: 25, width: 25),
),
),
),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SafeArea(
bottom: false,
child: SizedBox(height: 20),
),
Row(
children: [
Padding(
padding:
const EdgeInsets.only(left: 45, top: 45, bottom: 35),
child: Hero(
// transitionOnUserGestures: true,
tag: month.name 'text',
// sized box to prevent flickering bug
child: SizedBox(
height: 40,
width: 200,
// material is need for Hero Text
child: Material(
color: Colors.transparent,
child: Text(
month.name,
style: TextStyle(
color: Colors.white,
fontSize: 28,
fontFamily: Fonts.glossAndBloom,
),
),
),
),
),
),
],
),
Hero(
tag: month.name 'frame',
child: Container(
height: Constants.width(context) - 60,
width: Constants.width(context) - 60,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
border: Border.all(color: Colors.white, width: 5),
),
),
),
],
)
],
),
),
);
}
В Swift мне удалось добиться анимации с помощью этого кода:
@objc private func handlePan(gestureRecognizer:UIPanGestureRecognizer) {
// calculate the progress based on how far the user moved
let translation = panGR.translation(in: nil)
let progress = translation.y / 2 / view.bounds.height
switch panGR.state {
case .began:
// begin the transition as normal
self.dismissView()
break
case .changed:
Hero.shared.update(progress)
default:
// finish or cancel the transition based on the progress and user's touch velocity
if progress panGR.velocity(in: nil).y / view.bounds.height > 0.3 {
self.dismissView()
Hero.shared.finish()
} else {
Hero.shared.cancel()
}
}
}
Ответ №1:
Я предлагаю использовать DragEndDetails функции обратного вызова. Простым примером будет:
onVerticalDragEnd: (endDetails) {
double velocity = endDetails.primaryVelocity;
if (velocity > 0 ){
Navigator.pop(context);
}
},
В этом случае, если вы удерживаете жест перетаскивания в конце, он не появится, потому что скорость будет равна 0.
Редактировать:
Это простой пример реализации анимации на деталях перетаскивания. При обновлении перетаскивания высота контейнера будет скорректирована, но с ограничением от max: 300 до min: 100. При перетаскивании зависит от того, проводите ли вы пальцем вверх или вниз, высота контейнера будет установлена на max или min.
class AnimatedContainerApp extends StatefulWidget {
@override
_AnimatedContainerAppState createState() => _AnimatedContainerAppState();
}
class _AnimatedContainerAppState extends State<AnimatedContainerApp> {
double height = 300;
bool gestureUp = false;
@override
Widget build(BuildContext context) {
final maxHeight = 300.0;
final minHeight = 100.0;
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('AnimatedContainer Demo'),
),
body: Align(alignment: Alignment.bottomCenter,
child:AnimatedContainer(
color: Colors.red,
height: height,
duration: Duration(milliseconds: 200),
curve: Curves.fastOutSlowIn,
child: GestureDetector(
onVerticalDragUpdate: (details) {
setState((){
if (0 < details.delta.dy)
gestureUp = false;
else
gestureUp = true;
height -= details.delta.dy;
if (height > maxHeight)
height = maxHeight;
else if (height < minHeight)
height = minHeight;
});
},
onVerticalDragEnd: (details) {
setState((){
if (gestureUp) {
height = maxHeight;
} else {
height = minHeight;
}
});
},
)
),
),
),
);
}
}
Комментарии:
1. на самом деле вы должны использовать kMinFlingVelocity или некоторые подобные константы
2. хорошо, этот вид работает так, что
pop
вызывается только тогда, когда он действительно должен быть вызван. Однако при перетаскивании вниз анимация не запускается. при завершении всего перетаскивания он анимируется. Но это не 100% желаемая анимация. Вы понимаете, что я имею в виду?3. @Chris Да. В этом случае вы должны обрабатывать анимацию с
onVerticalDragUpdate
помощью обратного вызова4. @hyobbb как бы я это сделал??
5. Это зависит от того, как вы хотите реализовать поведение анимации с помощью
DragUpdateDetails
. ознакомьтесь с этим документом api. api.flutter.dev/flutter/gestures/DragUpdateDetails-class.html