rename container folders

This commit is contained in:
2026-01-29 11:11:45 +02:00
parent d5349d981c
commit 5b052a1fa9
654 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,152 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_circle_avatar.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_icons.dart';
import 'package:mzansi_innovation_hub/mih_providers/mih_mine_sweeper_provider.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import 'package:provider/provider.dart';
class BuildMinesweeperLeaderboardList extends StatefulWidget {
const BuildMinesweeperLeaderboardList({super.key});
@override
State<BuildMinesweeperLeaderboardList> createState() =>
_BuildMinesweeperLeaderboardListState();
}
class _BuildMinesweeperLeaderboardListState
extends State<BuildMinesweeperLeaderboardList> {
Color getMedalColor(int index) {
switch (index) {
case (0):
return MihColors.getGoldColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark");
case (1):
return MihColors.getSilverColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark");
case (2):
return MihColors.getBronze(
MzansiInnovationHub.of(context)!.theme.mode == "Dark");
default:
return MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark");
}
}
@override
Widget build(BuildContext context) {
final double width = MediaQuery.sizeOf(context).width;
return Consumer2<MzansiProfileProvider, MihMineSweeperProvider>(
builder: (BuildContext context, MzansiProfileProvider profileProvider,
MihMineSweeperProvider mineSweeperProvider, Widget? child) {
return ListView.separated(
separatorBuilder: (BuildContext context, index) {
return Divider(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
);
},
itemCount: mineSweeperProvider.leaderboard!.length,
itemBuilder: (context, index) {
return FutureBuilder(
future: mineSweeperProvider.leaderboardUserPicturesUrl[index],
builder: (context, asyncSnapshot) {
ImageProvider<Object>? imageFile;
bool loading = true;
if (asyncSnapshot.connectionState == ConnectionState.done) {
loading = false;
if (asyncSnapshot.hasData) {
imageFile = asyncSnapshot.requireData != ""
? CachedNetworkImageProvider(
asyncSnapshot.requireData)
: null;
} else {
imageFile = null;
}
} else {
imageFile = null;
}
return Padding(
padding: EdgeInsets.symmetric(horizontal: width / 20),
child: Row(
children: [
Text(
"#${index + 1}",
style: TextStyle(
fontSize: 25,
color: getMedalColor(index),
),
),
const SizedBox(width: 10),
loading
? Icon(
MihIcons.mihRing,
size: 80,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
)
: imageFile == null
? Icon(
MihIcons.iDontKnow,
size: 80,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
)
: MihCircleAvatar(
key: UniqueKey(),
imageFile: imageFile,
width: 80,
editable: false,
fileNameController: null,
userSelectedfile: null,
frameColor: getMedalColor(index),
backgroundColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
onChange: () {},
),
const SizedBox(width: 10),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${mineSweeperProvider.leaderboard![index].username}${profileProvider.user!.username == mineSweeperProvider.leaderboard![index].username ? " (You)" : ""}",
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: getMedalColor(index),
),
),
Text(
"Score: ${mineSweeperProvider.leaderboard![index].game_score}\nTime: ${mineSweeperProvider.leaderboard![index].game_time}",
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 18,
// fontWeight: FontWeight.bold,
color: getMedalColor(index),
),
),
],
)
],
),
);
});
},
);
},
);
}
}

View File

@@ -0,0 +1,94 @@
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_providers/mih_mine_sweeper_provider.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import 'package:provider/provider.dart';
class BuildMyScoreBoardList extends StatefulWidget {
const BuildMyScoreBoardList({super.key});
@override
State<BuildMyScoreBoardList> createState() =>
_BuildMinesweeperLeaderboardListState();
}
class _BuildMinesweeperLeaderboardListState
extends State<BuildMyScoreBoardList> {
Color getMedalColor(int index) {
switch (index) {
case (0):
return MihColors.getGoldColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark");
case (1):
return MihColors.getSilverColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark");
case (2):
return MihColors.getBronze(
MzansiInnovationHub.of(context)!.theme.mode == "Dark");
default:
return MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark");
}
}
@override
Widget build(BuildContext context) {
final double width = MediaQuery.sizeOf(context).width;
return Consumer2<MzansiProfileProvider, MihMineSweeperProvider>(
builder: (BuildContext context, MzansiProfileProvider profileProvider,
MihMineSweeperProvider mineSweeperProvider, Widget? child) {
return ListView.separated(
separatorBuilder: (BuildContext context, index) {
return Divider(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
);
},
itemCount: mineSweeperProvider.myScoreboard!.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: width / 20),
child: Row(
children: [
Text(
"#${index + 1}",
style: TextStyle(
fontSize: 25,
color: getMedalColor(index),
),
),
const SizedBox(width: 10),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Score: ${mineSweeperProvider.myScoreboard![index].game_score}",
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: getMedalColor(index),
),
),
Text(
"Time: ${mineSweeperProvider.myScoreboard![index].game_time}",
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 18,
// fontWeight: FontWeight.bold,
color: getMedalColor(index),
),
),
],
)
],
),
);
},
);
},
);
}
}

View File

@@ -0,0 +1,13 @@
class BoardSquare {
bool hasBomb;
int bombsAround;
bool isOpened;
bool isFlagged;
BoardSquare({
this.hasBomb = false,
this.bombsAround = 0,
this.isOpened = false,
this.isFlagged = false,
});
}

View File

@@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
import 'package:ken_logger/ken_logger.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_circle_avatar.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import 'package:redacted/redacted.dart';
class LeaderboardUserRanking extends StatelessWidget {
final int index;
final String proPicUrl;
final String username;
final dynamic gameScore;
final String gameTime;
final bool isCurrentUser;
final Future<ImageProvider<Object>?> Function(String) getUserPicture;
const LeaderboardUserRanking({
super.key,
required this.index,
required this.proPicUrl,
required this.username,
required this.gameScore,
required this.gameTime,
required this.isCurrentUser,
required this.getUserPicture,
});
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getUserPicture(proPicUrl),
builder: (context, asyncSnapshot) {
bool isLoading =
asyncSnapshot.connectionState == ConnectionState.waiting;
KenLogger.success("URL: ${asyncSnapshot.data.toString()}");
return ListTile(
leading: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"#${index + 1}",
style: const TextStyle(
fontSize: 25,
),
),
const SizedBox(width: 10),
MihCircleAvatar(
key: ValueKey(asyncSnapshot.data
.toString()), // Use ValueKey for stable identity
imageFile: asyncSnapshot.data,
width: 60,
editable: false,
fileNameController: null,
userSelectedfile: null,
frameColor: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
backgroundColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
onChange: () {},
),
],
),
title: Text(
"$username${isCurrentUser ? " (You)" : ""}",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
).redacted(context: context, redact: isLoading),
subtitle: Text(
"Score: $gameScore\nTime: $gameTime",
style: TextStyle(
fontSize: 18,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
).redacted(context: context, redact: isLoading),
);
},
);
}
}

