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

Add minimal "board game"

parent 8de3f0ea
No related branches found
No related tags found
1 merge request!46Resolve "Add draw board game"
Pipeline #4870 passed
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
app.versionName=1.0.37 app.versionName=1.0.38
app.versionCode=38 app.versionCode=39
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
"bottom_nav_sample": "Sample", "bottom_nav_sample": "Sample",
"bottom_nav_api": "API", "bottom_nav_api": "API",
"bottom_nav_chart": "Graph", "bottom_nav_chart": "Graph",
"bottom_nav_game": "Game",
"bottom_nav_settings": "Settings", "bottom_nav_settings": "Settings",
"bottom_nav_about": "About", "bottom_nav_about": "About",
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
"bottom_nav_sample": "Démo", "bottom_nav_sample": "Démo",
"bottom_nav_api": "API", "bottom_nav_api": "API",
"bottom_nav_chart": "Graph", "bottom_nav_chart": "Graph",
"bottom_nav_game": "Jeu",
"bottom_nav_settings": "Paramètres", "bottom_nav_settings": "Paramètres",
"bottom_nav_about": "À propos", "bottom_nav_about": "À propos",
......
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:random/models/game_data.dart';
part 'game_state.dart';
class GameCubit extends HydratedCubit<GameState> {
GameCubit() : super(const GameState());
void getData(GameState state) {
emit(state);
}
void updateGameState(GameData game) {
emit(GameState(game: game));
}
@override
GameState? fromJson(Map<String, dynamic> json) {
GameData game = json['game'] as GameData;
return GameState(
game: game,
);
}
@override
Map<String, dynamic>? toJson(GameState state) {
return <String, dynamic>{
'game': state.game?.toJson(),
};
}
}
part of 'game_cubit.dart';
@immutable
class GameState extends Equatable {
const GameState({
this.game,
});
final GameData? game;
@override
List<Object?> get props => <Object?>[
game,
];
}
...@@ -10,6 +10,7 @@ import 'package:path_provider/path_provider.dart'; ...@@ -10,6 +10,7 @@ import 'package:path_provider/path_provider.dart';
import 'package:random/config/theme.dart'; import 'package:random/config/theme.dart';
import 'package:random/cubit/bottom_nav_cubit.dart'; import 'package:random/cubit/bottom_nav_cubit.dart';
import 'package:random/cubit/data_cubit.dart'; import 'package:random/cubit/data_cubit.dart';
import 'package:random/cubit/game_cubit.dart';
import 'package:random/cubit/settings_cubit.dart'; import 'package:random/cubit/settings_cubit.dart';
import 'package:random/cubit/api_cubit.dart'; import 'package:random/cubit/api_cubit.dart';
import 'package:random/repository/api.dart'; import 'package:random/repository/api.dart';
...@@ -50,6 +51,7 @@ class MyApp extends StatelessWidget { ...@@ -50,6 +51,7 @@ class MyApp extends StatelessWidget {
BlocProvider<SettingsCubit>(create: (context) => SettingsCubit()), BlocProvider<SettingsCubit>(create: (context) => SettingsCubit()),
BlocProvider<BottomNavCubit>(create: (context) => BottomNavCubit()), BlocProvider<BottomNavCubit>(create: (context) => BottomNavCubit()),
BlocProvider<DataCubit>(create: (context) => DataCubit()), BlocProvider<DataCubit>(create: (context) => DataCubit()),
BlocProvider<GameCubit>(create: (context) => GameCubit()),
BlocProvider<ApiDataCubit>( BlocProvider<ApiDataCubit>(
create: (context) => ApiDataCubit( create: (context) => ApiDataCubit(
apiRepository: ApiRepository( apiRepository: ApiRepository(
......
import 'dart:convert';
import 'dart:math';
class GameDataItem {
final int value;
const GameDataItem({
required this.value,
});
factory GameDataItem.fromJson(Map<String, dynamic>? json) {
return GameDataItem(
value: (json?['value'] != null) ? (json?['value'] as int) : 0,
);
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
'value': this.value,
};
}
String toString() {
return jsonEncode(this.toJson());
}
}
class GameData {
final int boardSize;
final List<List<GameDataItem>> board;
const GameData({
required this.boardSize,
required this.board,
});
factory GameData.createNew(int boardSize) {
final List<List<GameDataItem>> cells = [];
for (var y = 0; y < boardSize; y++) {
final List<GameDataItem> line = [];
for (var x = 0; x < boardSize; x++) {
final GameDataItem item = new GameDataItem(value: 0);
line.add(item);
}
cells.add(line);
}
return GameData(
boardSize: boardSize,
board: cells,
);
}
factory GameData.createRandom(int boardSize) {
const allowedValues = [0, 1, 2, 3, 4, 5];
final allowedValuesSize = allowedValues.length;
final List<List<GameDataItem>> cells = [];
for (var y = 0; y < boardSize; y++) {
final List<GameDataItem> line = [];
for (var x = 0; x < boardSize; x++) {
final value = allowedValues[Random().nextInt(allowedValuesSize)];
final GameDataItem item = new GameDataItem(value: value);
line.add(item);
}
cells.add(line);
}
return GameData(
boardSize: boardSize,
board: cells,
);
}
factory GameData.fromJson(Map<String, dynamic>? json) {
return GameData(
boardSize: (json?['boardSize'] != null) ? (json?['boardSize'] as int) : 0,
board: (json?['board'] != null) ? (json?['board'] as List<List<GameDataItem>>) : [],
);
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
'boardSize': this.boardSize,
'board': this.board,
};
}
String toString() {
return jsonEncode(this.toJson());
}
}
import 'package:flutter/material.dart';
import 'package:random/config/app_colors.dart';
class CellPainter extends CustomPainter {
const CellPainter({required this.value});
final int value;
Color getIndexedColor(int index) {
const List<Color> availableColors = [
AppColors.contentColorCyan,
AppColors.contentColorGreen,
AppColors.contentColorOrange,
AppColors.contentColorPurple,
AppColors.contentColorYellow,
AppColors.contentColorPink,
AppColors.contentColorWhite,
AppColors.mainTextColor3,
];
return availableColors[index % availableColors.length];
}
@override
void paint(Canvas canvas, Size size) {
final paintBackground = Paint();
paintBackground.color = getIndexedColor(value);
paintBackground.style = PaintingStyle.fill;
final Rect rectBackground = Rect.fromPoints(Offset(0, 0), Offset(size.width, size.height));
canvas.drawRect(rectBackground, paintBackground);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:random/cubit/game_cubit.dart';
import 'package:random/models/game_data.dart';
import 'package:random/ui/widgets/game_board.dart';
import 'package:unicons/unicons.dart';
class GamePage extends StatefulWidget {
const GamePage({super.key});
@override
State<GamePage> createState() => _GamePageState();
}
class _GamePageState extends State<GamePage> {
@override
Widget build(BuildContext context) {
return BlocBuilder<GameCubit, GameState>(
builder: (context, gameState) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
gameState.game != null
? Container(
margin: EdgeInsets.all(4),
padding: EdgeInsets.all(4),
child: GameBoardWidget(
gameData: gameState.game!,
),
)
: SizedBox.shrink(),
IconButton(
onPressed: () {
const boardSize = 6;
final GameData newGame = GameData.createRandom(boardSize);
BlocProvider.of<GameCubit>(context).updateGameState(newGame);
print(gameState);
},
icon: Icon(UniconsSolid.star),
color: Colors.white,
),
],
);
},
);
}
}
...@@ -6,6 +6,7 @@ import 'package:random/cubit/bottom_nav_cubit.dart'; ...@@ -6,6 +6,7 @@ import 'package:random/cubit/bottom_nav_cubit.dart';
import 'package:random/ui/screens/about_page.dart'; import 'package:random/ui/screens/about_page.dart';
import 'package:random/ui/screens/api_page.dart'; import 'package:random/ui/screens/api_page.dart';
import 'package:random/ui/screens/demo_page.dart'; import 'package:random/ui/screens/demo_page.dart';
import 'package:random/ui/screens/game_page.dart';
import 'package:random/ui/screens/graph_page.dart'; import 'package:random/ui/screens/graph_page.dart';
import 'package:random/ui/screens/settings_page.dart'; import 'package:random/ui/screens/settings_page.dart';
import 'package:random/ui/widgets/app_bar.dart'; import 'package:random/ui/widgets/app_bar.dart';
...@@ -25,6 +26,7 @@ class _SkeletonScreenState extends State<SkeletonScreen> { ...@@ -25,6 +26,7 @@ class _SkeletonScreenState extends State<SkeletonScreen> {
DemoPage(), DemoPage(),
ApiPage(), ApiPage(),
GraphPage(), GraphPage(),
GamePage(),
SettingsPage(), SettingsPage(),
AboutPage(), AboutPage(),
]; ];
......
...@@ -54,6 +54,10 @@ class BottomNavBar extends StatelessWidget { ...@@ -54,6 +54,10 @@ class BottomNavBar extends StatelessWidget {
icon: const Icon(UniconsLine.pen), icon: const Icon(UniconsLine.pen),
label: tr('bottom_nav_chart'), label: tr('bottom_nav_chart'),
), ),
BottomNavigationBarItem(
icon: const Icon(UniconsLine.star),
label: tr('bottom_nav_game'),
),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: const Icon(UniconsLine.setting), icon: const Icon(UniconsLine.setting),
label: tr('bottom_nav_settings'), label: tr('bottom_nav_settings'),
......
import 'package:flutter/material.dart';
import 'package:random/models/game_data.dart';
import 'package:random/ui/painters/cell_painter.dart';
class GameBoardWidget extends StatelessWidget {
const GameBoardWidget({super.key, required this.gameData});
final GameData gameData;
@override
Widget build(BuildContext context) {
const staticBoardWidth = 300;
const staticBoardHeight = 300;
final rowsCount = this.gameData.board.length;
final columnsCount = this.gameData.board[0].length;
print('counts: rows=' + rowsCount.toString() + ' / columns=' + columnsCount.toString());
final cellWidth = staticBoardWidth / columnsCount;
final cellHeight = staticBoardHeight / rowsCount;
print('cell: width=' + cellWidth.toString() + ' / height=' + cellHeight.toString());
final List<Widget> cells = [];
for (var y = 0; y < rowsCount; y++) {
for (var x = 0; x < columnsCount; x++) {
final GameDataItem item = this.gameData.board[y][x];
final Widget cellContent = CustomPaint(
size: Size(cellWidth, cellHeight),
willChange: false,
painter: CellPainter(value: item.value),
);
final Widget widget = Positioned(
left: (x * cellWidth).toDouble(),
top: (y * cellHeight).toDouble(),
child: Container(
width: cellWidth,
height: cellHeight,
color: Colors.deepPurpleAccent,
child: cellContent,
),
);
cells.add(widget);
}
}
return Container(
width: staticBoardWidth.toDouble(),
height: staticBoardHeight.toDouble(),
color: Colors.grey,
child: Stack(
children: cells,
),
);
}
}
...@@ -3,7 +3,7 @@ description: A random application, for testing purpose only. ...@@ -3,7 +3,7 @@ description: A random application, for testing purpose only.
publish_to: 'none' publish_to: 'none'
version: 1.0.37+38 version: 1.0.38+39
environment: environment:
sdk: '^3.0.0' sdk: '^3.0.0'
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment