Изменить анимацию стека карт с просмотра Flip на физические картыIOS

Программируем под IOS
Ответить
Anonymous
 Изменить анимацию стека карт с просмотра Flip на физические карты

Сообщение Anonymous »

Я работаю над приложением 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 
, но мне нужно что -то, что имитирует, взяв карту сверху и помещая ее внизу.

Подробнее здесь: https://stackoverflow.com/questions/795 ... d-browsing
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «IOS»