View File

@@ -0,0 +1,174 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_dropdwn_field.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_form.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_window.dart';
import 'package:mzansi_innovation_hub/mih_providers/mih_mine_sweeper_provider.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import 'package:provider/provider.dart';
class MihMineSweeperStartGameWindow extends StatefulWidget {
final void Function()? onPressed;
const MihMineSweeperStartGameWindow({
super.key,
required this.onPressed,
});
@override
State<MihMineSweeperStartGameWindow> createState() =>
_MihMineSweeperStartGameWindowState();
}
class _MihMineSweeperStartGameWindowState
extends State<MihMineSweeperStartGameWindow> {
TextEditingController modeController = TextEditingController();
final _formKey = GlobalKey<FormState>();
void applyGameSettings(MihMineSweeperProvider mihMineSweeperProvider) {
mihMineSweeperProvider.setDifficulty(modeController.text);
switch (mihMineSweeperProvider.difficulty) {
case ("Very Easy"):
mihMineSweeperProvider.setRowCount(6);
mihMineSweeperProvider.setCoulmnCount(6);
mihMineSweeperProvider.setTotalMines(5);
// mihMineSweeperProvider.setRowCount(5);
// mihMineSweeperProvider.setCoulmnCount(5);
// mihMineSweeperProvider.setTotalMines(3);
break;
case ("Easy"):
mihMineSweeperProvider.setRowCount(8);
mihMineSweeperProvider.setCoulmnCount(8);
mihMineSweeperProvider.setTotalMines(10);
// mihMineSweeperProvider.setRowCount(10);
// mihMineSweeperProvider.setCoulmnCount(10);
// mihMineSweeperProvider.setTotalMines(15);
break;
case ("Intermediate"):
mihMineSweeperProvider.setRowCount(10);
mihMineSweeperProvider.setCoulmnCount(10);
mihMineSweeperProvider.setTotalMines(18);
// mihMineSweeperProvider.setRowCount(15);
// mihMineSweeperProvider.setCoulmnCount(10);
// mihMineSweeperProvider.setTotalMines(23);
break;
case ("Hard"):
mihMineSweeperProvider.setRowCount(12);
mihMineSweeperProvider.setCoulmnCount(10);
mihMineSweeperProvider.setTotalMines(30);
// mihMineSweeperProvider.setRowCount(20);
// mihMineSweeperProvider.setCoulmnCount(10);
// mihMineSweeperProvider.setTotalMines(30);
break;
default:
break;
}
}
String getModeConfig() {
switch (modeController.text) {
case ("Very Easy"):
return "Columns: 6\nRows: 6\nBombs: 5";
case ("Easy"):
return "Columns: 8\nRows: 8\nBombs: 10";
case ("Intermediate"):
return "Columns: 10\nRows: 10\nBombs: 18";
case ("Hard"):
return "Columns: 10\nRows: 12\nBombs: 30";
default:
return "Error";
}
}
void _onModeChanged() {
setState(() {});
}
@override
void dispose() {
modeController.removeListener(_onModeChanged);
modeController.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
modeController.text = context.read<MihMineSweeperProvider>().difficulty;
modeController.addListener(_onModeChanged);
}
@override
Widget build(BuildContext context) {
return MihPackageWindow(
fullscreen: false,
windowTitle: "New Game Settings",
onWindowTapClose: () {
context.pop();
},
windowBody: Consumer<MihMineSweeperProvider>(
builder: (BuildContext context,
MihMineSweeperProvider mihMineSweeperProvider, Widget? child) {
return Column(
children: [
MihForm(
formKey: _formKey,
formFields: [
MihDropdownField(
controller: modeController,
hintText: "Difficulty",
dropdownOptions: [
"Very Easy",
"Easy",
"Intermediate",
"Hard"
],
requiredText: true,
editable: true,
enableSearch: false,
),
const SizedBox(height: 10),
Text(
getModeConfig(),
style: TextStyle(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 25),
Center(
child: MihButton(
onPressed: () {
applyGameSettings(mihMineSweeperProvider);
context.pop();
widget.onPressed?.call();
},
buttonColor: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
width: 300,
child: Text(
"Start Game",
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
],
);
},
),
);
}
}

View File

@@ -0,0 +1,112 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/components/board_square.dart';
class MineTile extends StatelessWidget {
final BoardSquare square;
final VoidCallback onTap;
final VoidCallback onLongPress;
const MineTile({
super.key,
required this.square,
required this.onTap,
required this.onLongPress,
});
Widget _getTileContent(BuildContext context) {
if (square.isFlagged) {
return Icon(
Icons.flag,
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode != "Dark",
),
);
}
if (square.isOpened) {
if (square.hasBomb) {
return const Icon(FontAwesomeIcons.bomb, color: Colors.black);
} else if (square.bombsAround > 0) {
// Display bomb count
return Center(
child: Text(
'${square.bombsAround}',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: _getTileColor(square.bombsAround, context),
),
),
);
} else {
// Opened, but no bomb count (empty square)
return const SizedBox.shrink();
}
}
// Default: Unopened tile
return const SizedBox.shrink();
}
Color _getTileColor(int bombsAround, BuildContext context) {
// Choose colors based on standard Minesweeper appearance
switch (bombsAround) {
case 1:
return MihColors.getBluishPurpleColor(
MzansiInnovationHub.of(context)!.theme.mode != "Dark",
);
// return Colors.blue;
case 2:
return MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode != "Dark",
);
// return Colors.green;
case 3:
return MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode != "Dark",
);
// return Colors.red;
case 4:
return MihColors.getPurpleColor(
MzansiInnovationHub.of(context)!.theme.mode != "Dark",
);
// return Colors.purple;
case 5:
return MihColors.getOrangeColor(
MzansiInnovationHub.of(context)!.theme.mode != "Dark",
);
// return Colors.brown;
default:
// return MihColors.getBluishPurpleColor(
// MzansiInnovationHub.of(context)!.theme.mode == "Dark",
// );
return Colors.black;
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(1.0),
child: MihButton(
onPressed: onTap,
onLongPressed: onLongPress,
buttonColor: square.isOpened
? MihColors.getGreyColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark",
)
: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark",
),
width: 50,
height: 50,
borderRadius: 3,
child: _getTileContent(context),
),
);
}
}

View File

@@ -0,0 +1,141 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:go_router/go_router.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_action.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tools.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_loading_circle.dart';
import 'package:mzansi_innovation_hub/mih_providers/mih_banner_ad_provider.dart';
import 'package:mzansi_innovation_hub/mih_providers/mih_mine_sweeper_provider.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart';
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/package_tools/mih_mine_sweeper_leader_board.dart';
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/package_tools/mine_sweeper_game.dart';
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/package_tools/mine_sweeper_quick_start_guide.dart';
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/package_tools/my_score_board.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_data_helper_services.dart';
import 'package:provider/provider.dart';
class MihMineSweeper extends StatefulWidget {
const MihMineSweeper({super.key});
@override
State<MihMineSweeper> createState() => _MihMineSweeperState();
}
class _MihMineSweeperState extends State<MihMineSweeper> {
bool _isLoadingInitialData = true;
late final MineSweeperGame _mineSweeperGame;
late final MihMineSweeperLeaderBoard _mineSweeperLeaderBoard;
late final MyScoreBoard _myScoreBoard;
late final MineSweeperQuickStartGuide _mineSweeperQuickStartGuide;
Future<void> _loadInitialData() async {
setState(() {
_isLoadingInitialData = true;
});
MzansiProfileProvider mzansiProfileProvider =
context.read<MzansiProfileProvider>();
MihBannerAdProvider bannerAdProvider = context.read<MihBannerAdProvider>();
if (mzansiProfileProvider.user == null) {
await MihDataHelperServices().loadUserDataOnly(
mzansiProfileProvider,
);
}
bannerAdProvider.loadBannerAd();
setState(() {
_isLoadingInitialData = false;
});
}
@override
void initState() {
super.initState();
_mineSweeperGame = MineSweeperGame();
_mineSweeperLeaderBoard = MihMineSweeperLeaderBoard();
_myScoreBoard = MyScoreBoard();
_mineSweeperQuickStartGuide = MineSweeperQuickStartGuide();
_loadInitialData();
}
@override
Widget build(BuildContext context) {
return Consumer<MihMineSweeperProvider>(
builder:
(BuildContext context, MihMineSweeperProvider value, Widget? child) {
if (_isLoadingInitialData) {
return Scaffold(
body: Center(
child: Mihloadingcircle(),
),
);
}
return MihPackage(
appActionButton: getAction(),
appTools: getTools(),
appToolTitles: getToolTitle(),
appBody: getToolBody(),
selectedbodyIndex: context.watch<MihMineSweeperProvider>().toolIndex,
onIndexChange: (newIndex) {
context.read<MihMineSweeperProvider>().setToolIndex(newIndex);
},
);
},
);
}
MihPackageAction getAction() {
return MihPackageAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
MihMineSweeperProvider mineSweeperProvider =
context.read<MihMineSweeperProvider>();
mineSweeperProvider.setToolIndex(0);
mineSweeperProvider.setDifficulty("Easy");
context.goNamed(
'mihHome',
);
FocusScope.of(context).unfocus();
},
);
}
MihPackageTools getTools() {
Map<Widget, void Function()?> temp = {};
temp[const Icon(FontAwesomeIcons.bomb)] = () {
context.read<MihMineSweeperProvider>().setToolIndex(0);
};
temp[const Icon(Icons.leaderboard_rounded)] = () {
context.read<MihMineSweeperProvider>().setToolIndex(1);
};
temp[const Icon(Icons.perm_identity_rounded)] = () {
context.read<MihMineSweeperProvider>().setToolIndex(2);
};
temp[const Icon(Icons.rule_rounded)] = () {
context.read<MihMineSweeperProvider>().setToolIndex(3);
};
return MihPackageTools(
tools: temp,
selcetedIndex: context.watch<MihMineSweeperProvider>().toolIndex,
);
}
List<String> getToolTitle() {
List<String> toolTitles = [
"Minesweeper",
"Leader Board",
"My Scores",
"Guide",
];
return toolTitles;
}
List<Widget> getToolBody() {
return [
_mineSweeperGame,
_mineSweeperLeaderBoard,
_myScoreBoard,
_mineSweeperQuickStartGuide,
];
}
}

View File

@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_icons.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tile.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
class MihMineSweeperTile extends StatefulWidget {
final double packageSize;
const MihMineSweeperTile({
super.key,
required this.packageSize,
});
@override
State<MihMineSweeperTile> createState() => _MihMineSweeperTileState();
}
class _MihMineSweeperTileState extends State<MihMineSweeperTile> {
@override
Widget build(BuildContext context) {
return MihPackageTile(
onTap: () {
context.goNamed(
"mihMinesweeper",
);
},
appName: "Minesweeper",
appIcon: Icon(
MihIcons.mineSweeper,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
// size: widget.packageSize,
),
iconSize: widget.packageSize,
textColor: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
);
}
}

View File

