Skip to content

Commit

Permalink
第5章の初期版実装
Browse files Browse the repository at this point in the history
  • Loading branch information
okaryo committed Sep 23, 2023
1 parent 4b05fd0 commit ca9fa93
Show file tree
Hide file tree
Showing 19 changed files with 362 additions and 79 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release

# Firebase
**/google-services.json
**/GoogleService-Info.plist
4 changes: 4 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ if (flutterVersionName == null) {
}

apply plugin: 'com.android.application'
// START: FlutterFire Configuration
apply plugin: 'com.google.gms.google-services'
// END: FlutterFire Configuration
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

Expand Down Expand Up @@ -51,6 +54,7 @@ android {
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
}

buildTypes {
Expand Down
3 changes: 3 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ buildscript {

dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
// START: FlutterFire Configuration
classpath 'com.google.gms:google-services:4.3.10'
// END: FlutterFire Configuration
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand Down
4 changes: 4 additions & 0 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
752703CAEFDAEFDAB76B9856 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3867D3A3D7E5FDA190F32018 /* GoogleService-Info.plist */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
Expand All @@ -31,6 +32,7 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3867D3A3D7E5FDA190F32018 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -72,6 +74,7 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
3867D3A3D7E5FDA190F32018 /* GoogleService-Info.plist */,
);
sourceTree = "<group>";
};
Expand Down Expand Up @@ -163,6 +166,7 @@
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
752703CAEFDAEFDAB76B9856 /* GoogleService-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
7 changes: 7 additions & 0 deletions ios/firebase_app_id_file.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"file_generated_by": "FlutterFire CLI",
"purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory",
"GOOGLE_APP_ID": "1:807943088200:ios:125d7bfaa751582d21907d",
"FIREBASE_PROJECT_ID": "tic-tac-toe-handson",
"GCM_SENDER_ID": "807943088200"
}
80 changes: 80 additions & 0 deletions lib/firebase_options.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// File generated by FlutterFire CLI.
// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, kIsWeb, TargetPlatform;

/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
return web;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
return macos;
case TargetPlatform.windows:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for windows - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}

static const FirebaseOptions web = FirebaseOptions(
apiKey: 'AIzaSyAoY0U1veRmqWWMmFBULkerX6X5HYjMmIs',
appId: '1:807943088200:web:eda1735cfd79d15e21907d',
messagingSenderId: '807943088200',
projectId: 'tic-tac-toe-handson',
authDomain: 'tic-tac-toe-handson.firebaseapp.com',
storageBucket: 'tic-tac-toe-handson.appspot.com',
);

static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyCCWnRieFsedimIvgsjNevAdUSb0dm1faY',
appId: '1:807943088200:android:d989c8a1b020562321907d',
messagingSenderId: '807943088200',
projectId: 'tic-tac-toe-handson',
storageBucket: 'tic-tac-toe-handson.appspot.com',
);

static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyBZHELrAr4SRfXLYrxc4zF27P0R3sGsbi0',
appId: '1:807943088200:ios:125d7bfaa751582d21907d',
messagingSenderId: '807943088200',
projectId: 'tic-tac-toe-handson',
storageBucket: 'tic-tac-toe-handson.appspot.com',
iosBundleId: 'com.example.ticTacToeHandson',
);

static const FirebaseOptions macos = FirebaseOptions(
apiKey: 'AIzaSyBZHELrAr4SRfXLYrxc4zF27P0R3sGsbi0',
appId: '1:807943088200:ios:125d7bfaa751582d21907d',
messagingSenderId: '807943088200',
projectId: 'tic-tac-toe-handson',
storageBucket: 'tic-tac-toe-handson.appspot.com',
iosBundleId: 'com.example.ticTacToeHandson',
);
}
9 changes: 8 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:tic_tac_toe_handson/firebase_options.dart';
import 'package:tic_tac_toe_handson/view/board.dart';

void main() {
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);

runApp(
const ProviderScope(
child: MyApp(),
Expand Down
9 changes: 9 additions & 0 deletions lib/model/players.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class Players {
final String playerX;
final String playerO;

Players({
required this.playerX,
required this.playerO,
});
}
64 changes: 54 additions & 10 deletions lib/model/tic_tac_toe.dart
Original file line number Diff line number Diff line change
@@ -1,24 +1,68 @@
import 'package:tic_tac_toe_handson/model/players.dart';

class TicTacToe {
final List<List<String>> board;
final Players players;
final String currentPlayer;

factory TicTacToe.start() {
return TicTacToe._([
['', '', ''],
['', '', ''],
['', '', ''],
], 'X');
factory TicTacToe.start({
playerX = 'X',
playerO = 'O',
}) {
final players = Players(
playerX: playerX,
playerO: playerO,
);

return TicTacToe(
[
['', '', ''],
['', '', ''],
['', '', ''],
],
players,
players.playerX,
);
}

TicTacToe._(this.board, this.currentPlayer);
TicTacToe(this.board, this.players, this.currentPlayer);

factory TicTacToe.fromJson(Map<String, dynamic> json) {
final flatBoard = List<String>.from(json['board']);

return TicTacToe(
[
List<String>.from(flatBoard.sublist(0, 3)),
List<String>.from(flatBoard.sublist(3, 6)),
List<String>.from(flatBoard.sublist(6, 9)),
],
Players(
playerX: json['players']['playerX'],
playerO: json['players']['playerO'],
),
json['currentPlayer'],
);
}

Map<String, dynamic> toJson() {
return {
// Firestoreではネストした配列を扱えないため、1次元配列に変換する
'board': [...board[0], ...board[1], ...board[2]],
'players': {
'playerX': players.playerX,
'playerO': players.playerO,
},
'currentPlayer': currentPlayer,
};
}

TicTacToe placeMark(int row, int col) {
if (board[row][col].isEmpty) {
final newBoard = List.of(board);
newBoard[row][col] = currentPlayer;
String nextPlayer = currentPlayer == 'X' ? 'O' : 'X';
String nextPlayer = currentPlayer == players.playerX ? players.playerO : players.playerX;

return TicTacToe._(newBoard, nextPlayer);
return TicTacToe(newBoard, players, nextPlayer);
}
return this;
}
Expand Down Expand Up @@ -50,6 +94,6 @@ class TicTacToe {
}

TicTacToe resetBoard() {
return TicTacToe.start();
return TicTacToe.start(playerX: players.playerX, playerO: players.playerO);
}
}
18 changes: 4 additions & 14 deletions lib/provider/tic_tac_toe_provider.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:tic_tac_toe_handson/model/tic_tac_toe.dart';
import 'package:tic_tac_toe_handson/repository/tic_tac_toe_repository.dart';

final ticTacToeProvider = StateNotifierProvider.autoDispose<TicTacToeProvider, TicTacToe>((ref) {
return TicTacToeProvider();
final ticTacToeProvider = StreamProvider.autoDispose<TicTacToe>((ref) {
// 対戦相手同士のIDを設定する
return getTicTacToe(playerX: 'flutter', playerO: 'kaigi');
});

class TicTacToeProvider extends StateNotifier<TicTacToe> {
TicTacToeProvider() : super(TicTacToe.start());

placeMark(int row, int col) {
state = state.placeMark(row, col);
}

resetBoard() {
state = state.resetBoard();
}
}
26 changes: 26 additions & 0 deletions lib/repository/tic_tac_toe_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:tic_tac_toe_handson/model/tic_tac_toe.dart';

final client = FirebaseFirestore.instance;
const rootCollectionKey = 'tic_tac_toe';

Stream<TicTacToe> getTicTacToe({String playerX = 'X', String playerO = 'O'}) {
final documentKey = _documentKey(playerX, playerO);
return client.collection(rootCollectionKey).doc(documentKey).snapshots().map((snapshot) {
final data = snapshot.data();
if (data == null) {
return TicTacToe.start(playerX: playerX, playerO: playerO);
}

return TicTacToe.fromJson(data);
});
}

Future<void> updateTicTacToe(TicTacToe ticTacToe) async {
final documentKey = _documentKey(ticTacToe.players.playerX, ticTacToe.players.playerO);
await client.collection(rootCollectionKey).doc(documentKey).set(ticTacToe.toJson());
}

String _documentKey(String playerX, String playerO) {
return '${playerX}_${playerO}';
}
Loading

0 comments on commit ca9fa93

Please sign in to comment.