Я работаю над приложением Flutter с анимацией карты с сложенной картой, но мне нужно изменить, как работает анимация. В настоящее время карты переворачиваются при прокрутке, но я хочу другой эффект. < /P>
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:manycards/model/card_info.dart';
import 'package:manycards/view/constants/widgets/cards.dart';
class CardStackAnimation extends StatefulWidget {
final double height;
final Function(bool)? onCardSelected;
const CardStackAnimation({
super.key,
required this.height,
this.onCardSelected,
});
@override
State createState() => _CardStackAnimationState();
}
class _CardStackAnimationState extends State {
late double positionY_line1;
late double positionY_line2;
late List _cardInfoList;
late double _middleAreaHeight;
late double _outsiteCardInterval;
late double scrollOffsetY;
@override
void initState() {
super.initState();
positionY_line1 = widget.height * 0.1;
positionY_line2 = positionY_line1 + 200;
_middleAreaHeight = positionY_line2 - positionY_line1;
_outsiteCardInterval = 30.0;
scrollOffsetY = 0.0;
_cardInfoList = [
CardInfo(
cardholderName: "John Doe",
balanceAmount: "₦801,521.91",
backgroundColor: Color(0xFFEA5EBE),
),
CardInfo(
cardholderName: "John Doe",
balanceAmount: "₦425,600.00",
backgroundColor: Color(0xFF0A0A0A),
),
CardInfo(
cardholderName: "John Doe",
balanceAmount: "₦650,320.50",
backgroundColor: Colors.pinkAccent,
),
CardInfo(
cardholderName: "John Doe",
balanceAmount: "₦975,432.25",
backgroundColor: Color(0xFF0B258A),
),
CardInfo(
cardholderName: "John Doe",
balanceAmount: "₦123,450.75",
backgroundColor: Colors.red,
),
];
// Initialize card positions
for (int i = 0; i < _cardInfoList.length; i++) {
CardInfo cardInfo = _cardInfoList[i];
if (i == 0) {
cardInfo.positionY = positionY_line1;
cardInfo.opacity = 1.0;
cardInfo.rotate = 1.0;
cardInfo.scale = 1.0;
} else {
cardInfo.positionY = positionY_line2 + (i - 1) * 30;
cardInfo.opacity = 0.7 - (i - 1) * 0.1;
cardInfo.rotate = -60;
cardInfo.scale = 0.9;
}
}
_cardInfoList = _cardInfoList.reversed.toList();
}
List _buildCards() {
List widgetList = [];
final screenWidth = MediaQuery.of(context).size.width;
for (CardInfo cardInfo in _cardInfoList) {
widgetList.add(
Positioned(
top: cardInfo.positionY,
// Center the card horizontally
left: 0,
right: 0,
child: Center(
child: Transform(
transform:
Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateX(pi / 180 * cardInfo.rotate)
..scale(cardInfo.scale),
alignment: Alignment.topCenter,
child: Opacity(
opacity: cardInfo.opacity,
child: SizedBox(
width: screenWidth, // Use full screen width
child: CurrencyCard(
cardholderName: cardInfo.cardholderName,
balanceAmount: cardInfo.balanceAmount,
backgroundColor: cardInfo.backgroundColor,
),
),
),
),
),
),
);
}
return widgetList;
}
void _updateCardPosition(double offsetY) {
scrollOffsetY += offsetY;
void updatePosition(CardInfo cardInfo, double firstCardAreaIdx, int index) {
double currentCardAreaIdx = firstCardAreaIdx + index;
if (currentCardAreaIdx < 0) {
cardInfo.positionY = positionY_line1 + currentCardAreaIdx * 5;
cardInfo.scale =
1.0 - 0.2 / 10 * (positionY_line1 - cardInfo.positionY);
if (cardInfo.scale < 0.8) cardInfo.scale = 0.8;
if (cardInfo.scale > 1) cardInfo.scale = 1.0;
cardInfo.rotate = -90.0 / 5 * (positionY_line1 - cardInfo.positionY);
if (cardInfo.rotate > 0.0) cardInfo.rotate = 0.0;
if (cardInfo.rotate < -90.0) cardInfo.rotate = -90.0;
cardInfo.opacity =
1.0 - 0.7 / 5 * (positionY_line1 - cardInfo.positionY);
if (cardInfo.opacity < 0.0) cardInfo.opacity = 0.0;
if (cardInfo.opacity > 1) cardInfo.opacity = 1.0;
} else if (currentCardAreaIdx >= 0 && currentCardAreaIdx < 1) {
cardInfo.scale =
1.0 -
0.1 /
(positionY_line2 - positionY_line1) *
(cardInfo.positionY - positionY_line1);
if (cardInfo.scale < 0.9) cardInfo.scale = 0.9;
if (cardInfo.scale > 1) cardInfo.scale = 1.0;
cardInfo.positionY =
positionY_line1 + currentCardAreaIdx * _middleAreaHeight;
cardInfo.rotate =
-60.0 /
(positionY_line2 - positionY_line1) *
(cardInfo.positionY - positionY_line1);
if (cardInfo.rotate > 0.0) cardInfo.rotate = 0.0;
if (cardInfo.rotate < -60.0) cardInfo.rotate = -60.0;
cardInfo.opacity =
1.0 -
0.3 /
(positionY_line2 - positionY_line1) *
(cardInfo.positionY - positionY_line1);
if (cardInfo.opacity < 0.0) cardInfo.opacity = 0.0;
if (cardInfo.opacity > 1) cardInfo.opacity = 1.0;
} else if (currentCardAreaIdx >= 1) {
cardInfo.positionY =
positionY_line2 + (currentCardAreaIdx - 1) * _outsiteCardInterval;
cardInfo.rotate = -60.0;
cardInfo.scale = 0.9;
cardInfo.opacity = 0.7;
}
}
double firstCardAreaIdx = scrollOffsetY / _middleAreaHeight;
setState(() {
for (int i = 0; i < _cardInfoList.length; i++) {
CardInfo cardInfo = _cardInfoList[_cardInfoList.length - 1 - i];
updatePosition(cardInfo, firstCardAreaIdx, i);
}
});
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onVerticalDragUpdate: (DragUpdateDetails d) {
_updateCardPosition(d.delta.dy);
},
onVerticalDragEnd: (DragEndDetails d) {
scrollOffsetY =
(scrollOffsetY / _middleAreaHeight).round() * _middleAreaHeight;
_updateCardPosition(0);
},
child: Container(
width: double.infinity, // Use all available width
height: widget.height, // Use provided height
decoration: BoxDecoration(color: Colors.transparent),
child: Stack(
alignment: Alignment.topCenter,
fit: StackFit.expand, // Make stack fill its parent
children: [..._buildCards()],
),
),
);
}
}
< /code>
Вот где я использую его в своем пользовательском интерфейсе: < /p>
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:manycards/gen/assets.gen.dart';
import 'package:manycards/view/constants/text/text.dart';
import 'package:manycards/view/constants/widgets/subcard_animation.dart';
class SubCard extends StatelessWidget {
const SubCard({super.key});
@override
Widget build(BuildContext context) {
// Calculate available height for the card stack
final screenHeight = MediaQuery.of(context).size.height;
final topPadding = MediaQuery.of(context).padding.top;
final availableHeight =
screenHeight - topPadding - 120.h; // Subtract top bar and padding
return Scaffold(
backgroundColor: const Color(0xFF121212),
body: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Back button
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Container(
height: 32.h,
width: 48.w,
decoration: BoxDecoration(
color: const Color(0xFF2C2C2C),
borderRadius: BorderRadius.circular(50.r),
),
child: Center(child: Assets.svg.backArrow.svg()),
),
),
],
),
SizedBox(height: 30.h),
// Title
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Assets.images.nigerianFlag.image(height: 25.h, width: 25.w),
CustomTextWidget(
text: ' Subcards',
fontSize: 18.sp,
color: Color(0xFFEAEAEA),
fontWeight: FontWeight.bold,
),
],
),
SizedBox(height: 30.h),
// Card stack with explicit height
Expanded(
child: CardStackAnimation(
height: availableHeight,
onCardSelected: (selected) {
if (selected) {
// Handle card selection here
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Card selected!')));
}
},
),
),
],
),
),
),
);
}
}
< /code>
Вместо текущей анимации Flip я хочу воссоздать опыт удержания физической стопки карт в вашей руке, где: < /p>
Вы видите верхнюю карту, полностью видимую
, когда вы взаимодействуете с ней (нажимайте или промахите), она переходит к обратном спине, пока вы не сможете, пока вы не сможете, пока вы не сможете, пока вы не сможете, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли переназначить, чтобы вы могли продолжить, пока вы не сможете, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, пока вы не сможете, пока вы не сможете, пока не сможете, чтобы вы могли продолжить, пока вы не сможете, чтобы вы могли продолжить твит. Оригинальная карта < /p>
Кроме того, я хотел бы сохранить ощущение, похожее на карусель, где вы можете частично увидеть края предыдущих и следующих карт в стеке, чтобы указать, что есть больше карт для просмотра. Кажется, я не могу получить желаемое чувство «стека физических карт». Текущая реализация в значительной степени зависит от переворачивания карт с помощью rotatex
, но мне нужно что -то, что имитирует, взяв карту сверху и помещая ее внизу.
Я работаю над приложением Flutter с анимацией карты с сложенной картой, но мне нужно изменить, как работает анимация. В настоящее время карты переворачиваются при прокрутке, но я хочу другой эффект. < /P> [code]import 'dart:math'; import 'package:flutter/material.dart'; import 'package:manycards/model/card_info.dart'; import 'package:manycards/view/constants/widgets/cards.dart';
class CardStackAnimation extends StatefulWidget { final double height; final Function(bool)? onCardSelected;
@override State createState() => _CardStackAnimationState(); }
class _CardStackAnimationState extends State { late double positionY_line1; late double positionY_line2; late List _cardInfoList; late double _middleAreaHeight; late double _outsiteCardInterval; late double scrollOffsetY;
setState(() { for (int i = 0; i < _cardInfoList.length; i++) { CardInfo cardInfo = _cardInfoList[_cardInfoList.length - 1 - i]; updatePosition(cardInfo, firstCardAreaIdx, i); } }); }
@override Widget build(BuildContext context) { return GestureDetector( onVerticalDragUpdate: (DragUpdateDetails d) { _updateCardPosition(d.delta.dy); }, onVerticalDragEnd: (DragEndDetails d) { scrollOffsetY = (scrollOffsetY / _middleAreaHeight).round() * _middleAreaHeight; _updateCardPosition(0); }, child: Container( width: double.infinity, // Use all available width height: widget.height, // Use provided height decoration: BoxDecoration(color: Colors.transparent), child: Stack( alignment: Alignment.topCenter, fit: StackFit.expand, // Make stack fill its parent children: [..._buildCards()], ), ), ); } } < /code> Вот где я использую его в своем пользовательском интерфейсе: < /p> import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:manycards/gen/assets.gen.dart'; import 'package:manycards/view/constants/text/text.dart'; import 'package:manycards/view/constants/widgets/subcard_animation.dart';
class SubCard extends StatelessWidget { const SubCard({super.key});
@override Widget build(BuildContext context) { // Calculate available height for the card stack final screenHeight = MediaQuery.of(context).size.height; final topPadding = MediaQuery.of(context).padding.top; final availableHeight = screenHeight - topPadding - 120.h; // Subtract top bar and padding
// Card stack with explicit height Expanded( child: CardStackAnimation( height: availableHeight, onCardSelected: (selected) { if (selected) { // Handle card selection here ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('Card selected!'))); } }, ), ), ], ), ), ), ); } } < /code> Вместо текущей анимации Flip я хочу воссоздать опыт удержания физической стопки карт в вашей руке, где: < /p> Вы видите верхнюю карту, полностью видимую , когда вы взаимодействуете с ней (нажимайте или промахите), она переходит к обратном спине, пока вы не сможете, пока вы не сможете, пока вы не сможете, пока вы не сможете, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли переназначить, чтобы вы могли продолжить, пока вы не сможете, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, чтобы вы могли продолжить, пока вы не сможете, пока вы не сможете, пока не сможете, чтобы вы могли продолжить, пока вы не сможете, чтобы вы могли продолжить твит. Оригинальная карта < /p> Кроме того, я хотел бы сохранить ощущение, похожее на карусель, где вы можете частично увидеть края предыдущих и следующих карт в стеке, чтобы указать, что есть больше карт для просмотра. Кажется, я не могу получить желаемое чувство «стека физических карт». Текущая реализация в значительной степени зависит от переворачивания карт с помощью rotatex [/code], но мне нужно что -то, что имитирует, взяв карту сверху и помещая ее внизу.