@@ -0,0 +1,201 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:ken_logger/ken_logger.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_dropdwn_field.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_icons.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tool_body.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_loading_circle.dart';
import 'package:mzansi_innovation_hub/mih_providers/mih_mine_sweeper_provider.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/builders/build_minesweeper_leaderboard_list.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_minesweeper_services.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart';
import 'package:provider/provider.dart';
class MihMineSweeperLeaderBoard extends StatefulWidget {
const MihMineSweeperLeaderBoard({super.key});
@override
State<MihMineSweeperLeaderBoard> createState() =>
_MihMineSweeperLeaderBoardState();
}
class _MihMineSweeperLeaderBoardState extends State<MihMineSweeperLeaderBoard> {
TextEditingController filterController = TextEditingController();
bool isLoading = true;
Future<void> initialiseLeaderboard() async {
MihMineSweeperProvider mineSweeperProvider =
context.read<MihMineSweeperProvider>();
filterController.text = mineSweeperProvider.difficulty;
KenLogger.success("getting data");
await MihMinesweeperServices().getTop20Leaderboard(mineSweeperProvider);
List<Future<String>> userPicturesUrl = [];
Future<String> userPicUrl;
for (final ranking in mineSweeperProvider.leaderboard!) {
userPicUrl = MihFileApi.getMinioFileUrl(ranking.proPicUrl);
userPicturesUrl.add(userPicUrl);
}
mineSweeperProvider.setLeaderboardUserPictures(
leaderboardUserPicturesUrl: userPicturesUrl);
setState(() {
isLoading = false;
});
}
void refreshLeaderBoard(
MihMineSweeperProvider mineSweeperProvider, String difficulty) {
setState(() {
isLoading = true;
});
mineSweeperProvider.setDifficulty(difficulty);
mineSweeperProvider.setLeaderboard(leaderboard: null);
mineSweeperProvider.setMyScoreboard(myScoreboard: null);
initialiseLeaderboard();
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await initialiseLeaderboard();
});
}
@override
Widget build(BuildContext context) {
final double width = MediaQuery.sizeOf(context).width;
return Consumer<MihMineSweeperProvider>(
builder: (BuildContext context,
MihMineSweeperProvider mineSweeperProvider, Widget? child) {
return RefreshIndicator(
onRefresh: () async {
refreshLeaderBoard(mineSweeperProvider, filterController.text);
},
child: MihPackageToolBody(
borderOn: false,
bodyItem: getBody(width),
),
);
},
);
}
Widget getBody(double width) {
return Consumer<MihMineSweeperProvider>(
builder: (BuildContext context,
MihMineSweeperProvider mineSweeperProvider, Widget? child) {
if (isLoading) {
return Center(
child: Mihloadingcircle(),
);
} else {
return Column(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: width / 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
child: MihDropdownField(
controller: filterController,
hintText: "Leaderboards",
dropdownOptions: const [
"Very Easy",
"Easy",
"Intermediate",
"Hard",
],
requiredText: true,
editable: true,
enableSearch: false,
validator: (value) {
return MihValidationServices().isEmpty(value);
},
onSelected: (selection) {
refreshLeaderBoard(mineSweeperProvider, selection!);
},
),
),
],
),
),
const SizedBox(height: 10),
!isLoading && mineSweeperProvider.leaderboard!.isEmpty
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 50),
Icon(
MihIcons.mineSweeper,
size: 165,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
const SizedBox(height: 10),
Text(
"Be the first on the leaderboard.",
textAlign: TextAlign.center,
overflow: TextOverflow.visible,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
),
const SizedBox(height: 25),
Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.normal,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
),
children: [
TextSpan(text: "Press "),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(
FontAwesomeIcons.bomb,
size: 20,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
),
),
TextSpan(text: " and start a new game"),
],
),
),
),
],
),
)
: Expanded(
child: BuildMinesweeperLeaderboardList(),
),
],
);
}
},
);
}
}

View File

