Skip to content
Snippets Groups Projects
Commit 375f7369 authored by Benoît Harrault's avatar Benoît Harrault
Browse files

Merge branch '25-clean-improve-update-code' into 'master'

Resolve "Clean / improve / update code"

Closes #25

See merge request !25
parents 326bb9a5 ff2bbaef
No related branches found
No related tags found
1 merge request!25Resolve "Clean / improve / update code"
Pipeline #5863 passed
Showing
with 1055 additions and 361 deletions
part of 'settings_global_cubit.dart';
@immutable
class GlobalSettingsState extends Equatable {
const GlobalSettingsState({
required this.settings,
});
final GlobalSettings settings;
@override
List<dynamic> get props => <dynamic>[
settings,
];
}
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
part 'theme_state.dart';
class ThemeCubit extends HydratedCubit<ThemeModeState> {
ThemeCubit() : super(const ThemeModeState());
void getTheme(ThemeModeState state) {
emit(state);
}
@override
ThemeModeState? fromJson(Map<String, dynamic> json) {
switch (json['themeMode']) {
case 'ThemeMode.dark':
return const ThemeModeState(themeMode: ThemeMode.dark);
case 'ThemeMode.light':
return const ThemeModeState(themeMode: ThemeMode.light);
case 'ThemeMode.system':
default:
return const ThemeModeState(themeMode: ThemeMode.system);
}
}
@override
Map<String, String>? toJson(ThemeModeState state) {
return <String, String>{'themeMode': state.themeMode.toString()};
}
}
part of 'theme_cubit.dart';
@immutable
class ThemeModeState extends Equatable {
const ThemeModeState({
this.themeMode,
});
final ThemeMode? themeMode;
@override
List<Object?> get props => <Object?>[
themeMode,
];
}
import 'package:memory/models/tile_model.dart';
String selectedTile = "";
int selectedIndex = 0;
bool selected = true;
int points = 0;
List<TileModel> myPairs = [];
List<bool> clicked = [];
List<bool> getClicked() {
List<bool> yoClicked = [];
List<TileModel> myPairs = getPairs();
for (int i = 0; i < myPairs.length; i++) {
yoClicked[i] = false;
}
return yoClicked;
}
List<TileModel> getPairs() {
List<TileModel> pairs = [];
TileModel tileModel = TileModel();
//1
tileModel.setImageAssetPath("assets/fox.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
tileModel = TileModel();
//2
tileModel.setImageAssetPath("assets/hippo.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
tileModel = TileModel();
//3
tileModel.setImageAssetPath("assets/horse.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
tileModel = TileModel();
//4
tileModel.setImageAssetPath("assets/monkey.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
tileModel = TileModel();
//5
tileModel.setImageAssetPath("assets/panda.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
tileModel = TileModel();
//6
tileModel.setImageAssetPath("assets/parrot.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
tileModel = TileModel();
//7
tileModel.setImageAssetPath("assets/rabbit.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
tileModel = TileModel();
//8
tileModel.setImageAssetPath("assets/zoo.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
return pairs;
}
List<TileModel> getQuestionPairs() {
List<TileModel> pairs = [];
TileModel tileModel = TileModel();
//1
tileModel.setImageAssetPath("assets/question.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
tileModel = TileModel();
//2
tileModel.setImageAssetPath("assets/question.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
tileModel = TileModel();
//3
tileModel.setImageAssetPath("assets/question.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
tileModel = TileModel();
//4
tileModel.setImageAssetPath("assets/question.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
tileModel = TileModel();
//5
tileModel.setImageAssetPath("assets/question.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
tileModel = TileModel();
//6
tileModel.setImageAssetPath("assets/question.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
tileModel = TileModel();
//7
tileModel.setImageAssetPath("assets/question.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
tileModel = TileModel();
//8
tileModel.setImageAssetPath("assets/question.png");
tileModel.setIsSelected(false);
pairs.add(tileModel);
pairs.add(tileModel);
return pairs;
}
import 'dart:async';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive/hive.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:path_provider/path_provider.dart';
import 'package:memory/config/theme.dart';
import 'package:memory/cubit/game_cubit.dart';
import 'package:memory/cubit/nav_cubit.dart';
import 'package:memory/cubit/settings_game_cubit.dart';
import 'package:memory/cubit/settings_global_cubit.dart';
import 'package:memory/cubit/theme_cubit.dart';
import 'package:memory/ui/skeleton.dart';
void main() async {
// Initialize packages
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
final Directory tmpDir = await getTemporaryDirectory();
Hive.init(tmpDir.toString());
HydratedBloc.storage = await HydratedStorage.build(
storageDirectory: tmpDir,
);
import 'package:memory/data/data.dart';
import 'package:memory/models/tile_model.dart';
import 'package:memory/utils/tools.dart';
void main() => runApp(const MyApp());
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((value) => runApp(EasyLocalization(
path: 'assets/translations',
supportedLocales: const <Locale>[
Locale('en'),
Locale('fr'),
],
fallbackLocale: const Locale('en'),
useFallbackTranslations: true,
child: const MyApp(),
)));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Home(),
);
}
final List<String> assets = getImagesAssets();
for (String asset in assets) {
precacheImage(AssetImage(asset), context);
}
class Home extends StatefulWidget {
const Home({super.key});
@override
HomeState createState() => HomeState();
}
class HomeState extends State<Home> {
List<TileModel> gridViewTiles = [];
List<TileModel> questionPairs = [];
@override
void initState() {
super.initState();
reStart();
}
void reStart() {
myPairs = getPairs();
myPairs.shuffle();
gridViewTiles = myPairs;
Future.delayed(const Duration(seconds: 3), () {
setState(() {
questionPairs = getQuestionPairs();
gridViewTiles = questionPairs;
selected = false;
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 50),
child: Column(
children: <Widget>[
const SizedBox(
height: 40,
),
points != 800
? Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
"$points/800",
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
const Text(
"Points",
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w300,
),
),
return MultiBlocProvider(
providers: [
BlocProvider<NavCubit>(create: (context) => NavCubit()),
BlocProvider<ThemeCubit>(create: (context) => ThemeCubit()),
BlocProvider<GameCubit>(create: (context) => GameCubit()),
BlocProvider<GlobalSettingsCubit>(create: (context) => GlobalSettingsCubit()),
BlocProvider<GameSettingsCubit>(create: (context) => GameSettingsCubit()),
],
)
: Container(),
const SizedBox(
height: 20,
),
points != 800
? GridView(
shrinkWrap: true,
//physics: ClampingScrollPhysics(),
scrollDirection: Axis.vertical,
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
mainAxisSpacing: 0.0,
maxCrossAxisExtent: 100.0,
),
children: List.generate(gridViewTiles.length, (index) {
return Tile(
imagePathUrl: gridViewTiles[index].getImageAssetPath(),
tileIndex: index,
parent: this,
child: BlocBuilder<ThemeCubit, ThemeModeState>(
builder: (BuildContext context, ThemeModeState state) {
return MaterialApp(
title: 'Memory',
home: const SkeletonScreen(),
// Theme stuff
theme: lightTheme,
darkTheme: darkTheme,
themeMode: state.themeMode,
// Localization stuff
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
debugShowCheckedModeBanner: false,
);
}),
)
: Column(
children: <Widget>[
GestureDetector(
onTap: () {
setState(() {
points = 0;
reStart();
});
},
child: Container(
height: 50,
width: 200,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(24),
),
child: const Text(
"Replay",
style: TextStyle(
color: Colors.white,
fontSize: 17,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
],
),
),
),
);
}
}
class Tile extends StatefulWidget {
final String imagePathUrl;
final int tileIndex;
final HomeState? parent;
List<String> getImagesAssets() {
final List<String> assets = [];
const Tile({super.key, this.imagePathUrl = '', this.tileIndex = 0, this.parent});
const List<String> gameImages = [
'button_back',
'button_delete_saved_game',
'button_resume_game',
'button_start',
'game_win',
'placeholder',
];
@override
TileState createState() => TileState();
for (String image in gameImages) {
assets.add('assets/ui/$image.png');
}
class TileState extends State<Tile> {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
if (!selected) {
setState(() {
myPairs[widget.tileIndex].setIsSelected(true);
});
if (selectedTile != "") {
/// testing if the selected tiles are same
if (selectedTile == myPairs[widget.tileIndex].getImageAssetPath()) {
printlog("add point");
points = points + 100;
printlog("$selectedTile -> ${widget.imagePathUrl}");
TileModel tileModel = TileModel();
printlog(widget.tileIndex.toString());
selected = true;
Future.delayed(const Duration(seconds: 2), () {
tileModel.setImageAssetPath("");
myPairs[widget.tileIndex] = tileModel;
printlog(selectedIndex.toString());
myPairs[selectedIndex] = tileModel;
widget.parent?.setState(() {});
setState(() {
selected = false;
});
selectedTile = "";
});
} else {
printlog("$selectedTile -> ${myPairs[widget.tileIndex].getImageAssetPath()}");
printlog("wrong choice");
printlog(widget.tileIndex.toString());
printlog(selectedIndex.toString());
selected = true;
Future.delayed(const Duration(seconds: 2), () {
widget.parent?.setState(() {
myPairs[widget.tileIndex].setIsSelected(false);
myPairs[selectedIndex].setIsSelected(false);
});
setState(() {
selected = false;
});
});
selectedTile = "";
List<String> skinImages = [
'found',
'unknown',
];
for (int value = 1; value <= 8; value++) {
skinImages.add(value.toString());
}
} else {
setState(() {
selectedTile = myPairs[widget.tileIndex].getImageAssetPath();
selectedIndex = widget.tileIndex;
});
printlog(selectedTile);
printlog(selectedIndex.toString());
}
}
},
child: Container(
margin: const EdgeInsets.all(5),
child: myPairs[widget.tileIndex].getImageAssetPath() != ""
? Image.asset(myPairs[widget.tileIndex].getIsSelected()
? myPairs[widget.tileIndex].getImageAssetPath()
: widget.imagePathUrl)
: Container(
color: Colors.white,
child: Image.asset("assets/correct.png"),
),
),
);
return assets;
}
}
import 'dart:math';
import 'package:memory/models/game/tile.dart';
import 'package:memory/models/settings/settings_game.dart';
import 'package:memory/utils/tools.dart';
class Board {
Board({
required this.tiles,
});
List<Tile> tiles = const [];
factory Board.createNull() {
return Board(
tiles: [],
);
}
factory Board.createNew({
GameSettings? gameSettings,
}) {
List<Tile> tiles = [];
const int itemsCount = 8;
for (int i = 1; i <= itemsCount; i++) {
tiles.add(Tile(
value: i,
paired: false,
selected: false,
));
tiles.add(Tile(
value: i,
paired: false,
selected: false,
));
}
tiles.shuffle();
return Board(
tiles: tiles,
);
}
void dump() {
printlog('');
printlog('$Board:');
dumpGrid();
printlog('');
}
void dumpGrid() {
final int rowsCount = sqrt(tiles.length).toInt();
final int colsCount = tiles.length ~/ rowsCount;
for (int row = 0; row < rowsCount; row++) {
String line = ' ';
for (int col = 0; col < colsCount; col++) {
final int tileIndex = col + row * colsCount;
final Tile tile = tiles[tileIndex];
line += '[${tile.value}${tile.selected ? 'S' : '.'}${tile.paired ? 'P' : '.'}]';
}
printlog(line);
}
}
@override
String toString() {
return '$Board(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
'tiles': tiles,
};
}
}
import 'package:memory/models/game/board.dart';
import 'package:memory/models/settings/settings_game.dart';
import 'package:memory/models/settings/settings_global.dart';
import 'package:memory/utils/tools.dart';
typedef MovingTile = String;
typedef Move = Board;
typedef Player = String;
typedef ConflictsCount = List<List<int>>;
typedef AnimatedBoard = List<List<bool>>;
typedef AnimatedBoardSequence = List<AnimatedBoard>;
typedef Word = String;
class Game {
Game({
// Settings
required this.gameSettings,
required this.globalSettings,
// State
this.isRunning = false,
this.isStarted = false,
this.isFinished = false,
this.animationInProgress = false,
this.shufflingInProgress = false,
// Base data
required this.board,
// Game data
required this.movesCount,
required this.pairsFound,
});
// Settings
final GameSettings gameSettings;
final GlobalSettings globalSettings;
// State
bool isRunning;
bool isStarted;
bool isFinished;
bool animationInProgress;
bool shufflingInProgress;
// Base data
final Board board;
// Game data
int movesCount;
List<int> pairsFound = [];
factory Game.createNull() {
return Game(
// Settings
gameSettings: GameSettings.createDefault(),
globalSettings: GlobalSettings.createDefault(),
// Base data
board: Board.createNull(),
// Game data
movesCount: 0,
pairsFound: [],
);
}
factory Game.createNew({
GameSettings? gameSettings,
GlobalSettings? globalSettings,
}) {
final GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault();
final GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault();
final Board board = Board.createNew(
gameSettings: newGameSettings,
);
return Game(
// Settings
gameSettings: newGameSettings,
globalSettings: newGlobalSettings,
// State
isRunning: true,
// Base data
board: board,
// Game data
movesCount: 0,
pairsFound: [],
);
}
bool get canBeResumed => isStarted && !isFinished;
bool get gameWon {
return pairsFound.length == board.tiles.length ~/ 2;
}
void dump() {
printlog('');
printlog('## Current game dump:');
printlog('');
printlog('$Game:');
printlog(' Settings');
gameSettings.dump();
globalSettings.dump();
printlog(' State');
printlog(' isRunning: $isRunning');
printlog(' isStarted: $isStarted');
printlog(' isFinished: $isFinished');
printlog(' animationInProgress: $animationInProgress');
printlog(' shufflingInProgress: $shufflingInProgress');
printlog(' Base data');
board.dump();
printlog(' Game data');
printlog(' movesCount: $movesCount');
printlog(' pairsFound: $pairsFound');
printlog('');
}
@override
String toString() {
return '$Game(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
// Settings
'gameSettings': gameSettings.toJson(),
'globalSettings': globalSettings.toJson(),
// State
'isRunning': isRunning,
'isStarted': isStarted,
'isFinished': isFinished,
'animationInProgress': animationInProgress,
'shufflingInProgress': shufflingInProgress,
// Base data
'board': board.toJson(),
// Game data
'movesCount': movesCount,
'pairsFound': pairsFound,
};
}
}
import 'package:memory/utils/tools.dart';
class Tile {
Tile({
required this.value,
required this.selected,
required this.paired,
});
int value = 0;
bool selected = false;
bool paired = false;
factory Tile.createNull() {
return Tile(
value: 0,
selected: false,
paired: false,
);
}
void dump() {
printlog('');
printlog('$Tile:');
printlog(' value: $value');
printlog(' selected: $selected');
printlog(' paired: $paired');
printlog('');
}
@override
String toString() {
return '$Tile(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
'value': value,
'selected': selected,
'paired': paired,
};
}
}
import 'package:memory/config/default_game_settings.dart';
import 'package:memory/utils/tools.dart';
class GameSettings {
final String level;
GameSettings({
required this.level,
});
static String getLevelValueFromUnsafe(String level) {
if (DefaultGameSettings.allowedLevelValues.contains(level)) {
return level;
}
return DefaultGameSettings.defaultLevelValue;
}
factory GameSettings.createDefault() {
return GameSettings(
level: DefaultGameSettings.defaultLevelValue,
);
}
void dump() {
printlog('$GameSettings:');
printlog(' ${DefaultGameSettings.parameterCodeLevel}: $level');
printlog('');
}
@override
String toString() {
return '$GameSettings(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
DefaultGameSettings.parameterCodeLevel: level,
};
}
}
import 'package:memory/config/default_global_settings.dart';
import 'package:memory/utils/tools.dart';
class GlobalSettings {
String skin;
GlobalSettings({
required this.skin,
});
static String getSkinValueFromUnsafe(String skin) {
if (DefaultGlobalSettings.allowedSkinValues.contains(skin)) {
return skin;
}
return DefaultGlobalSettings.defaultSkinValue;
}
factory GlobalSettings.createDefault() {
return GlobalSettings(
skin: DefaultGlobalSettings.defaultSkinValue,
);
}
void dump() {
printlog('$GlobalSettings:');
printlog(' ${DefaultGlobalSettings.parameterCodeSkin}: $skin');
printlog('');
}
@override
String toString() {
return '$GlobalSettings(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
DefaultGlobalSettings.parameterCodeSkin: skin,
};
}
}
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
class AppHeader extends StatelessWidget {
const AppHeader({super.key, required this.text});
final String text;
@override
Widget build(BuildContext context) {
return Text(
tr(text),
textAlign: TextAlign.start,
style: Theme.of(context).textTheme.headlineMedium!.apply(fontWeightDelta: 2),
);
}
}
class AppTitle extends StatelessWidget {
const AppTitle({super.key, required this.text});
final String text;
@override
Widget build(BuildContext context) {
return Text(
tr(text),
textAlign: TextAlign.start,
style: Theme.of(context).textTheme.titleLarge!.apply(fontWeightDelta: 2),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:memory/cubit/game_cubit.dart';
import 'package:memory/models/game/game.dart';
import 'package:memory/ui/widgets/game/game_board.dart';
import 'package:memory/ui/widgets/game/game_bottom.dart';
import 'package:memory/ui/widgets/game/game_end.dart';
import 'package:memory/ui/widgets/game/game_top.dart';
class GameLayout extends StatelessWidget {
const GameLayout({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<GameCubit, GameState>(
builder: (BuildContext context, GameState gameState) {
final Game currentGame = gameState.currentGame;
return Container(
alignment: AlignmentDirectional.topCenter,
padding: const EdgeInsets.all(4),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const GameTopWidget(),
const SizedBox(height: 8),
const GameBoardWidget(),
const SizedBox(height: 8),
const GameBottomWidget(),
const Expanded(child: SizedBox.shrink()),
currentGame.isFinished ? const GameEndWidget() : const SizedBox.shrink(),
],
),
);
},
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:memory/config/default_game_settings.dart';
import 'package:memory/config/default_global_settings.dart';
import 'package:memory/cubit/settings_game_cubit.dart';
import 'package:memory/cubit/settings_global_cubit.dart';
import 'package:memory/ui/parameters/parameter_painter.dart';
import 'package:memory/ui/widgets/actions/button_delete_saved_game.dart';
import 'package:memory/ui/widgets/actions/button_game_start_new.dart';
import 'package:memory/ui/widgets/actions/button_resume_saved_game.dart';
import 'package:memory/ui/parameters/parameter_image.dart';
class ParametersLayout extends StatelessWidget {
const ParametersLayout({super.key, required this.canResume});
final bool canResume;
final double separatorHeight = 8.0;
@override
Widget build(BuildContext context) {
final List<Widget> lines = [];
// Game settings
for (String code in DefaultGameSettings.availableParameters) {
lines.add(Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: buildParametersLine(
code: code,
isGlobal: false,
),
));
lines.add(SizedBox(height: separatorHeight));
}
lines.add(SizedBox(height: separatorHeight));
if (canResume == false) {
// Start new game
lines.add(const Expanded(
child: StartNewGameButton(),
));
} else {
// Resume game
lines.add(const Expanded(
child: ResumeSavedGameButton(),
));
// Delete saved game
lines.add(SizedBox.square(
dimension: MediaQuery.of(context).size.width / 4,
child: const DeleteSavedGameButton(),
));
}
lines.add(SizedBox(height: separatorHeight));
// Global settings
for (String code in DefaultGlobalSettings.availableParameters) {
lines.add(Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: buildParametersLine(
code: code,
isGlobal: true,
),
));
lines.add(SizedBox(height: separatorHeight));
}
return Column(
children: lines,
);
}
List<Widget> buildParametersLine({
required String code,
required bool isGlobal,
}) {
final List<Widget> parameterButtons = [];
final List<String> availableValues = isGlobal
? DefaultGlobalSettings.getAvailableValues(code)
: DefaultGameSettings.getAvailableValues(code);
if (availableValues.length <= 1) {
return [];
}
for (String value in availableValues) {
final Widget parameterButton = BlocBuilder<GameSettingsCubit, GameSettingsState>(
builder: (BuildContext context, GameSettingsState gameSettingsState) {
return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>(
builder: (BuildContext context, GlobalSettingsState globalSettingsState) {
final GameSettingsCubit gameSettingsCubit =
BlocProvider.of<GameSettingsCubit>(context);
final GlobalSettingsCubit globalSettingsCubit =
BlocProvider.of<GlobalSettingsCubit>(context);
final String currentValue = isGlobal
? globalSettingsCubit.getParameterValue(code)
: gameSettingsCubit.getParameterValue(code);
final bool isActive = (value == currentValue);
final double displayWidth = MediaQuery.of(context).size.width;
final double itemWidth = displayWidth / availableValues.length - 26;
final bool displayedWithAssets =
DefaultGlobalSettings.displayedWithAssets.contains(code) ||
DefaultGameSettings.displayedWithAssets.contains(code);
return TextButton(
child: Container(
child: displayedWithAssets
? SizedBox.square(
dimension: itemWidth,
child: ParameterImage(
code: code,
value: value,
isSelected: isActive,
),
)
: CustomPaint(
size: Size(itemWidth, itemWidth),
willChange: false,
painter: ParameterPainter(
code: code,
value: value,
isSelected: isActive,
gameSettings: gameSettingsState.settings,
globalSettings: globalSettingsState.settings,
),
isComplex: true,
),
),
onPressed: () {
isGlobal
? globalSettingsCubit.setParameterValue(code, value)
: gameSettingsCubit.setParameterValue(code, value);
},
);
},
);
},
);
parameterButtons.add(parameterButton);
}
return parameterButtons;
}
}
import 'package:flutter/material.dart';
class ParameterImage extends StatelessWidget {
const ParameterImage({
super.key,
required this.code,
required this.value,
required this.isSelected,
});
final String code;
final String value;
final bool isSelected;
static const Color buttonBackgroundColor = Colors.white;
static const Color buttonBorderColorActive = Colors.blue;
static const Color buttonBorderColorInactive = Colors.white;
static const double buttonBorderWidth = 8.0;
static const double buttonBorderRadius = 8.0;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: buttonBackgroundColor,
borderRadius: BorderRadius.circular(buttonBorderRadius),
border: Border.all(
color: isSelected ? buttonBorderColorActive : buttonBorderColorInactive,
width: buttonBorderWidth,
),
),
child: Image(
image: AssetImage('assets/ui/${code}_$value.png'),
fit: BoxFit.fill,
),
);
}
}
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:memory/models/settings/settings_game.dart';
import 'package:memory/models/settings/settings_global.dart';
import 'package:memory/utils/tools.dart';
class ParameterPainter extends CustomPainter {
const ParameterPainter({
required this.code,
required this.value,
required this.isSelected,
required this.gameSettings,
required this.globalSettings,
});
final String code;
final String value;
final bool isSelected;
final GameSettings gameSettings;
final GlobalSettings globalSettings;
@override
void paint(Canvas canvas, Size size) {
// force square
final double canvasSize = min(size.width, size.height);
const Color borderColorEnabled = Colors.blue;
const Color borderColorDisabled = Colors.white;
// "enabled/disabled" border
final paint = Paint();
paint.style = PaintingStyle.stroke;
paint.color = isSelected ? borderColorEnabled : borderColorDisabled;
paint.strokeJoin = StrokeJoin.round;
paint.strokeWidth = 10;
canvas.drawRect(
Rect.fromPoints(const Offset(0, 0), Offset(canvasSize, canvasSize)), paint);
// content
switch (code) {
default:
printlog('Unknown parameter: $code/$value');
paintUnknownParameterItem(value, canvas, canvasSize);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
// "unknown" parameter -> simple block with text
void paintUnknownParameterItem(
final String value,
final Canvas canvas,
final double size,
) {
final paint = Paint();
paint.strokeJoin = StrokeJoin.round;
paint.strokeWidth = 3;
paint.color = Colors.grey;
paint.style = PaintingStyle.fill;
canvas.drawRect(Rect.fromPoints(const Offset(0, 0), Offset(size, size)), paint);
final textSpan = TextSpan(
text: '?\n$value',
style: const TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.bold,
),
);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
textAlign: TextAlign.center,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
(size - textPainter.width) * 0.5,
(size - textPainter.height) * 0.5,
),
);
}
}
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:memory/ui/helpers/app_titles.dart';
class PageAbout extends StatelessWidget {
const PageAbout({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
const SizedBox(height: 8),
const AppTitle(text: 'about_title'),
const Text('about_content').tr(),
FutureBuilder<PackageInfo>(
future: PackageInfo.fromPlatform(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
return const Text('about_version').tr(
namedArgs: {
'version': snapshot.data!.version,
},
);
default:
return const SizedBox();
}
},
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:memory/cubit/game_cubit.dart';
import 'package:memory/models/game/game.dart';
import 'package:memory/ui/layouts/parameters_layout.dart';
import 'package:memory/ui/layouts/game_layout.dart';
class PageGame extends StatelessWidget {
const PageGame({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<GameCubit, GameState>(
builder: (BuildContext context, GameState gameState) {
final Game currentGame = gameState.currentGame;
return currentGame.isRunning
? const GameLayout()
: ParametersLayout(canResume: currentGame.canBeResumed);
},
);
}
}
import 'package:flutter/material.dart';
import 'package:memory/ui/helpers/app_titles.dart';
import 'package:memory/ui/settings/settings_form.dart';
class PageSettings extends StatelessWidget {
const PageSettings({super.key});
@override
Widget build(BuildContext context) {
return const Padding(
padding: EdgeInsets.symmetric(horizontal: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
SizedBox(height: 8),
AppTitle(text: 'settings_title'),
SizedBox(height: 8),
SettingsForm(),
],
),
);
}
}
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:unicons/unicons.dart';
import 'package:memory/ui/settings/theme_card.dart';
class SettingsForm extends StatefulWidget {
const SettingsForm({super.key});
@override
State<SettingsForm> createState() => _SettingsFormState();
}
class _SettingsFormState extends State<SettingsForm> {
@override
void dispose() {
super.dispose();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
// Light/dark theme
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Text('settings_label_theme').tr(),
const Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ThemeCard(
mode: ThemeMode.system,
icon: UniconsLine.cog,
),
ThemeCard(
mode: ThemeMode.light,
icon: UniconsLine.sun,
),
ThemeCard(
mode: ThemeMode.dark,
icon: UniconsLine.moon,
)
],
),
],
),
const SizedBox(height: 16),
],
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:memory/cubit/theme_cubit.dart';
class ThemeCard extends StatelessWidget {
const ThemeCard({
super.key,
required this.mode,
required this.icon,
});
final IconData icon;
final ThemeMode mode;
@override
Widget build(BuildContext context) {
return BlocBuilder<ThemeCubit, ThemeModeState>(
builder: (BuildContext context, ThemeModeState state) {
return Card(
elevation: 2,
shadowColor: Theme.of(context).colorScheme.shadow,
color: state.themeMode == mode
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.surface,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
margin: const EdgeInsets.all(5),
child: InkWell(
onTap: () => BlocProvider.of<ThemeCubit>(context).getTheme(
ThemeModeState(themeMode: mode),
),
borderRadius: const BorderRadius.all(Radius.circular(12)),
child: Icon(
icon,
size: 32,
color: state.themeMode != mode
? Theme.of(context).colorScheme.primary
: Colors.white,
),
),
);
},
);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment