202 lines
7.3 KiB
Dart
202 lines
7.3 KiB
Dart
|
|
import 'dart:math';
|
||
|
|
|
||
|
|
import 'package:auto_route/auto_route.dart';
|
||
|
|
import 'package:dio/dio.dart';
|
||
|
|
import 'package:flutter/foundation.dart';
|
||
|
|
import 'package:flutter/material.dart';
|
||
|
|
import 'package:mnemo_cards/features/packs/images_holder.dart';
|
||
|
|
import 'package:mnemo_cards/flags.dart';
|
||
|
|
import 'package:mnemo_cards/utils/color_helper.dart';
|
||
|
|
import 'package:mnemo_cards/utils/string_helper.dart';
|
||
|
|
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
||
|
|
import 'package:rxdart/rxdart.dart';
|
||
|
|
|
||
|
|
import '../../admin/add_card.dart';
|
||
|
|
import '../../di/locator.dart';
|
||
|
|
import '../../domain/router/app_router.dart';
|
||
|
|
import '../../features/card_flipper/flip_card_controllers.dart';
|
||
|
|
import '../../features/card_swiper/src/allowed_swipe_direction.dart';
|
||
|
|
import '../../features/card_swiper/src/card_swiper.dart';
|
||
|
|
import '../../features/card_swiper/src/card_swiper_controller.dart';
|
||
|
|
import '../../main.dart';
|
||
|
|
import '../game_card_widget.dart';
|
||
|
|
|
||
|
|
class CardGameMainWidget extends StatelessWidget {
|
||
|
|
final CardPackDto cardPack;
|
||
|
|
final int? startCardId;
|
||
|
|
final CardSwiperController cardSwiperController;
|
||
|
|
final FlipCardController flipCardController;
|
||
|
|
final int length;
|
||
|
|
|
||
|
|
const CardGameMainWidget(
|
||
|
|
this.cardPack,
|
||
|
|
this.startCardId,
|
||
|
|
this.cardSwiperController,
|
||
|
|
this.flipCardController,
|
||
|
|
this.length,
|
||
|
|
);
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
final numberOfCardsDisplayed = min(2, cardPack.cards.length);
|
||
|
|
final initialScale = 0.9;
|
||
|
|
int lastThreshold = 0;
|
||
|
|
List<GameCardDto> cards = cardPack.cards;
|
||
|
|
if (startCardId != null) {
|
||
|
|
final startCardIndex = cards.indexWhere(
|
||
|
|
(card) => card.id == startCardId,
|
||
|
|
);
|
||
|
|
if (startCardIndex >= 0) {
|
||
|
|
cards = [
|
||
|
|
...cards.sublist(startCardIndex),
|
||
|
|
...cards.sublist(0, startCardIndex),
|
||
|
|
];
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
cards = cards.toList()..shuffle();
|
||
|
|
}
|
||
|
|
GameCardDto? activeCard() => cardSwiperController.currentIndex >= 0
|
||
|
|
? cards[cardSwiperController.currentIndex % cards.length]
|
||
|
|
: null;
|
||
|
|
|
||
|
|
return PopScope(
|
||
|
|
canPop: false,
|
||
|
|
onPopInvoked: (v) {
|
||
|
|
if (!v) {
|
||
|
|
appRouter.popForced(activeCard()?.id);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
child: Material(
|
||
|
|
type: MaterialType.transparency,
|
||
|
|
child: LayoutBuilder(builder: (context, constraints) {
|
||
|
|
final cardWidth = constraints.maxWidth;
|
||
|
|
final cardHeight = cardWidth;
|
||
|
|
bool enabled = true;
|
||
|
|
return StatefulBuilder(builder: (context, setState) {
|
||
|
|
return Stack(
|
||
|
|
children: [
|
||
|
|
_EndWidget(cardSwiperController, flipCardController, () {
|
||
|
|
setState(() {
|
||
|
|
enabled = false;
|
||
|
|
});
|
||
|
|
AutoRouter.of(context).maybePop();
|
||
|
|
}),
|
||
|
|
if (enabled)
|
||
|
|
Hero(
|
||
|
|
tag: activeCard()?.id ?? 'empty',
|
||
|
|
child: StreamBuilder<bool>(
|
||
|
|
stream: cardSwiperController.asStream
|
||
|
|
.map((index) => /*cards[index].isAd*/ false)
|
||
|
|
.pairwise()
|
||
|
|
.asyncMap((pair) async {
|
||
|
|
if (pair.first) {
|
||
|
|
await Future.delayed(Duration(seconds: 5));
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return pair.last;
|
||
|
|
}).timeout(Duration(seconds: 5),
|
||
|
|
onTimeout: (_) => false),
|
||
|
|
builder: (context, snapshot) {
|
||
|
|
return AbsorbPointer(
|
||
|
|
absorbing: snapshot.data ?? false,
|
||
|
|
child: CardSwiper(
|
||
|
|
controller: cardSwiperController,
|
||
|
|
padding: const EdgeInsets.symmetric(
|
||
|
|
vertical: 0.0,
|
||
|
|
horizontal: 0.0,
|
||
|
|
),
|
||
|
|
cardsCount: length,
|
||
|
|
numberOfCardsDisplayed: numberOfCardsDisplayed,
|
||
|
|
allowedSwipeDirection:
|
||
|
|
AllowedSwipeDirection.symmetric(
|
||
|
|
horizontal: true,
|
||
|
|
),
|
||
|
|
scale: initialScale,
|
||
|
|
backCardOffset: Offset(0, cardHeight * 0.1 / 2),
|
||
|
|
isLoop: false,
|
||
|
|
cardBuilder: (
|
||
|
|
context,
|
||
|
|
cardIndex,
|
||
|
|
percentThresholdX,
|
||
|
|
percentThresholdY,
|
||
|
|
scale,
|
||
|
|
) {
|
||
|
|
final index = cardIndex % cards.length;
|
||
|
|
final isFirstCard =
|
||
|
|
(cardSwiperController.currentIndex ==
|
||
|
|
cardIndex);
|
||
|
|
if (cardSwiperController.currentIndex ==
|
||
|
|
index) {
|
||
|
|
lastThreshold = percentThresholdX;
|
||
|
|
}
|
||
|
|
print('${cards[index].translation} $scale');
|
||
|
|
final cardWidget = Center(
|
||
|
|
child: GameCardWidget(
|
||
|
|
cards[index],
|
||
|
|
cards[index].id.memoryImage,
|
||
|
|
flipCardController,
|
||
|
|
flipCardController.state
|
||
|
|
// || cards[index].isAd
|
||
|
|
,
|
||
|
|
isFirstCard,
|
||
|
|
isFirstCard
|
||
|
|
? cardPack.color?.asColor
|
||
|
|
: cardPack.color?.asColor?.darken(
|
||
|
|
(scale - 1) /
|
||
|
|
(initialScale - 1) *
|
||
|
|
0.075,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
return cardWidget;
|
||
|
|
},
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
);
|
||
|
|
});
|
||
|
|
}),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class _EndWidget extends StatelessWidget {
|
||
|
|
final CardSwiperController cardSwiperController;
|
||
|
|
final FlipCardController flipCardController;
|
||
|
|
final VoidCallback callback;
|
||
|
|
|
||
|
|
_EndWidget(
|
||
|
|
this.cardSwiperController,
|
||
|
|
this.flipCardController,
|
||
|
|
this.callback,
|
||
|
|
);
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
return Center(
|
||
|
|
child: Column(
|
||
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
||
|
|
children: [
|
||
|
|
Text('Ok: ${cardSwiperController.swipedLeftIds.length}'),
|
||
|
|
Text('Repeat: ${cardSwiperController.swipedRightIds.length}'),
|
||
|
|
MaterialButton(
|
||
|
|
onPressed: () {
|
||
|
|
cardSwiperController.restart();
|
||
|
|
},
|
||
|
|
child: Text('restart'),
|
||
|
|
),
|
||
|
|
MaterialButton(
|
||
|
|
onPressed: () {
|
||
|
|
callback();
|
||
|
|
},
|
||
|
|
child: Text('to pack'),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|