@@ -0,0 +1,860 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:go_router/go_router.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_banner_ad.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_button.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_floating_menu.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_icons.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tool_body.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_window.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_single_child_scroll.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_loading_circle.dart';
import 'package:mzansi_innovation_hub/mih_providers/mih_mine_sweeper_provider.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/components/board_square.dart';
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/components/mih_mine_sweeper_start_game_window.dart';
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/components/mine_tile.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_minesweeper_services.dart';
import 'package:provider/provider.dart';
class MineSweeperGame extends StatefulWidget {
const MineSweeperGame({super.key});
@override
State<MineSweeperGame> createState() => _MineSweeperGameState();
}
class _MineSweeperGameState extends State<MineSweeperGame> {
List<List<BoardSquare>> board = [];
bool isGameOver = false;
bool isGameWon = false;
int squaresLeft = -1;
Timer? _timer;
int _milliseconds = 0;
bool _isRunning = false;
static const int millisecondsPerUpdate = 100;
double timeStringToTotalSeconds(String timeString) {
try {
List<String> parts = timeString.split(':');
if (parts.length != 4) {
return 0.0;
}
double hours = double.parse(parts[0]);
double minutes = double.parse(parts[1]);
double seconds = double.parse(parts[2]);
double milliseconds = double.parse(parts[3]);
double totalSeconds =
(hours * 3600) + (minutes * 60) + seconds + (milliseconds / 100);
return totalSeconds;
} catch (e) {
print("Error parsing time string: $e");
return 0.0;
}
}
double calculateGameScore(MihMineSweeperProvider mihMineSweeperProvider) {
int scoreConst = 10000;
double dificusltyMultiplier;
switch (mihMineSweeperProvider.difficulty) {
case ("Very Easy"):
dificusltyMultiplier = 0.5;
break;
case ("Easy"):
dificusltyMultiplier = 1.0;
break;
case ("Intermediate"):
dificusltyMultiplier = 2.5;
break;
case ("Hard"):
dificusltyMultiplier = 5.0;
break;
default:
dificusltyMultiplier = 0.0;
break;
}
double rawScore = (scoreConst * dificusltyMultiplier) /
timeStringToTotalSeconds(_formatTime());
String scoreString = rawScore.toStringAsFixed(5);
return double.parse(scoreString);
}
void startTimer() {
if (_isRunning) return;
_isRunning = true;
_timer = Timer.periodic(const Duration(milliseconds: millisecondsPerUpdate),
(timer) {
setState(() {
_milliseconds += millisecondsPerUpdate; // Increment by the interval
});
});
}
void stopTimer() {
_timer?.cancel();
setState(() {
_isRunning = false;
});
}
void resetTimer() {
stopTimer(); // Stop the timer first
setState(() {
_milliseconds = 0; // Reset the time to zero
});
}
String _formatTime() {
Duration duration = Duration(milliseconds: _milliseconds);
final int hours = duration.inHours;
final int minutes = duration.inMinutes.remainder(60);
final int seconds = duration.inSeconds.remainder(60);
final int centiseconds = (duration.inMilliseconds.remainder(1000)) ~/ 10;
String hoursStr = hours.toString().padLeft(2, '0');
String minutesStr = minutes.toString().padLeft(2, '0');
String secondsStr = seconds.toString().padLeft(2, '0');
String centiStr = centiseconds.toString().padLeft(2, '0');
return '$hoursStr:$minutesStr:$secondsStr:$centiStr';
}
void showStartGameWindow(
MihMineSweeperProvider mihMineSweeperProvider,
) {
// easy - 10 * 10 & 15 bombs
// Intermediate - 10 * 15 & 23 bombs
// Hard - 10 * 20 & 30 bombs
showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return MihMineSweeperStartGameWindow(
onPressed: () {
resetTimer();
mihMineSweeperProvider
.setDifficulty(mihMineSweeperProvider.difficulty);
setState(() => initializeBoard(mihMineSweeperProvider));
},
);
});
}
// --- GAME INITIALIZATION LOGIC ---
void initializeBoard(MihMineSweeperProvider mihMineSweeperProvider) {
// 1. Create a board of empty squares
board = List.generate(
mihMineSweeperProvider.rowCount,
(i) => List.generate(
mihMineSweeperProvider.columnCount,
(j) => BoardSquare(),
),
);
// 2. Place bombs randomly
placeBombs(mihMineSweeperProvider);
// 3. Calculate the number of bombs around each non-mine square
calculateBombsAround(mihMineSweeperProvider);
// Reset state variables
squaresLeft =
mihMineSweeperProvider.rowCount * mihMineSweeperProvider.columnCount;
isGameOver = false;
isGameWon = false;
// You'd typically add a call to setState here, but it's in initState.
startTimer();
}
void placeBombs(MihMineSweeperProvider mihMineSweeperProvider) {
final Random random = Random();
int bombsPlaced = 0;
while (bombsPlaced < mihMineSweeperProvider.totalMines) {
int r = random.nextInt(mihMineSweeperProvider.rowCount);
int c = random.nextInt(mihMineSweeperProvider.columnCount);
if (!board[r][c].hasBomb) {
board[r][c].hasBomb = true;
bombsPlaced++;
}
}
}
void calculateBombsAround(MihMineSweeperProvider mihMineSweeperProvider) {
for (int r = 0; r < mihMineSweeperProvider.rowCount; r++) {
for (int c = 0; c < mihMineSweeperProvider.columnCount; c++) {
if (!board[r][c].hasBomb) {
int count = 0;
// Check the 8 neighbors
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
if (i == 0 && j == 0) continue; // Skip the current square
int neighborR = r + i;
int neighborC = c + j;
// Check if neighbor is within bounds
if (neighborR >= 0 &&
neighborR < mihMineSweeperProvider.rowCount &&
neighborC >= 0 &&
neighborC < mihMineSweeperProvider.columnCount) {
if (board[neighborR][neighborC].hasBomb) {
count++;
}
}
}
}
board[r][c].bombsAround = count;
}
}
}
}
// Handles recursive opening of zero-squares
void _expandZeros(
MihMineSweeperProvider mihMineSweeperProvider, int r, int c) {
if (r < 0 ||
r >= mihMineSweeperProvider.rowCount ||
c < 0 ||
c >= mihMineSweeperProvider.columnCount ||
board[r][c].isOpened) {
return;
}
BoardSquare square = board[r][c];
// Open the current square
square.isOpened = true;
squaresLeft--;
// If it's a zero square, recursively call for neighbors
if (square.bombsAround == 0) {
// Check all 8 neighbors (not just 4 sides, for standard Minesweeper expansion)
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
_expandZeros(mihMineSweeperProvider, r + i, c + j);
}
}
}
}
Future<void> handleTap(MzansiProfileProvider profileProvider,
MihMineSweeperProvider mihMineSweeperProvider, int r, int c) async {
if (isGameOver || board[r][c].isOpened || board[r][c].isFlagged) {
return;
}
// 1. Check for bomb (LOSS)
if (board[r][c].hasBomb) {
stopTimer();
setState(() {
board[r][c].isOpened = true;
isGameOver = true;
// lose alert
loseAlert(mihMineSweeperProvider);
});
return;
}
// 2. Open square and handle expansion (RECURSION)
if (board[r][c].bombsAround == 0) {
// Start recursive expansion
_expandZeros(mihMineSweeperProvider, r, c);
} else {
// Just open the single square
board[r][c].isOpened = true;
squaresLeft--;
}
// 3. Check for win
_checkWinCondition(profileProvider, mihMineSweeperProvider);
// Update the UI
setState(() {});
}
void handleLongPress(int r, int c) {
if (isGameOver || board[r][c].isOpened) {
return;
}
setState(() {
// Toggle the flag status
board[r][c].isFlagged = !board[r][c].isFlagged;
});
}
// --- GAME ACTION LOGIC ---
Future<void> _checkWinCondition(
MzansiProfileProvider profileProvider,
MihMineSweeperProvider mihMineSweeperProvider,
) async {
// Game is won if all non-mine squares are opened.
if (squaresLeft <= mihMineSweeperProvider.totalMines) {
stopTimer();
isGameWon = true;
isGameOver = true;
// win alert
winAlert(mihMineSweeperProvider);
showDialog(
context: context,
builder: (context) {
return Mihloadingcircle(
message: "Uploading your score",
);
});
await MihMinesweeperServices().addPlayerScore(
profileProvider,
mihMineSweeperProvider,
_formatTime().replaceAll("00:", ""),
calculateGameScore(mihMineSweeperProvider),
);
context.pop();
}
}
Color? getDifficultyColor(MihMineSweeperProvider mihMineSweeperProvider) {
String mode = mihMineSweeperProvider.difficulty;
switch (mode) {
case "Very Easy":
return MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark",
);
case "Easy":
return MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode != "Dark",
);
case "Intermediate":
return MihColors.getOrangeColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark",
);
case "Hard":
return MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark",
);
default:
return null;
}
}
void loseAlert(MihMineSweeperProvider mihMineSweeperProvider) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return MihPackageWindow(
fullscreen: false,
windowTitle: null,
onWindowTapClose: null,
backgroundColor: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode != "Dark"),
windowBody: Column(
children: [
const SizedBox(height: 10),
Icon(
FontAwesomeIcons.bomb,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
size: 125,
),
const SizedBox(height: 10),
Text(
"Better Luck Next Time",
textAlign: TextAlign.center,
style: TextStyle(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Center(
child: Text(
"Your lost this game of MIH Minesweeper!!!",
textAlign: TextAlign.center,
style: TextStyle(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 15),
Text(
"Please feel free to start a New Game or check out the Leader Board to find out who's the best in Mzansi.",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
),
const SizedBox(height: 20),
Wrap(
runAlignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
alignment: WrapAlignment.center,
spacing: 10,
runSpacing: 10,
children: [
MihButton(
onPressed: () {
context.pop();
showStartGameWindow(mihMineSweeperProvider);
},
buttonColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
width: 300,
child: Text(
"New Game",
style: TextStyle(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
MihButton(
onPressed: () {
context.pop();
},
buttonColor: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
width: 300,
child: Text(
"View Board",
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
MihButton(
onPressed: () {
context.pop();
mihMineSweeperProvider.setToolIndex(1);
},
buttonColor: MihColors.getGoldColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
width: 300,
child: Text(
"Leader Board",
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
);
},
);
}
void winAlert(MihMineSweeperProvider mihMineSweeperProvider) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return MihPackageWindow(
fullscreen: false,
windowTitle: null,
onWindowTapClose: null,
backgroundColor: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
windowBody: Column(
children: [
const SizedBox(height: 10),
Icon(
Icons.celebration,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
size: 150,
),
const SizedBox(height: 10),
Text(
"Congratulations",
textAlign: TextAlign.center,
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"Your won this game of MIH Minesweeper!!!",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
),
const SizedBox(height: 10),
Text(
"Time Taken: ${_formatTime().replaceAll("00:", "")}",
style: TextStyle(
fontSize: 20,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
),
const SizedBox(height: 10),
Text(
"Score: ${calculateGameScore(mihMineSweeperProvider)}",
style: TextStyle(
fontSize: 20,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
),
const SizedBox(height: 20),
Wrap(
runAlignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
alignment: WrapAlignment.center,
spacing: 10,
runSpacing: 10,
children: [
MihButton(
onPressed: () {
context.pop();
showStartGameWindow(mihMineSweeperProvider);
},
buttonColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
width: 300,
child: Text(
"New Game",
style: TextStyle(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
MihButton(
onPressed: () {
context.pop();
},
buttonColor: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
width: 300,
child: Text(
"View Board",
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
MihButton(
onPressed: () {
mihMineSweeperProvider.setLeaderboard(leaderboard: null);
context.pop();
mihMineSweeperProvider.setToolIndex(1);
},
buttonColor: MihColors.getGoldColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
width: 300,
child: Text(
"Leader Board",
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
);
},
);
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
void initState() {
// UBongani was here during the MIH Live
super.initState();
}
@override
Widget build(BuildContext context) {
return MihPackageToolBody(
borderOn: false,
bodyItem: getBody(),
);
}
Widget getBody() {
return Consumer2<MzansiProfileProvider, MihMineSweeperProvider>(
builder: (BuildContext context, MzansiProfileProvider profileProvider,
MihMineSweeperProvider mihMineSweeperProvider, Widget? child) {
return Column(
children: [
Expanded(
child: Stack(
alignment: Alignment.topCenter,
children: [
MihSingleChildScroll(
child: board.isEmpty && squaresLeft < 0
// Start Up Message before setting up game
? Padding(
padding:
const EdgeInsets.symmetric(horizontal: 10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 50),
Icon(
MihIcons.mineSweeper,
size: 165,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
),
const SizedBox(height: 10),
Text(
"Welcom to Minesweeper, the first game of MIH.",
textAlign: TextAlign.center,
overflow: TextOverflow.visible,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
),
),
const SizedBox(height: 25),
Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.normal,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
),
children: [
TextSpan(text: "Press "),
WidgetSpan(
alignment:
PlaceholderAlignment.middle,
child: Icon(
Icons.menu,
size: 20,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
),
),
TextSpan(
text:
" to start a new game or learn how to play the minesweeper."),
],
),
),
),
],
),
)
// Display Game Board when game started
: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Display game status
Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0),
child: Text(
'Mines: ${mihMineSweeperProvider.totalMines}',
textAlign: TextAlign.left,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0),
child: Text(
_formatTime().replaceAll("00:", ""),
textAlign: TextAlign.right,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold),
),
),
),
],
),
Text(
mihMineSweeperProvider.difficulty,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: getDifficultyColor(
mihMineSweeperProvider),
),
),
// const SizedBox(
// height: 30,
// ),
// The Board Grid
SizedBox(
width: mihMineSweeperProvider.columnCount *
40.0, // Control size based on columns
height: mihMineSweeperProvider.rowCount *
40.0, // Control size based on rows
child: GridView.builder(
physics:
const NeverScrollableScrollPhysics(), // Prevent scrolling
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount:
mihMineSweeperProvider.columnCount,
crossAxisSpacing: 0,
mainAxisSpacing: 0,
),
itemCount: mihMineSweeperProvider.rowCount *
mihMineSweeperProvider.columnCount,
itemBuilder: (context, index) {
int r = index ~/
mihMineSweeperProvider
.columnCount; // Integer division for row
int c = index %
mihMineSweeperProvider
.columnCount; // Remainder for column
return MineTile(
square: board[r][c],
onTap: () => handleTap(profileProvider,
mihMineSweeperProvider, r, c),
onLongPress: () => handleLongPress(r, c),
);
},
),
),
SizedBox(height: 30),
// const SizedBox(height: 100),
],
),
),
Positioned(
right: 10,
bottom: 10,
child: MihFloatingMenu(
animatedIcon: AnimatedIcons.menu_close,
children: [
SpeedDialChild(
child: Icon(
Icons.rule_rounded,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
label: "Learn how to play",
labelBackgroundColor: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
labelStyle: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
),
backgroundColor: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
onTap: () {
mihMineSweeperProvider.setToolIndex(3);
},
),
SpeedDialChild(
child: Icon(
Icons.add,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
label: "Start New Game",
labelBackgroundColor: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
labelStyle: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
),
backgroundColor: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
onTap: () {
showStartGameWindow(mihMineSweeperProvider);
},
),
]),
)
],
),
),
_timer != null ? MihBannerAd() : SizedBox(),
SizedBox(height: 15),
],
);
},
);
}
}

View File

@@ -0,0 +1,880 @@
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tool_body.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_single_child_scroll.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
class MineSweeperQuickStartGuide extends StatefulWidget {
const MineSweeperQuickStartGuide({super.key});
@override
State<MineSweeperQuickStartGuide> createState() =>
_MineSweeperQuickStartGuideState();
}
class _MineSweeperQuickStartGuideState
extends State<MineSweeperQuickStartGuide> {
double titleSize = 22.0;
double subtitleSize = 20.0;
double pointsSize = 18.0;
Widget sectionOne() {
return Container(
decoration: BoxDecoration(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode != "Darl"),
borderRadius: BorderRadius.circular(15),
),
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
children: [
// Title
Text(
"1. Two Main Actions\n(Your Controls)",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: titleSize,
fontWeight: FontWeight.bold,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
),
const SizedBox(height: 8),
//Part One
RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: 'Quick Tap (or Click): This is the Dig action.',
style: TextStyle(
color: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: subtitleSize,
),
),
],
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Align(
alignment: Alignment.topLeft,
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: '• Goal:',
style: TextStyle(
color: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text: ' To uncover a square and see a number clue.',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
],
),
),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Align(
alignment: Alignment.centerLeft,
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: '• Risk:',
style: TextStyle(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text: ' If you click a mine, the game ends!',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
],
),
),
),
),
const SizedBox(height: 8),
//Part Two
RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text:
'Tap and Hold (or Long Press): This is the Flag action (🚩).',
style: TextStyle(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: subtitleSize,
),
),
],
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Align(
alignment: Alignment.topLeft,
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: '• Goal:',
style: TextStyle(
color: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text: ' To safely mark a square that you are',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
TextSpan(
text: ' certain',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text: ' is a mine.',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
],
),
),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Align(
alignment: Alignment.centerLeft,
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: '• Risk:',
style: TextStyle(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text:
' Accidental placement of flags will cause confusion.',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
],
),
),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Align(
alignment: Alignment.centerLeft,
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: '• Benefit:',
style: TextStyle(
color: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text:
' You cannot accidentally click a square that is flagged.',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
],
),
),
),
),
],
),
),
);
}
Widget sectionTwo() {
return Container(
decoration: BoxDecoration(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode != "Darl"),
borderRadius: BorderRadius.circular(15),
),
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
children: [
// Title
Text(
"2. The Golden Rule\n(Reading the Numbers)",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: titleSize,
fontWeight: FontWeight.bold,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
),
const SizedBox(height: 8),
//Part One
RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text:
'The number tells you exactly how many mines are touching that square (including sides and corners).',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: subtitleSize,
),
),
],
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Align(
alignment: Alignment.topLeft,
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: "• If you see a Blank Space (a '0'):",
style: TextStyle(
color: MihColors.getOrangeColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text: " Zero (0) ",
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text:
' mines are touching it. All surrounding squares are safe, and the game will open them for you automatically.',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
],
),
),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Align(
alignment: Alignment.centerLeft,
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: "• If you see a '1':",
style: TextStyle(
color: MihColors.getOrangeColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text: ' Only ',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
TextSpan(
text: 'one',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text:
' mine is touching this square. You must find and flag that single mine.',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
],
),
),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Align(
alignment: Alignment.topLeft,
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: "• If you see a '3':",
style: TextStyle(
color: MihColors.getOrangeColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text: " Three ",
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text:
'mines are touching this square. You must find and flag all three.',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
],
),
),
),
),
],
),
),
);
}
Widget sectionThree() {
return Container(
decoration: BoxDecoration(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode != "Darl"),
borderRadius: BorderRadius.circular(15),
),
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
children: [
// Title
Text(
"3. The Winning Strategy\n(The Deduction Loop)",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: titleSize,
fontWeight: FontWeight.bold,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
),
const SizedBox(height: 8),
//Part One
RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text:
'The game is won by uncovering every single safe square and correctly flagging all the mines. Use this two-step loop to clear the board:',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: subtitleSize,
),
),
],
),
),
const SizedBox(height: 8),
RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: 'A. Find the Mines (Where to Flag 🚩)',
style: TextStyle(
color: MihColors.getPurpleColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: subtitleSize,
),
),
],
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Align(
alignment: Alignment.topLeft,
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: '• Goal:',
style: TextStyle(
color: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text:
' Look for a number that only has one choice for a mine. e.g. If a \'1\' is touching only one hidden square, that hidden square',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
TextSpan(
text: ' must ',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text: 'be the mine.',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
],
),
),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Align(
alignment: Alignment.centerLeft,
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: '• Action:',
style: TextStyle(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text: ' Tap and Hold to place a',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
TextSpan(
text: ' Flag ',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text: 'on the square you are sure is a mine.',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
],
),
),
),
),
const SizedBox(height: 8),
//Part Two
RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: 'B. Find the Safe Squares (Where to Dig)',
style: TextStyle(
color: MihColors.getPurpleColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: subtitleSize,
),
),
],
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Align(
alignment: Alignment.topLeft,
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: '• Goal:',
style: TextStyle(
color: MihColors.getGreenColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text:
' Look for a number that has been \'satisfied\' by your flags. e.g. You see a \'2\' and you have already placed two 🚩 flags touching it. The \'2\' is satisfied.',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
],
),
),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Align(
alignment: Alignment.centerLeft,
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: '• Action:',
style: TextStyle(
color: MihColors.getRedColor(
MzansiInnovationHub.of(context)!.theme.mode !=
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text:
' Quick Tap any of the remaining hidden squares touching that \'satisfied\' number. They',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
TextSpan(
text: ' must be safe ',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text:
'because the mine requirement has already been met.',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
],
),
),
),
),
],
),
),
);
}
Widget sectionFour() {
return Container(
decoration: BoxDecoration(
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode != "Darl"),
borderRadius: BorderRadius.circular(15),
),
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
children: [
// Title
Text(
"✨ Key Beginner Tips",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: titleSize,
fontWeight: FontWeight.bold,
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Align(
alignment: Alignment.topLeft,
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: "• Start on the Edges and Corners: ",
style: TextStyle(
color: MihColors.getBronze(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text:
'Numbers on the edge or corner of the board are easier to solve because they have fewer surrounding squares to check.',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
],
),
),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Align(
alignment: Alignment.topLeft,
child: RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: "• Don't Guess: ",
style: TextStyle(
color: MihColors.getBronze(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.bold,
fontSize: pointsSize,
),
),
TextSpan(
text:
'If you are down to two squares and either one could be the mine, look somewhere else on the board for a guaranteed, safe move.',
style: TextStyle(
color: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
fontWeight: FontWeight.normal,
fontSize: pointsSize,
),
),
],
),
),
),
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
final Size size = MediaQuery.sizeOf(context);
final double width = size.width;
return MihPackageToolBody(
borderOn: false,
bodyItem: getBody(width),
);
}
Widget getBody(double width) {
return MihSingleChildScroll(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: width / 20),
child: Column(
children: [
const Text(
'Simple Rules and Strategy',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
const Text(
'Minesweeper is a puzzle game where you use numbers to figure out where the hidden bombs (mines) are located.',
style: TextStyle(fontSize: 18),
),
// const Divider(height: 30),
const SizedBox(height: 15),
sectionOne(),
const SizedBox(height: 15),
sectionTwo(),
const SizedBox(height: 15),
sectionThree(),
const SizedBox(height: 15),
sectionFour(),
const SizedBox(height: 15),
],
),
),
);
}
}

