2025-11-14 18:03:50 +09:00

298 lines
9.1 KiB
Dart

// packages/feature_game_spider/lib/widgets/card_widget.dart
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/spider_card.dart';
import '../controllers/spider_game_controller.dart';
class CardWidget extends StatelessWidget {
final SpiderCard card;
final double width;
final double height;
final bool isDraggable; // 👈 [추가]
const CardWidget({
super.key,
required this.card,
required this.width,
required this.height,
this.isDraggable = true, // 👈 [추가] 기본값은 true
});
@override
Widget build(BuildContext context) {
// 🔽 [수정] isDraggable이 false이면, 드래그 기능 없이 카드 앞면만 즉시 반환
if (!isDraggable) {
return _buildCardFace(context, card);
}
// --- (isDraggable이 true일 때만 아래 로직 실행) ---
final controller = Provider.of<SpiderGameController>(context, listen: false);
final List<SpiderCard> draggableStack = controller.getDraggableStack(card);
final bool canDrag = draggableStack.isNotEmpty;
return Draggable<List<SpiderCard>>(
data: draggableStack,
// 🔽 [수정] 겹침 높이 계산을 0.4로 수정
feedback: Opacity(
opacity: 0.8,
child: SizedBox(
width: width,
height: height + (draggableStack.length - 1) * (height * 0.4), // 👈 0.22 -> 0.4
child: Stack(
children: List.generate(draggableStack.length, (index) {
return Positioned(
top: index * (height * 0.4), // 👈 0.22 -> 0.4
left: 0,
// 🔽 [수정] 여기는 CardWidget이 아닌 _buildCardFace를 직접 호출 (중첩 Draggable 방지)
child: _buildCardFace(context, draggableStack[index]),
);
}),
),
),
),
childWhenDragging: _buildCardPlaceholder(context),
child: (card.isBeingDragged)
? _buildCardPlaceholder(context)
: _buildCardFace(context, card),
onDragStarted: () {
if (canDrag) {
controller.onDragStarted(draggableStack);
}
},
onDraggableCanceled: (velocity, offset) {
controller.onDragCancelled();
},
onDragEnd: (details) {
if (controller.draggedCards.isNotEmpty) {
controller.onDragCancelled();
}
},
);
}
// ( _buildCardFace, _buildRankText, _buildCenterSymbols 는 이전과 동일 )
Widget _buildCardFace(BuildContext context, SpiderCard card) {
return Container(
width: width,
height: height,
decoration: BoxDecoration(
color: card.isFaceUp ? Colors.white : Theme.of(context).primaryColor,
border: Border.all(color: Colors.black54, width: 0.5),
borderRadius: BorderRadius.circular(width * 0.08),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 2,
offset: const Offset(1, 1),
)
],
),
child: card.isFaceUp
? Stack(
children: [
_buildRankText(card, Alignment.topLeft),
_buildRankText(card, Alignment.bottomRight),
_buildCenterSymbols(card),
],
)
: null,
);
}
Widget _buildRankText(SpiderCard card, Alignment alignment) {
final bool isTopLeft = alignment == Alignment.topLeft;
final double fontSize = width * 0.4;
final double padding = width * 0.05;
Widget content = Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
card.rankText,
style: TextStyle(
color: card.isRed ? Colors.red : Colors.black,
fontWeight: FontWeight.bold,
fontSize: fontSize,
),
),
Text(
card.suitSymbol,
style: TextStyle(
color: card.isRed ? Colors.red : Colors.black,
fontSize: fontSize * 0.5,
),
),
],
);
if (!isTopLeft) {
content = Transform.rotate(
angle: math.pi,
child: content,
);
}
return Positioned(
top: isTopLeft ? padding : null,
left: isTopLeft ? padding : null,
bottom: isTopLeft ? null : padding,
right: isTopLeft ? null : padding,
child: content,
);
}
Widget _buildCenterSymbols(SpiderCard card) {
final double symbolSize = width * 0.2;
final double bigSymbolSize = width * 0.7;
if (card.rank > 10) {
return Center(
child: Text(
card.suitSymbol,
style: TextStyle(
color: card.isRed ? Colors.red : Colors.black,
fontSize: bigSymbolSize,
),
),
);
}
if (card.rank == 1) {
return Center(
child: Text(
card.suitSymbol,
style: TextStyle(
color: card.isRed ? Colors.red : Colors.black,
fontSize: bigSymbolSize * 0.8,
),
),
);
}
List<Widget> symbols = [];
Widget symbol(Alignment align) {
return Align(
alignment: align,
child: Text(
card.suitSymbol,
style: TextStyle(
color: card.isRed ? Colors.red : Colors.black,
fontSize: symbolSize,
),
),
);
}
Widget flippedSymbol(Alignment align) {
return Align(
alignment: align,
child: Transform.rotate(
angle: math.pi,
child: Text(
card.suitSymbol,
style: TextStyle(
color: card.isRed ? Colors.red : Colors.black,
fontSize: symbolSize,
),
),
),
);
}
switch (card.rank) {
case 2:
symbols.add(symbol(Alignment.topCenter));
symbols.add(flippedSymbol(Alignment.bottomCenter));
break;
case 3:
symbols.add(symbol(Alignment.topCenter));
symbols.add(symbol(Alignment.center));
symbols.add(flippedSymbol(Alignment.bottomCenter));
break;
case 4:
symbols.add(symbol(Alignment.topLeft));
symbols.add(symbol(Alignment.topRight));
symbols.add(flippedSymbol(Alignment.bottomLeft));
symbols.add(flippedSymbol(Alignment.bottomRight));
break;
case 5:
symbols.addAll([
symbol(Alignment.topLeft),
symbol(Alignment.topRight),
symbol(Alignment.center),
flippedSymbol(Alignment.bottomLeft),
flippedSymbol(Alignment.bottomRight),
]);
break;
case 6:
symbols.addAll([
symbol(Alignment.topLeft),
symbol(Alignment.topRight),
symbol(Alignment.centerLeft),
symbol(Alignment.centerRight),
flippedSymbol(Alignment.bottomLeft),
flippedSymbol(Alignment.bottomRight),
]);
break;
case 7:
symbols.addAll([
symbol(Alignment.topLeft),
symbol(Alignment.topRight),
symbol(const Alignment(0.0, -0.25)),
symbol(Alignment.centerLeft),
symbol(Alignment.centerRight),
flippedSymbol(Alignment.bottomLeft),
flippedSymbol(Alignment.bottomRight),
]);
break;
case 8:
symbols.addAll([
symbol(Alignment.topLeft),
symbol(Alignment.topRight),
symbol(const Alignment(0.0, -0.25)),
symbol(Alignment.centerLeft),
symbol(Alignment.centerRight),
flippedSymbol(Alignment.bottomLeft),
flippedSymbol(Alignment.bottomRight),
flippedSymbol(const Alignment(0.0, 0.25)),
]);
break;
case 9:
symbols.addAll([
symbol(const Alignment(-0.8, -0.6)),
symbol(const Alignment(0.8, -0.6)),
symbol(const Alignment(-0.8, 0.0)),
symbol(const Alignment(0.8, 0.0)),
symbol(Alignment.center),
flippedSymbol(const Alignment(-0.8, 0.6)),
flippedSymbol(const Alignment(0.8, 0.6)),
symbol(const Alignment(0.0, -0.8)),
flippedSymbol(const Alignment(0.0, 0.8)),
]);
break;
case 10:
symbols.addAll([
symbol(const Alignment(-0.8, -0.7)),
symbol(const Alignment(0.8, -0.7)),
symbol(const Alignment(-0.8, -0.1)),
symbol(const Alignment(0.8, -0.1)),
symbol(const Alignment(0.0, -0.9)),
symbol(const Alignment(0.0, -0.4)),
flippedSymbol(const Alignment(-0.8, 0.7)),
flippedSymbol(const Alignment(0.8, 0.7)),
flippedSymbol(const Alignment(0.0, 0.9)),
flippedSymbol(const Alignment(0.0, 0.4)),
]);
break;
}
return Padding(
padding: EdgeInsets.symmetric(horizontal: width * 0.2, vertical: height * 0.15),
child: Stack(children: symbols),
);
}
Widget _buildCardPlaceholder(BuildContext context) {
return Container(
width: width,
height: height,
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor.withOpacity(0.5),
borderRadius: BorderRadius.circular(width * 0.08),
),
);
}
}