Skip to content

Commit

Permalink
Add memory task game
Browse files Browse the repository at this point in the history
  • Loading branch information
AhsanSarwar45 committed Sep 27, 2024
1 parent df9fe07 commit 8672373
Show file tree
Hide file tree
Showing 5 changed files with 274 additions and 7 deletions.
17 changes: 17 additions & 0 deletions lib/alarm/data/alarm_task_schemas.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:clock_app/alarm/types/alarm_task.dart';
import 'package:clock_app/alarm/widgets/tasks/math_task.dart';
import 'package:clock_app/alarm/widgets/tasks/memory_task.dart';
import 'package:clock_app/alarm/widgets/tasks/retype_task.dart';
import 'package:clock_app/alarm/widgets/tasks/sequence_task.dart';
import 'package:clock_app/settings/types/setting.dart';
Expand Down Expand Up @@ -98,4 +99,20 @@ Map<AlarmTaskType, AlarmTaskSchema> alarmTaskSchemasMap = {
return SequenceTask(onSolve: onSolve, settings: settings);
},
),
AlarmTaskType.memory: AlarmTaskSchema(
(context) => AppLocalizations.of(context)!.memoryTask,
SettingGroup("memorySettings",
(context) => AppLocalizations.of(context)!.memoryTask, [
SliderSetting(
"numberOfPairs",
(context) => AppLocalizations.of(context)!.numberOfPairsSetting,
3,
10,
3,
snapLength: 1),
]),
(onSolve, settings) {
return MemoryTask(onSolve: onSolve, settings: settings);
},
),
};
245 changes: 245 additions & 0 deletions lib/alarm/widgets/tasks/memory_task.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
import 'dart:async';
import 'dart:math';

import 'package:clock_app/common/widgets/card_container.dart';
import 'package:clock_app/settings/types/setting_group.dart';
import 'package:flutter/material.dart';

class MemoryTask extends StatefulWidget {
const MemoryTask({
super.key,
required this.onSolve,
required this.settings,
});

final VoidCallback onSolve;
final SettingGroup settings;

@override
State<MemoryTask> createState() => _MemoryTaskState();
}

class _MemoryTaskState extends State<MemoryTask> with TickerProviderStateMixin {
late final int numberOfPairs =
widget.settings.getSetting("numberOfPairs").value.toInt();

late List<CardModel> _cards;
CardModel? _firstCard;
bool _isWaiting = false;

@override
void initState() {
super.initState();
_initializeCards();
}

void _initializeCards() {
// Generate pairs of cards
List<int> cardValues = [
...List.generate(numberOfPairs, (index) => index + 1),
...List.generate(numberOfPairs, (index) => index + 1)
];
// cardValues.addAll(cardValues); // Duplicate for pairs
cardValues.shuffle(); // Shuffle the cards

_cards = cardValues
.map((value) => CardModel(value: value, isFlipped: false))
.toList();
}

void _onCardTap(CardModel card) {
if (_isWaiting || card.isFlipped) return;

setState(() {
card.isFlipped = true;
});

if (_firstCard == null) {
_firstCard = card;
} else {
if (_firstCard!.value == card.value) {
// Match found
_firstCard!.isCompleted = true;
card.isCompleted = true;
_firstCard = null;

if (_cards.every((card) => card.isFlipped)) {
// All cards are flipped
Future.delayed(const Duration(seconds: 1), () {
widget.onSolve();
});
}
} else {
// No match, flip back after delay
_isWaiting = true;
Future.delayed(const Duration(seconds: 1), () {
setState(() {
card.isFlipped = false;
_firstCard!.isFlipped = false;
_firstCard = null;
_isWaiting = false;
});
});
}
}
}

@override
Widget build(BuildContext context) {
ThemeData theme = Theme.of(context);
ColorScheme colorScheme = theme.colorScheme;
TextTheme textTheme = theme.textTheme;
int gridSize = (sqrt(_cards.length)).floor();

return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Text(
"Match card pairs",
style: textTheme.headlineMedium,
),
const SizedBox(height: 16.0),
SizedBox(
width: double.infinity,
// height: 512,
child: GridView.builder(
itemCount: _cards.length,
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: gridSize,
),
itemBuilder: (context, index) {
CardModel card = _cards[index];
return GestureDetector(
key: ValueKey(card),
onTap: () => _onCardTap(card),
child: FlipCard(
isFlipped: card.isFlipped,
front: CardContainer(
margin: const EdgeInsets.all(4.0),
color: colorScheme.primary,
child: Center(
child: Text(
'?',
style: textTheme.displayMedium?.copyWith(
color: colorScheme.onPrimary,
),
),
),
),
back: CardContainer(
margin: const EdgeInsets.all(4.0),
color: card.isCompleted ? Colors.green : Colors.orangeAccent,
child: Center(
child: Text(
'${card.value}',
style: textTheme.displayMedium?.copyWith(
color: Colors.white,

),
),
),
),
),
);
},
),
),
],
),
);
}
}