View File

@@ -0,0 +1,204 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:ken_logger/ken_logger.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_circle_avatar.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_dropdwn_field.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_icons.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_package_tool_body.dart';
import 'package:mzansi_innovation_hub/mih_package_components/mih_loading_circle.dart';
import 'package:mzansi_innovation_hub/mih_providers/mih_mine_sweeper_provider.dart';
import 'package:mzansi_innovation_hub/mih_providers/mzansi_profile_provider.dart';
import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart';
import 'package:mzansi_innovation_hub/mih_packages/mine_sweeper/builders/build_my_scoreboard_list.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_minesweeper_services.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart';
import 'package:provider/provider.dart';
class MyScoreBoard extends StatefulWidget {
const MyScoreBoard({super.key});
@override
State<MyScoreBoard> createState() => _MihMineSweeperLeaderBoardState();
}
class _MihMineSweeperLeaderBoardState extends State<MyScoreBoard> {
TextEditingController filterController = TextEditingController();
Future<void> initialiseLeaderboard() async {
MzansiProfileProvider profileProvider =
context.read<MzansiProfileProvider>();
MihMineSweeperProvider mineSweeperProvider =
context.read<MihMineSweeperProvider>();
filterController.text = mineSweeperProvider.difficulty;
KenLogger.success("getting data");
await MihMinesweeperServices()
.getMyScoreboard(profileProvider, mineSweeperProvider);
KenLogger.success("${mineSweeperProvider.myScoreboard}");
}
void refreshLeaderBoard(
MihMineSweeperProvider mineSweeperProvider, String difficulty) {
mineSweeperProvider.setDifficulty(difficulty);
mineSweeperProvider.setLeaderboard(leaderboard: null);
mineSweeperProvider.setMyScoreboard(myScoreboard: null);
initialiseLeaderboard();
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await initialiseLeaderboard();
});
}
@override
Widget build(BuildContext context) {
final double width = MediaQuery.sizeOf(context).width;
return Consumer<MihMineSweeperProvider>(
builder: (BuildContext context,
MihMineSweeperProvider mineSweeperProvider, Widget? child) {
return RefreshIndicator(
onRefresh: () async {
refreshLeaderBoard(mineSweeperProvider, filterController.text);
},
child: MihPackageToolBody(
borderOn: false,
bodyItem: getBody(width),
),
);
},
);
}
Widget getBody(double width) {
return Consumer2<MzansiProfileProvider, MihMineSweeperProvider>(
builder: (BuildContext context, MzansiProfileProvider profileProvider,
MihMineSweeperProvider mineSweeperProvider, Widget? child) {
if (mineSweeperProvider.myScoreboard == null) {
return Center(
child: Mihloadingcircle(),
);
} else {
return Column(
children: [
Center(
child: MihCircleAvatar(
imageFile: profileProvider.userProfilePicture,
width: 150,
editable: false,
fileNameController: null,
userSelectedfile: null,
frameColor: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
backgroundColor: MihColors.getPrimaryColor(
MzansiInnovationHub.of(context)!.theme.mode == "Dark"),
onChange: (selectedImage) {},
key: ValueKey(profileProvider.userProfilePicUrl),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: width / 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
child: MihDropdownField(
controller: filterController,
hintText: "Scoreboards",
dropdownOptions: const [
"Very Easy",
"Easy",
"Intermediate",
"Hard",
],
requiredText: true,
editable: true,
enableSearch: false,
validator: (value) {
return MihValidationServices().isEmpty(value);
},
onSelected: (selection) {
refreshLeaderBoard(mineSweeperProvider, selection!);
},
),
),
],
),
),
const SizedBox(height: 10),
mineSweeperProvider.myScoreboard!.isEmpty
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 50),
Icon(
MihIcons.mineSweeper,
size: 165,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
const SizedBox(height: 10),
Text(
"You have played and ${mineSweeperProvider.difficulty} yet.",
textAlign: TextAlign.center,
overflow: TextOverflow.visible,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!.theme.mode ==
"Dark"),
),
),
const SizedBox(height: 25),
Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.normal,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
),
children: [
TextSpan(text: "Press "),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(
FontAwesomeIcons.bomb,
size: 20,
color: MihColors.getSecondaryColor(
MzansiInnovationHub.of(context)!
.theme
.mode ==
"Dark"),
),
),
TextSpan(text: " and start a new game"),
],
),
),
),
],
),
)
: Expanded(child: BuildMyScoreBoardList()),
],
);
}
},
);
}
}