class CardModel {
final int value;
bool isCompleted = false;
bool isFlipped;

CardModel({required this.value, this.isFlipped = false});
}

class FlipCard extends StatefulWidget {
final Widget front;
final Widget back;
final bool isFlipped;

const FlipCard({
super.key,
required this.front,
required this.back,
required this.isFlipped,
});

@override
State<FlipCard> createState() => _FlipCardState();
}

class _FlipCardState extends State<FlipCard>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;

@override
void initState() {
super.initState();

_controller = AnimationController(
duration: const Duration(milliseconds: 400),
vsync: this,
);

_animation = Tween<double>(begin: 0, end: 1).animate(_controller);

if (widget.isFlipped) {
_controller.value = 1;
}
}

@override
void didUpdateWidget(FlipCard oldWidget) {
super.didUpdateWidget(oldWidget);

if (widget.isFlipped != oldWidget.isFlipped) {
if (widget.isFlipped) {
_controller.forward();
} else {
_controller.reverse();
}
}
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
final angle = _animation.value * pi;
final transform = Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(angle);

Widget content;
if (angle <= pi / 2) {
content = widget.front;
} else {
content = widget.back;
transform.rotateY(pi);
}

return Transform(
transform: transform,
alignment: Alignment.center,
child: content,
);
},
);
}
}
4 changes: 2 additions & 2 deletions lib/alarm/widgets/tasks/sequence_task.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import 'package:flutter/material.dart';

class SequenceTask extends StatefulWidget {
const SequenceTask({
Key? key,
super.key,
required this.onSolve,
required this.settings,
}) : super(key: key);
});

final VoidCallback onSolve;
final SettingGroup settings;
Expand Down
4 changes: 3 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,9 @@
"@sequenceLengthSetting": {},
"sequenceGridSizeSetting": "Grid size",
"@sequenceGridSizeSetting": {},
"numberOfProblemsSetting": "Number of Problems",
"memoryTask": "Memory",
"numberOfPairsSetting": "Number of pairs",
"numberOfProblemsSetting": "Number of problems",
"@numberOfProblemsSetting": {},
"saveReminderAlert": "Do you want to leave without saving?",
"@saveReminderAlert": {},
Expand Down
11 changes: 7 additions & 4 deletions lib/system/logic/handle_boot.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ void handleBoot() async {
// File('$appDataDirectory/log-dart.txt')
// .writeAsStringSync(message, mode: FileMode.append);
//
FlutterError.onError = (FlutterErrorDetails details) {
FlutterError.onError = (FlutterErrorDetails details) {
logger.f("Error in handleBoot isolate: ${details.exception.toString()}");
};

await initializeIsolate();

await updateAlarms("handleBoot(): Update alarms on system boot");
await updateTimers("handleBoot(): Update timers on system boot");
try {
await updateAlarms("handleBoot(): Update alarms on system boot");
await updateTimers("handleBoot(): Update timers on system boot");
} catch (e) {
logger.f("Error in handleBoot isolate: ${e.toString()}");
}
}

0 comments on commit 8672373

Please sign in to comment.