command_line_arguments;
33 |
34 | // Skip the first argument as it's the binary name.
35 | for (int i = 1; i < argc; i++) {
36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
37 | }
38 |
39 | ::LocalFree(argv);
40 |
41 | return command_line_arguments;
42 | }
43 |
44 | std::string Utf8FromUtf16(const wchar_t *utf16_string) {
45 | if (utf16_string == nullptr) {
46 | return std::string();
47 | }
48 | int target_length = ::WideCharToMultiByte(
49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
50 | -1, nullptr, 0, nullptr, nullptr)
51 | - 1; // remove the trailing null character
52 | int input_length = (int) wcslen(utf16_string);
53 | std::string utf8_string;
54 | if (target_length <= 0 || target_length > utf8_string.max_size()) {
55 | return utf8_string;
56 | }
57 | utf8_string.resize(target_length);
58 | int converted_length = ::WideCharToMultiByte(
59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
60 | input_length, utf8_string.data(), target_length, nullptr, nullptr);
61 | if (converted_length == 0) {
62 | return std::string();
63 | }
64 | return utf8_string;
65 | }
66 |
--------------------------------------------------------------------------------
/lib/widgets/scoreboard.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_tic_tac_toe/theme/app_sizes.dart';
3 | import 'package:google_fonts/google_fonts.dart';
4 |
5 | import '../theme/colors.dart';
6 |
7 | class ScoreBoard extends StatelessWidget {
8 | const ScoreBoard({
9 | super.key,
10 | required this.playerXScore,
11 | required this.playerOScore,
12 | required this.isTurn,
13 | required this.playerXName,
14 | required this.playerOName,
15 | });
16 |
17 | final int playerXScore;
18 | final int playerOScore;
19 | final bool isTurn;
20 | final String playerXName;
21 | final String playerOName;
22 |
23 | Widget _buildPlayerScore(String playerLabel, int score, bool isTurn) =>
24 | Container(
25 | padding: const EdgeInsets.all(16),
26 | width: 150,
27 | height: 100,
28 | decoration: BoxDecoration(
29 | color: GameColors.kForeground,
30 | borderRadius: borderRadiusM(),
31 | border: isTurn
32 | ? playerLabel == playerXName
33 | ? Border.all(
34 | color: GameColors.kBlue,
35 | width: 2.0,
36 | )
37 | : Border.all(
38 | color: GameColors.kPurple,
39 | width: 2.0,
40 | )
41 | : null,
42 | ),
43 | child: Column(
44 | mainAxisAlignment: MainAxisAlignment.spaceAround,
45 | children: [
46 | Text(
47 | playerLabel,
48 | style: TextStyle(
49 | fontSize: 16,
50 | fontWeight: FontWeight.bold,
51 | color: playerLabel == playerXName ? Colors.blue : Colors.purple,
52 | ),
53 | ),
54 | Text(
55 | score.toString(),
56 | style: TextStyle(
57 | fontSize: 20,
58 | fontFamily: GoogleFonts.caveat().fontFamily,
59 | fontWeight: FontWeight.bold,
60 | color: GameColors.kWhitish,
61 | ),
62 | ),
63 | ],
64 | ),
65 | );
66 |
67 | @override
68 | Widget build(BuildContext context) => Row(
69 | mainAxisAlignment: MainAxisAlignment.spaceAround,
70 | children: [
71 | _buildPlayerScore(playerXName, playerXScore, isTurn),
72 | _buildPlayerScore(playerOName, playerOScore, !isTurn),
73 | ],
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/lib/screens/main_menu.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_tic_tac_toe/screens/players_names_screen.dart';
3 | import 'package:flutter_tic_tac_toe/theme/app_sizes.dart';
4 | import 'package:flutter_tic_tac_toe/theme/colors.dart';
5 | import 'package:flutter_tic_tac_toe/widgets/wrapper_container.dart';
6 |
7 | import 'game_base_screen.dart';
8 |
9 | class MainMenu extends StatelessWidget {
10 | const MainMenu({super.key});
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return WrapperContainer(
15 | child: Center(
16 | child: Column(
17 | mainAxisAlignment: MainAxisAlignment.center,
18 | children: [
19 | const Text(
20 | 'Tic Tac Toe',
21 | style: TextStyle(
22 | fontSize: 50,
23 | color: Colors.white,
24 | ),
25 | ),
26 | gap4XL(),
27 | MainMenuButtons(
28 | btnText: 'Single Player',
29 | onPressed: () {
30 | Navigator.push(
31 | context,
32 | MaterialPageRoute(
33 | builder: (context) => const GameBaseScreen(
34 | playerOName: "AI",
35 | playerXName: "You",
36 | isAgainstAI: true,
37 | ),
38 | ),
39 | );
40 | },
41 | ),
42 | gapXL(),
43 | MainMenuButtons(
44 | btnText: 'Multiplayer',
45 | onPressed: () {
46 | Navigator.push(
47 | context,
48 | MaterialPageRoute(
49 | builder: (context) => const PlayerNames()));
50 | },
51 | ),
52 | ],
53 | ),
54 | ),
55 | );
56 | }
57 | }
58 |
59 | class MainMenuButtons extends StatelessWidget {
60 | const MainMenuButtons(
61 | {super.key, required this.btnText, required this.onPressed});
62 |
63 | final String btnText;
64 | final void Function() onPressed;
65 |
66 | @override
67 | Widget build(BuildContext context) => SizedBox(
68 | width: MediaQuery.of(context).size.width * 0.8,
69 | child: ElevatedButton(
70 | onPressed: onPressed,
71 | style: ElevatedButton.styleFrom(
72 | backgroundColor: GameColors.kForeground,
73 | padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
74 | shape: RoundedRectangleBorder(
75 | borderRadius: BorderRadius.circular(10),
76 | ),
77 | ),
78 | child: Text(
79 | btnText,
80 | style: const TextStyle(
81 | color: GameColors.kWhitish,
82 | fontSize: 20,
83 | fontWeight: FontWeight.bold,
84 | ),
85 | ),
86 | ),
87 | );
88 | }
89 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "size": "20x20",
5 | "idiom": "iphone",
6 | "filename": "Icon-App-20x20@2x.png",
7 | "scale": "2x"
8 | },
9 | {
10 | "size": "20x20",
11 | "idiom": "iphone",
12 | "filename": "Icon-App-20x20@3x.png",
13 | "scale": "3x"
14 | },
15 | {
16 | "size": "29x29",
17 | "idiom": "iphone",
18 | "filename": "Icon-App-29x29@1x.png",
19 | "scale": "1x"
20 | },
21 | {
22 | "size": "29x29",
23 | "idiom": "iphone",
24 | "filename": "Icon-App-29x29@2x.png",
25 | "scale": "2x"
26 | },
27 | {
28 | "size": "29x29",
29 | "idiom": "iphone",
30 | "filename": "Icon-App-29x29@3x.png",
31 | "scale": "3x"
32 | },
33 | {
34 | "size": "40x40",
35 | "idiom": "iphone",
36 | "filename": "Icon-App-40x40@2x.png",
37 | "scale": "2x"
38 | },
39 | {
40 | "size": "40x40",
41 | "idiom": "iphone",
42 | "filename": "Icon-App-40x40@3x.png",
43 | "scale": "3x"
44 | },
45 | {
46 | "size": "60x60",
47 | "idiom": "iphone",
48 | "filename": "Icon-App-60x60@2x.png",
49 | "scale": "2x"
50 | },
51 | {
52 | "size": "60x60",
53 | "idiom": "iphone",
54 | "filename": "Icon-App-60x60@3x.png",
55 | "scale": "3x"
56 | },
57 | {
58 | "size": "20x20",
59 | "idiom": "ipad",
60 | "filename": "Icon-App-20x20@1x.png",
61 | "scale": "1x"
62 | },
63 | {
64 | "size": "20x20",
65 | "idiom": "ipad",
66 | "filename": "Icon-App-20x20@2x.png",
67 | "scale": "2x"
68 | },
69 | {
70 | "size": "29x29",
71 | "idiom": "ipad",
72 | "filename": "Icon-App-29x29@1x.png",
73 | "scale": "1x"
74 | },
75 | {
76 | "size": "29x29",
77 | "idiom": "ipad",
78 | "filename": "Icon-App-29x29@2x.png",
79 | "scale": "2x"
80 | },
81 | {
82 | "size": "40x40",
83 | "idiom": "ipad",
84 | "filename": "Icon-App-40x40@1x.png",
85 | "scale": "1x"
86 | },
87 | {
88 | "size": "40x40",
89 | "idiom": "ipad",
90 | "filename": "Icon-App-40x40@2x.png",
91 | "scale": "2x"
92 | },
93 | {
94 | "size": "76x76",
95 | "idiom": "ipad",
96 | "filename": "Icon-App-76x76@1x.png",
97 | "scale": "1x"
98 | },
99 | {
100 | "size": "76x76",
101 | "idiom": "ipad",
102 | "filename": "Icon-App-76x76@2x.png",
103 | "scale": "2x"
104 | },
105 | {
106 | "size": "83.5x83.5",
107 | "idiom": "ipad",
108 | "filename": "Icon-App-83.5x83.5@2x.png",
109 | "scale": "2x"
110 | },
111 | {
112 | "size": "1024x1024",
113 | "idiom": "ios-marketing",
114 | "filename": "Icon-App-1024x1024@1x.png",
115 | "scale": "1x"
116 | }
117 | ],
118 | "info": {
119 | "version": 1,
120 | "author": "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/linux/flutter/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # This file controls Flutter-level build steps. It should not be edited.
2 | cmake_minimum_required(VERSION 3.10)
3 |
4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
5 |
6 | # Configuration provided via flutter tool.
7 | include(${EPHEMERAL_DIR}/generated_config.cmake)
8 |
9 | # TODO: Move the rest of this into files in ephemeral. See
10 | # https://github.com/flutter/flutter/issues/57146.
11 |
12 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...),
13 | # which isn't available in 3.10.
14 | function(list_prepend LIST_NAME PREFIX)
15 | set(NEW_LIST "")
16 | foreach (element ${${LIST_NAME}})
17 | list(APPEND NEW_LIST "${PREFIX}${element}")
18 | endforeach (element)
19 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
20 | endfunction()
21 |
22 | # === Flutter Library ===
23 | # System-level dependencies.
24 | find_package(PkgConfig REQUIRED)
25 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
26 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
27 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
28 |
29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
30 |
31 | # Published to parent scope for install step.
32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
36 |
37 | list(APPEND FLUTTER_LIBRARY_HEADERS
38 | "fl_basic_message_channel.h"
39 | "fl_binary_codec.h"
40 | "fl_binary_messenger.h"
41 | "fl_dart_project.h"
42 | "fl_engine.h"
43 | "fl_json_message_codec.h"
44 | "fl_json_method_codec.h"
45 | "fl_message_codec.h"
46 | "fl_method_call.h"
47 | "fl_method_channel.h"
48 | "fl_method_codec.h"
49 | "fl_method_response.h"
50 | "fl_plugin_registrar.h"
51 | "fl_plugin_registry.h"
52 | "fl_standard_message_codec.h"
53 | "fl_standard_method_codec.h"
54 | "fl_string_codec.h"
55 | "fl_value.h"
56 | "fl_view.h"
57 | "flutter_linux.h"
58 | )
59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
60 | add_library(flutter INTERFACE)
61 | target_include_directories(flutter INTERFACE
62 | "${EPHEMERAL_DIR}"
63 | )
64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
65 | target_link_libraries(flutter INTERFACE
66 | PkgConfig::GTK
67 | PkgConfig::GLIB
68 | PkgConfig::GIO
69 | )
70 | add_dependencies(flutter flutter_assemble)
71 |
72 | # === Flutter tool backend ===
73 | # _phony_ is a non-existent file to force this command to run every time,
74 | # since currently there's no way to get a full input/output list from the
75 | # flutter tool.
76 | add_custom_command(
77 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
78 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_
79 | COMMAND ${CMAKE_COMMAND} -E env
80 | ${FLUTTER_TOOL_ENVIRONMENT}
81 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
82 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
83 | VERBATIM
84 | )
85 | add_custom_target(flutter_assemble DEPENDS
86 | "${FLUTTER_LIBRARY}"
87 | ${FLUTTER_LIBRARY_HEADERS}
88 | )
89 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Tic Tac Toe
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ## Introduction
10 |
11 | The classic Tic Tac Toe game built using Flutter. To enhance the user experience, I implemented Hive
12 | as the local database, allowing players to view their games later. Additionally, I utilized hooks, a
13 | flutter implementation of React hooks, and Riverpod to efficiently manage the life-cycle of the
14 | Widget and state managment.
15 | ## Features
16 |
17 | * **Single Player** : Challenge yourself against an AI powered by the ****MiniMax**** algorithm, known for its strategic decision-making in game theory. Experience an engaging gameplay where the AI selects optimal moves, making every match a test of your skills.
18 |
19 | * **Multiplayer** : Compete against friends, customize player names, and choose "X" or "O" to determine
20 | the Tic Tac Toe champion!
21 | * **History** : Easily access and review your past matches, stored locally using Hive.
22 |
23 | ## State Management
24 |
25 | This project utilizes Hooks and Riverpod for efficient state management:
26 |
27 |
28 | * [**Hooks**](https://pub.dev/packages/flutter_hooks)
29 |
30 | Inspired by their React counterparts, Hooks offer a functional and reusable approach to constructing components, improving upon the usage of StatefulWidget. For instance, when using text field controllers, you have to manually dispose of them to prevent memory leaks, whereas hooks handle this automatically (useTextEditingController).
31 | * [**Riverpod**](https://pub.dev/packages/riverpod)
32 |
33 | Riverpod, a state management library, distinguishes itself through its emphasis on simplicity,
34 | performance, and scalability. This library seamlessly integrates into Flutter's ecosystem and
35 | furnishes an accessible interface. Employing Riverpod, I've adeptly overseen the application's
36 | state, simplifying the management of intricate interplays among distinct components.
37 |
38 | Combining Hooks and Riverpod has enabled me to create a cleaner and more efficient architecture for
39 | the Tic Tac Toe game, resulting in improved performance and maintainability.
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | ## Build Process
58 |
59 | - Follow the [Flutter Guide](https://docs.flutter.dev/) for getting started building a flutter
60 | project.
61 | - Clone or download the repo
62 |
63 | ```{r klippy, echo=FALSE, include=TRUE}
64 | git clone https://github.com/SouhailKrs/Flutter-Tic-Tac-Toe
65 | ```
66 |
67 | - Get dependencies
68 |
69 | ```{r klippy, echo=FALSE, include=TRUE}
70 | flutter pub get
71 | ```
72 |
--------------------------------------------------------------------------------
/windows/runner/Runner.rc:
--------------------------------------------------------------------------------
1 | // Microsoft Visual C++ generated resource script.
2 | //
3 | #pragma code_page(65001)
4 |
5 | #include "resource.h"
6 |
7 | #define APSTUDIO_READONLY_SYMBOLS
8 | /////////////////////////////////////////////////////////////////////////////
9 | //
10 | // Generated from the TEXTINCLUDE 2 resource.
11 | //
12 | #include "winres.h"
13 |
14 | /////////////////////////////////////////////////////////////////////////////
15 | #undef APSTUDIO_READONLY_SYMBOLS
16 |
17 | /////////////////////////////////////////////////////////////////////////////
18 | // English (United States) resources
19 |
20 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
21 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
22 |
23 | #ifdef APSTUDIO_INVOKED
24 | /////////////////////////////////////////////////////////////////////////////
25 | //
26 | // TEXTINCLUDE
27 | //
28 |
29 | 1 TEXTINCLUDE
30 | BEGIN
31 | "resource.h\0"
32 | END
33 |
34 | 2 TEXTINCLUDE
35 | BEGIN
36 | "#include ""winres.h""\r\n"
37 | "\0"
38 | END
39 |
40 | 3 TEXTINCLUDE
41 | BEGIN
42 | "\r\n"
43 | "\0"
44 | END
45 |
46 | #endif // APSTUDIO_INVOKED
47 |
48 |
49 | /////////////////////////////////////////////////////////////////////////////
50 | //
51 | // Icon
52 | //
53 |
54 | // Icon with lowest ID value placed first to ensure application icon
55 | // remains consistent on all systems.
56 | IDI_APP_ICON ICON "resources\\app_icon.ico"
57 |
58 |
59 | /////////////////////////////////////////////////////////////////////////////
60 | //
61 | // Version
62 | //
63 |
64 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
65 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
66 | #else
67 | #define VERSION_AS_NUMBER 1,0,0,0
68 | #endif
69 |
70 | #if defined(FLUTTER_VERSION)
71 | #define VERSION_AS_STRING FLUTTER_VERSION
72 | #else
73 | #define VERSION_AS_STRING "1.0.0"
74 | #endif
75 |
76 | VS_VERSION_INFO VERSIONINFO
77 | FILEVERSION VERSION_AS_NUMBER
78 | PRODUCTVERSION VERSION_AS_NUMBER
79 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
80 | #ifdef _DEBUG
81 | FILEFLAGS VS_FF_DEBUG
82 | #else
83 | FILEFLAGS 0x0L
84 | #endif
85 | FILEOS VOS__WINDOWS32
86 | FILETYPE VFT_APP
87 | FILESUBTYPE 0x0L
88 | BEGIN
89 | BLOCK
90 | "StringFileInfo"
91 | BEGIN
92 | BLOCK
93 | "040904e4"
94 | BEGIN
95 | VALUE
96 | "CompanyName", "com.tictactoe" "\0"
97 | VALUE "FileDescription", "flutter_tic_tac_toe" "\0"
98 | VALUE "FileVersion", VERSION_AS_STRING "\0"
99 | VALUE "InternalName", "flutter_tic_tac_toe" "\0"
100 | VALUE "LegalCopyright", "Copyright (C) 2023 com.tictactoe. All rights reserved." "\0"
101 | VALUE "OriginalFilename", "flutter_tic_tac_toe.exe" "\0"
102 | VALUE "ProductName", "flutter_tic_tac_toe" "\0"
103 | VALUE "ProductVersion", VERSION_AS_STRING "\0"
104 | END
105 | END
106 | BLOCK "VarFileInfo"
107 | BEGIN
108 | VALUE
109 | "Translation", 0x409, 1252
110 | END
111 | END
112 |
113 | #endif // English (United States) resources
114 | /////////////////////////////////////////////////////////////////////////////
115 |
116 |
117 |
118 | #ifndef APSTUDIO_INVOKED
119 | /////////////////////////////////////////////////////////////////////////////
120 | //
121 | // Generated from the TEXTINCLUDE 3 resource.
122 | //
123 |
124 |
125 | /////////////////////////////////////////////////////////////////////////////
126 | #endif // not APSTUDIO_INVOKED
127 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
43 |
49 |
50 |
51 |
52 |
53 |
63 |
65 |
71 |
72 |
73 |
74 |
80 |
82 |
88 |
89 |
90 |
91 |
93 |
94 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_tic_tac_toe
2 | description: Flutter Tic Tac Toe.
3 | # The following line prevents the package from being accidentally published to
4 | # pub.dev using `flutter pub publish`. This is preferred for private packages.
5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
6 |
7 | # The following defines the version and build number for your application.
8 | # A version number is three numbers separated by dots, like 1.2.43
9 | # followed by an optional build number separated by a +.
10 | # Both the version and the builder number may be overridden in flutter
11 | # build by specifying --build-name and --build-number, respectively.
12 | # In Android, build-name is used as versionName while build-number used as versionCode.
13 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
14 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
15 | # Read more about iOS versioning at
16 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
17 | # In Windows, build-name is used as the major, minor, and patch parts
18 | # of the product and file versions while build-number is used as the build suffix.
19 | version: 1.0.0+1
20 |
21 | environment:
22 | sdk: '>=3.0.5 <4.0.0'
23 |
24 | # Dependencies specify other packages that your package needs in order to work.
25 | # To automatically upgrade your package dependencies to the latest versions
26 | # consider running `flutter pub upgrade --major-versions`. Alternatively,
27 | # dependencies can be manually updated by changing the version numbers below to
28 | # the latest version available on pub.dev. To see which dependencies have newer
29 | # versions available, run `flutter pub outdated`.
30 | dependencies:
31 | flutter:
32 | sdk: flutter
33 |
34 |
35 | # The following adds the Cupertino Icons font to your application.
36 | # Use with the CupertinoIcons class for iOS style icons.
37 | cupertino_icons: ^1.0.2
38 | google_fonts: ^5.1.0
39 | flutter_hooks: ^0.20.0
40 | hooks_riverpod:
41 |
42 |
43 |
44 |
45 | # The "flutter_lints" package below contains a set of recommended lints to
46 | # encourage good coding practices. The lint set provided by the package is
47 | # activated in the `analysis_options.yaml` file located at the root of your
48 | # package. See that file for information about deactivating specific lint
49 | # rules and activating additional ones.
50 | flutter_lints: ^2.0.0
51 | hive:
52 | path_provider:
53 | hive_flutter:
54 | hive_generator:
55 | intl: ^0.18.1
56 | gap: ^3.0.1
57 |
58 | # For information on the generic Dart part of this file, see the
59 | # following page: https://dart.dev/tools/pub/pubspec
60 |
61 | # The following section is specific to Flutter packages.
62 | dev_dependencies:
63 | flutter_gen:
64 | flutter_test:
65 | sdk: flutter
66 | build_runner:
67 | dio:
68 | flutter:
69 | uses-material-design: true
70 | generate: true
71 | # To add assets to your application, add an assets section, like this:
72 | # assets:
73 | # - images/a_dot_burr.jpeg
74 | # - images/a_dot_ham.jpeg
75 |
76 | # An image asset can refer to one or more resolution-specific "variants", see
77 | # https://flutter.dev/assets-and-images/#resolution-aware
78 |
79 | # For details regarding adding assets from package dependencies, see
80 | # https://flutter.dev/assets-and-images/#from-packages
81 |
82 | # To add custom fonts to your application, add a fonts section here,
83 | # in this "flutter" section. Each entry in this list should have a
84 | # "family" key with the font family name, and a "fonts" key with a
85 | # list giving the asset and other descriptors for the font. For
86 | # example:
87 | fonts:
88 | - family: PermanentMarker
89 | fonts:
90 | - asset: assets/PermanentMarker-Regular.ttf
91 |
92 | #
93 | # For details regarding fonts from package dependencies,
94 | # see https://flutter.dev/custom-fonts/#from-packages
95 |
--------------------------------------------------------------------------------
/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
43 |
49 |
50 |
51 |
52 |
53 |
63 |
65 |
71 |
72 |
73 |
74 |
80 |
82 |
88 |
89 |
90 |
91 |
93 |
94 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/windows/runner/win32_window.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_WIN32_WINDOW_H_
2 | #define RUNNER_WIN32_WINDOW_H_
3 |
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be
11 | // inherited from by classes that wish to specialize with custom
12 | // rendering and input handling
13 | class Win32Window {
14 | public:
15 | struct Point {
16 | unsigned int x;
17 | unsigned int y;
18 |
19 | Point(unsigned int x, unsigned int y) : x(x), y(y) {}
20 | };
21 |
22 | struct Size {
23 | unsigned int width;
24 | unsigned int height;
25 |
26 | Size(unsigned int width, unsigned int height)
27 | : width(width), height(height) {}
28 | };
29 |
30 | Win32Window();
31 |
32 | virtual ~Win32Window();
33 |
34 | // Creates a win32 window with |title| that is positioned and sized using
35 | // |origin| and |size|. New windows are created on the default monitor. Window
36 | // sizes are specified to the OS in physical pixels, hence to ensure a
37 | // consistent size this function will scale the inputted width and height as
38 | // as appropriate for the default monitor. The window is invisible until
39 | // |Show| is called. Returns true if the window was created successfully.
40 | bool Create(const std::wstring &title, const Point &origin, const Size &size);
41 |
42 | // Show the current window. Returns true if the window was successfully shown.
43 | bool Show();
44 |
45 | // Release OS resources associated with window.
46 | void Destroy();
47 |
48 | // Inserts |content| into the window tree.
49 | void SetChildContent(HWND content);
50 |
51 | // Returns the backing Window handle to enable clients to set icon and other
52 | // window properties. Returns nullptr if the window has been destroyed.
53 | HWND GetHandle();
54 |
55 | // If true, closing this window will quit the application.
56 | void SetQuitOnClose(bool quit_on_close);
57 |
58 | // Return a RECT representing the bounds of the current client area.
59 | RECT GetClientArea();
60 |
61 | protected:
62 | // Processes and route salient window messages for mouse handling,
63 | // size change and DPI. Delegates handling of these to member overloads that
64 | // inheriting classes can handle.
65 | virtual LRESULT MessageHandler(HWND window,
66 | UINT const message,
67 | WPARAM const wparam,
68 | LPARAM const lparam)
69 |
70 | noexcept;
71 |
72 | // Called when CreateAndShow is called, allowing subclass window-related
73 | // setup. Subclasses should return false if setup fails.
74 | virtual bool OnCreate();
75 |
76 | // Called when Destroy is called.
77 | virtual void OnDestroy();
78 |
79 | private:
80 | friend class WindowClassRegistrar;
81 |
82 | // OS callback called by message pump. Handles the WM_NCCREATE message which
83 | // is passed when the non-client area is being created and enables automatic
84 | // non-client DPI scaling so that the non-client area automatically
85 | // responds to changes in DPI. All other messages are handled by
86 | // MessageHandler.
87 | static LRESULT CALLBACK
88 | WndProc(HWND
89 | const window,
90 | UINT const message,
91 | WPARAM
92 | const wparam,
93 | LPARAM const lparam
94 | )
95 | noexcept;
96 |
97 | // Retrieves a class instance pointer for |window|
98 | static Win32Window *GetThisFromHandle(HWND const window)
99 |
100 | noexcept;
101 |
102 | // Update the window frame's theme to match the system theme.
103 | static void UpdateTheme(HWND const window);
104 |
105 | bool quit_on_close_ = false;
106 |
107 | // window handle for top level window.
108 | HWND window_handle_ = nullptr;
109 |
110 | // window handle for hosted content.
111 | HWND child_content_ = nullptr;
112 | };
113 |
114 | #endif // RUNNER_WIN32_WINDOW_H_
115 |
--------------------------------------------------------------------------------
/windows/flutter/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # This file controls Flutter-level build steps. It should not be edited.
2 | cmake_minimum_required(VERSION 3.14)
3 |
4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
5 |
6 | # Configuration provided via flutter tool.
7 | include(${EPHEMERAL_DIR}/generated_config.cmake)
8 |
9 | # TODO: Move the rest of this into files in ephemeral. See
10 | # https://github.com/flutter/flutter/issues/57146.
11 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
12 |
13 | # === Flutter Library ===
14 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
15 |
16 | # Published to parent scope for install step.
17 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
18 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
19 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
20 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
21 |
22 | list(APPEND FLUTTER_LIBRARY_HEADERS
23 | "flutter_export.h"
24 | "flutter_windows.h"
25 | "flutter_messenger.h"
26 | "flutter_plugin_registrar.h"
27 | "flutter_texture_registrar.h"
28 | )
29 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
30 | add_library(flutter INTERFACE)
31 | target_include_directories(flutter INTERFACE
32 | "${EPHEMERAL_DIR}"
33 | )
34 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
35 | add_dependencies(flutter flutter_assemble)
36 |
37 | # === Wrapper ===
38 | list(APPEND CPP_WRAPPER_SOURCES_CORE
39 | "core_implementations.cc"
40 | "standard_codec.cc"
41 | )
42 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
43 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
44 | "plugin_registrar.cc"
45 | )
46 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
47 | list(APPEND CPP_WRAPPER_SOURCES_APP
48 | "flutter_engine.cc"
49 | "flutter_view_controller.cc"
50 | )
51 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
52 |
53 | # Wrapper sources needed for a plugin.
54 | add_library(flutter_wrapper_plugin STATIC
55 | ${CPP_WRAPPER_SOURCES_CORE}
56 | ${CPP_WRAPPER_SOURCES_PLUGIN}
57 | )
58 | apply_standard_settings(flutter_wrapper_plugin)
59 | set_target_properties(flutter_wrapper_plugin PROPERTIES
60 | POSITION_INDEPENDENT_CODE ON)
61 | set_target_properties(flutter_wrapper_plugin PROPERTIES
62 | CXX_VISIBILITY_PRESET hidden)
63 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
64 | target_include_directories(flutter_wrapper_plugin PUBLIC
65 | "${WRAPPER_ROOT}/include"
66 | )
67 | add_dependencies(flutter_wrapper_plugin flutter_assemble)
68 |
69 | # Wrapper sources needed for the runner.
70 | add_library(flutter_wrapper_app STATIC
71 | ${CPP_WRAPPER_SOURCES_CORE}
72 | ${CPP_WRAPPER_SOURCES_APP}
73 | )
74 | apply_standard_settings(flutter_wrapper_app)
75 | target_link_libraries(flutter_wrapper_app PUBLIC flutter)
76 | target_include_directories(flutter_wrapper_app PUBLIC
77 | "${WRAPPER_ROOT}/include"
78 | )
79 | add_dependencies(flutter_wrapper_app flutter_assemble)
80 |
81 | # === Flutter tool backend ===
82 | # _phony_ is a non-existent file to force this command to run every time,
83 | # since currently there's no way to get a full input/output list from the
84 | # flutter tool.
85 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
86 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
87 | add_custom_command(
88 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
89 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
90 | ${CPP_WRAPPER_SOURCES_APP}
91 | ${PHONY_OUTPUT}
92 | COMMAND ${CMAKE_COMMAND} -E env
93 | ${FLUTTER_TOOL_ENVIRONMENT}
94 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
95 | windows-x64 $
96 | VERBATIM
97 | )
98 | add_custom_target(flutter_assemble DEPENDS
99 | "${FLUTTER_LIBRARY}"
100 | ${FLUTTER_LIBRARY_HEADERS}
101 | ${CPP_WRAPPER_SOURCES_CORE}
102 | ${CPP_WRAPPER_SOURCES_PLUGIN}
103 | ${CPP_WRAPPER_SOURCES_APP}
104 | )
105 |
--------------------------------------------------------------------------------
/linux/my_application.cc:
--------------------------------------------------------------------------------
1 | #include "my_application.h"
2 |
3 | #include
4 |
5 | #ifdef GDK_WINDOWING_X11
6 | #include
7 | #endif
8 |
9 | #include "flutter/generated_plugin_registrant.h"
10 |
11 | struct _MyApplication {
12 | GtkApplication parent_instance;
13 | char **dart_entrypoint_arguments;
14 | };
15 |
16 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION
17 | )
18 |
19 | // Implements GApplication::activate.
20 | static void my_application_activate(GApplication *application) {
21 | MyApplication * self = MY_APPLICATION(application);
22 | GtkWindow *window =
23 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
24 |
25 | // Use a header bar when running in GNOME as this is the common style used
26 | // by applications and is the setup most users will be using (e.g. Ubuntu
27 | // desktop).
28 | // If running on X and not using GNOME then just use a traditional title bar
29 | // in case the window manager does more exotic layout, e.g. tiling.
30 | // If running on Wayland assume the header bar will work (may need changing
31 | // if future cases occur).
32 | gboolean use_header_bar = TRUE;
33 | #ifdef GDK_WINDOWING_X11
34 | GdkScreen* screen = gtk_window_get_screen(window);
35 | if (GDK_IS_X11_SCREEN(screen)) {
36 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
37 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
38 | use_header_bar = FALSE;
39 | }
40 | }
41 | #endif
42 | if (use_header_bar) {
43 | GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
44 | gtk_widget_show(GTK_WIDGET(header_bar));
45 | gtk_header_bar_set_title(header_bar, "flutter_tic_tac_toe");
46 | gtk_header_bar_set_show_close_button(header_bar, TRUE);
47 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
48 | } else {
49 | gtk_window_set_title(window, "flutter_tic_tac_toe");
50 | }
51 |
52 | gtk_window_set_default_size(window, 1280, 720);
53 | gtk_widget_show(GTK_WIDGET(window));
54 |
55 | g_autoptr(FlDartProject)
56 | project = fl_dart_project_new();
57 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
58 |
59 | FlView *view = fl_view_new(project);
60 | gtk_widget_show(GTK_WIDGET(view));
61 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
62 |
63 | fl_register_plugins(FL_PLUGIN_REGISTRY(view));
64 |
65 | gtk_widget_grab_focus(GTK_WIDGET(view));
66 | }
67 |
68 | // Implements GApplication::local_command_line.
69 | static gboolean
70 | my_application_local_command_line(GApplication *application, gchar ***arguments, int *exit_status) {
71 | MyApplication * self = MY_APPLICATION(application);
72 | // Strip out the first argument as it is the binary name.
73 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
74 |
75 | g_autoptr(GError)
76 | error = nullptr;
77 | if (!g_application_register(application, nullptr, &error)) {
78 | g_warning("Failed to register: %s", error->message);
79 | *exit_status = 1;
80 | return TRUE;
81 | }
82 |
83 | g_application_activate(application);
84 | *exit_status = 0;
85 |
86 | return TRUE;
87 | }
88 |
89 | // Implements GObject::dispose.
90 | static void my_application_dispose(GObject *object) {
91 | MyApplication * self = MY_APPLICATION(object);
92 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
93 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
94 | }
95 |
96 | static void my_application_class_init(MyApplicationClass *klass) {
97 | G_APPLICATION_CLASS(klass)->activate = my_application_activate;
98 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
99 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
100 | }
101 |
102 | static void my_application_init(MyApplication * self) {}
103 |
104 | MyApplication *my_application_new() {
105 | return MY_APPLICATION(g_object_new(my_application_get_type(),
106 | "application-id", APPLICATION_ID,
107 | "flags", G_APPLICATION_NON_UNIQUE,
108 | nullptr));
109 | }
110 |
--------------------------------------------------------------------------------
/windows/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Project-level configuration.
2 | cmake_minimum_required(VERSION 3.14)
3 | project(flutter_tic_tac_toe LANGUAGES CXX)
4 |
5 | # The name of the executable created for the application. Change this to change
6 | # the on-disk name of your application.
7 | set(BINARY_NAME "flutter_tic_tac_toe")
8 |
9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent
10 | # versions of CMake.
11 | cmake_policy(SET CMP0063 NEW)
12 |
13 | # Define build configuration option.
14 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
15 | if (IS_MULTICONFIG)
16 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
17 | CACHE STRING "" FORCE)
18 | else ()
19 | if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
20 | set(CMAKE_BUILD_TYPE "Debug" CACHE
21 | STRING "Flutter build mode" FORCE)
22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
23 | "Debug" "Profile" "Release")
24 | endif ()
25 | endif ()
26 | # Define settings for the Profile build mode.
27 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
28 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
29 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
30 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
31 |
32 | # Use Unicode for all projects.
33 | add_definitions(-DUNICODE -D_UNICODE)
34 |
35 | # Compilation settings that should be applied to most targets.
36 | #
37 | # Be cautious about adding new options here, as plugins use this function by
38 | # default. In most cases, you should add new options to specific targets instead
39 | # of modifying this function.
40 | function(APPLY_STANDARD_SETTINGS TARGET)
41 | target_compile_features(${TARGET} PUBLIC cxx_std_17)
42 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
43 | target_compile_options(${TARGET} PRIVATE /EHsc)
44 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
45 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>")
46 | endfunction()
47 |
48 | # Flutter library and tool build rules.
49 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
50 | add_subdirectory(${FLUTTER_MANAGED_DIR})
51 |
52 | # Application build; see runner/CMakeLists.txt.
53 | add_subdirectory("runner")
54 |
55 |
56 | # Generated plugin build rules, which manage building the plugins and adding
57 | # them to the application.
58 | include(flutter/generated_plugins.cmake)
59 |
60 |
61 | # === Installation ===
62 | # Support files are copied into place next to the executable, so that it can
63 | # run in place. This is done instead of making a separate bundle (as on Linux)
64 | # so that building and running from within Visual Studio will work.
65 | set(BUILD_BUNDLE_DIR "$")
66 | # Make the "install" step default, as it's required to run.
67 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
68 | if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
69 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
70 | endif ()
71 |
72 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
73 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
74 |
75 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
76 | COMPONENT Runtime)
77 |
78 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
79 | COMPONENT Runtime)
80 |
81 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
82 | COMPONENT Runtime)
83 |
84 | if (PLUGIN_BUNDLED_LIBRARIES)
85 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
86 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
87 | COMPONENT Runtime)
88 | endif ()
89 |
90 | # Fully re-copy the assets directory on each build to avoid having stale files
91 | # from a previous install.
92 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
93 | install(CODE "
94 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
95 | " COMPONENT Runtime)
96 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
97 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
98 |
99 | # Install the AOT library on non-Debug builds only.
100 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
101 | CONFIGURATIONS Profile;Release
102 | COMPONENT Runtime)
103 |
--------------------------------------------------------------------------------
/linux/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Project-level configuration.
2 | cmake_minimum_required(VERSION 3.10)
3 | project(runner LANGUAGES CXX)
4 |
5 | # The name of the executable created for the application. Change this to change
6 | # the on-disk name of your application.
7 | set(BINARY_NAME "flutter_tic_tac_toe")
8 | # The unique GTK application identifier for this application. See:
9 | # https://wiki.gnome.org/HowDoI/ChooseApplicationID
10 | set(APPLICATION_ID "com.tictactoe.flutter_tic_tac_toe")
11 |
12 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent
13 | # versions of CMake.
14 | cmake_policy(SET CMP0063 NEW)
15 |
16 | # Load bundled libraries from the lib/ directory relative to the binary.
17 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
18 |
19 | # Root filesystem for cross-building.
20 | if (FLUTTER_TARGET_PLATFORM_SYSROOT)
21 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
22 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
23 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
24 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
25 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
26 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
27 | endif ()
28 |
29 | # Define build configuration options.
30 | if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
31 | set(CMAKE_BUILD_TYPE "Debug" CACHE
32 | STRING "Flutter build mode" FORCE)
33 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
34 | "Debug" "Profile" "Release")
35 | endif ()
36 |
37 | # Compilation settings that should be applied to most targets.
38 | #
39 | # Be cautious about adding new options here, as plugins use this function by
40 | # default. In most cases, you should add new options to specific targets instead
41 | # of modifying this function.
42 | function(APPLY_STANDARD_SETTINGS TARGET)
43 | target_compile_features(${TARGET} PUBLIC cxx_std_14)
44 | target_compile_options(${TARGET} PRIVATE -Wall -Werror)
45 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
46 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
47 | endfunction()
48 |
49 | # Flutter library and tool build rules.
50 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
51 | add_subdirectory(${FLUTTER_MANAGED_DIR})
52 |
53 | # System-level dependencies.
54 | find_package(PkgConfig REQUIRED)
55 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
56 |
57 | add_definitions(-DAPPLICATION_ID= "${APPLICATION_ID}")
58 |
59 | # Define the application target. To change its name, change BINARY_NAME above,
60 | # not the value here, or `flutter run` will no longer work.
61 | #
62 | # Any new source files that you add to the application should be added here.
63 | add_executable(${BINARY_NAME}
64 | "main.cc"
65 | "my_application.cc"
66 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
67 | )
68 |
69 | # Apply the standard set of build settings. This can be removed for applications
70 | # that need different build settings.
71 | apply_standard_settings(${BINARY_NAME})
72 |
73 | # Add dependency libraries. Add any application-specific dependencies here.
74 | target_link_libraries(${BINARY_NAME} PRIVATE flutter)
75 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
76 |
77 | # Run the Flutter tool portions of the build. This must not be removed.
78 | add_dependencies(${BINARY_NAME} flutter_assemble)
79 |
80 | # Only the install-generated bundle's copy of the executable will launch
81 | # correctly, since the resources must in the right relative locations. To avoid
82 | # people trying to run the unbundled copy, put it in a subdirectory instead of
83 | # the default top-level location.
84 | set_target_properties(${BINARY_NAME}
85 | PROPERTIES
86 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
87 | )
88 |
89 |
90 | # Generated plugin build rules, which manage building the plugins and adding
91 | # them to the application.
92 | include(flutter/generated_plugins.cmake)
93 |
94 |
95 | # === Installation ===
96 | # By default, "installing" just makes a relocatable bundle in the build
97 | # directory.
98 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
99 | if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
100 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
101 | endif ()
102 |
103 | # Start with a clean build bundle directory every time.
104 | install(CODE "
105 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
106 | " COMPONENT Runtime)
107 |
108 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
109 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
110 |
111 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
112 | COMPONENT Runtime)
113 |
114 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
115 | COMPONENT Runtime)
116 |
117 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
118 | COMPONENT Runtime)
119 |
120 | foreach (bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
121 | install(FILES "${bundled_library}"
122 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
123 | COMPONENT Runtime)
124 | endforeach (bundled_library)
125 |
126 | # Fully re-copy the assets directory on each build to avoid having stale files
127 | # from a previous install.
128 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
129 | install(CODE "
130 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
131 | " COMPONENT Runtime)
132 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
133 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
134 |
135 | # Install the AOT library on non-Debug builds only.
136 | if (NOT CMAKE_BUILD_TYPE MATCHES "Debug")
137 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
138 | COMPONENT Runtime)
139 | endif ()
140 |
--------------------------------------------------------------------------------
/lib/widgets/history_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_tic_tac_toe/model/history_hive_model.dart';
3 | import 'package:flutter_tic_tac_toe/storage/history_box.dart';
4 | import 'package:flutter_tic_tac_toe/theme/app_sizes.dart';
5 | import 'package:flutter_tic_tac_toe/theme/colors.dart';
6 | import 'package:intl/intl.dart';
7 |
8 | class HistoryListView extends StatelessWidget {
9 | const HistoryListView({
10 | Key? key,
11 | }) : super(key: key);
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | List historyList = HistoryBox.getHistory();
16 |
17 | historyList.sort((a, b) => b.date.compareTo(a.date));
18 | return Scaffold(
19 | backgroundColor: GameColors.kLighterForeground,
20 | body: historyList.isEmpty
21 | ? const Center(
22 | child: Text(
23 | 'No game history !',
24 | style: TextStyle(
25 | fontSize: 20,
26 | color: GameColors.kWhitish,
27 | fontWeight: FontWeight.bold,
28 | ),
29 | ),
30 | )
31 | : CustomScrollView(
32 | physics: const BouncingScrollPhysics(
33 | parent: AlwaysScrollableScrollPhysics(),
34 | ),
35 | slivers: [
36 | SliverList(
37 | delegate: SliverChildBuilderDelegate(
38 | (BuildContext context, int index) => Container(
39 | margin: const EdgeInsets.symmetric(
40 | horizontal: 5, vertical: 5),
41 | width: double.infinity,
42 | decoration: BoxDecoration(
43 | color: Colors.white,
44 | borderRadius: BorderRadius.circular(10),
45 | boxShadow: [
46 | BoxShadow(
47 | color: Colors.grey.withOpacity(0.2),
48 | spreadRadius: 4,
49 | blurRadius: 4,
50 | offset: const Offset(0, 1),
51 | ),
52 | ],
53 | ),
54 | child: IntrinsicHeight(
55 | child: Row(
56 | crossAxisAlignment: CrossAxisAlignment.stretch,
57 | children: [
58 | Container(
59 | width: 10,
60 | decoration: BoxDecoration(
61 | color: historyList[index].winner == 'X'
62 | ? GameColors.kBlue
63 | : historyList[index].winner == 'draw'
64 | ? GameColors.kGrey
65 | : GameColors.kPurple,
66 | borderRadius: const BorderRadius.only(
67 | topLeft: Radius.circular(50),
68 | bottomLeft: Radius.circular(50),
69 | ),
70 | ),
71 | ),
72 | Expanded(
73 | child: Padding(
74 | padding: const EdgeInsets.symmetric(
75 | vertical: 5, horizontal: 30),
76 | child: Column(
77 | children: [
78 | Text(
79 | DateFormat('MMM d, y HH:mm').format(
80 | historyList[index].date.toLocal()),
81 | style: const TextStyle(
82 | fontSize: 14,
83 | fontWeight: FontWeight.w100,
84 | color: Colors.black,
85 | ),
86 | ),
87 | gapM(),
88 | Row(
89 | mainAxisAlignment:
90 | MainAxisAlignment.spaceBetween,
91 | children: [
92 | Text(
93 | historyList[index].playerXName,
94 | style: const TextStyle(
95 | fontSize: 20,
96 | fontWeight: FontWeight.bold,
97 | color: GameColors.kBlue,
98 | ),
99 | ),
100 | const Text(
101 | "VS",
102 | style: TextStyle(
103 | fontSize: 30,
104 | fontWeight: FontWeight.bold,
105 | ),
106 | ),
107 | Text(
108 | historyList[index].playerOName,
109 | style: const TextStyle(
110 | color: GameColors.kPurple,
111 | fontSize: 20,
112 | fontWeight: FontWeight.bold,
113 | ),
114 | ),
115 | ],
116 | ),
117 | ],
118 | ),
119 | ),
120 | ),
121 | ],
122 | ),
123 | ),
124 | ),
125 | childCount: historyList.length,
126 | ),
127 | ),
128 | ],
129 | ),
130 | );
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/lib/screens/players_names_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_hooks/flutter_hooks.dart';
3 | import 'package:flutter_tic_tac_toe/theme/app_sizes.dart';
4 | import 'package:flutter_tic_tac_toe/theme/colors.dart';
5 | import 'package:flutter_tic_tac_toe/widgets/button_widget.dart';
6 |
7 | import '../widgets/wrapper_container.dart';
8 | import 'game_base_screen.dart';
9 |
10 | class PlayerNames extends HookWidget {
11 | const PlayerNames({super.key});
12 |
13 | Widget buildTextField(String hintText, IconData icon, bool isX,
14 | ValueSetter onChanged, TextEditingController controller) =>
15 | TextField(
16 | cursorColor: isX ? GameColors.kWhitish : GameColors.kPurple,
17 | style: const TextStyle(
18 | color: GameColors.kWhitish,
19 | ),
20 | controller: controller,
21 | onChanged: onChanged,
22 | maxLength: 10,
23 | decoration: InputDecoration(
24 | counterText: '',
25 | filled: true,
26 | border: OutlineInputBorder(
27 | borderSide: BorderSide.none,
28 | borderRadius: borderRadiusM(),
29 | ),
30 | fillColor: GameColors.kForeground,
31 | hintText: hintText,
32 | hintStyle: const TextStyle(color: GameColors.kBackground),
33 | prefixIcon: Icon(
34 | icon,
35 | color: isX ? GameColors.kBlue : GameColors.kPurple,
36 | ),
37 | ),
38 | );
39 |
40 | @override
41 | Widget build(BuildContext context) {
42 | final playerXController = useTextEditingController();
43 | final playerOController = useTextEditingController();
44 | final isBtnEnabled = useValueNotifier(false);
45 | void checkFields() {
46 | isBtnEnabled.value = playerXController.text.isNotEmpty &&
47 | playerOController.text.isNotEmpty;
48 | }
49 |
50 | return Scaffold(
51 | appBar: AppBar(
52 | backgroundColor: GameColors.kGradient1,
53 | leading: IconButton(
54 | onPressed: () {
55 | Navigator.pop(context);
56 | },
57 | icon: const Icon(
58 | Icons.arrow_back_outlined,
59 | color: GameColors.kWhitish,
60 | ),
61 | ),
62 | ),
63 | body: WrapperContainer(
64 | child: SingleChildScrollView(
65 | child: SizedBox(
66 | height: MediaQuery.of(context).size.height,
67 | child: Column(
68 | mainAxisAlignment: MainAxisAlignment.start,
69 | children: [
70 | const Expanded(
71 | child: Text(
72 | 'Enter Player Names',
73 | style: TextStyle(
74 | color: Colors.white,
75 | fontSize: 20,
76 | ),
77 | ),
78 | ),
79 | gapXL(),
80 | Expanded(
81 | flex: 3,
82 | child: Column(
83 | children: [
84 | buildTextField('Player X', Icons.close, true, (value) {
85 | checkFields();
86 | }, playerXController),
87 | gapL(),
88 | buildTextField('Player O', Icons.circle_outlined, false,
89 | (value) {
90 | checkFields();
91 | }, playerOController),
92 | gap2XL(),
93 | SizedBox(
94 | width: double.infinity,
95 | child: HookBuilder(builder: (context) {
96 | final isEnabled = useValueListenable(isBtnEnabled);
97 | return ButtonWidget(
98 | isEnabled: isEnabled,
99 | onPressed: () {
100 | if (playerXController.text
101 | .toLowerCase()
102 | .trim() ==
103 | playerOController.text
104 | .toLowerCase()
105 | .trim()) {
106 | ScaffoldMessenger.of(context).showSnackBar(
107 | const SnackBar(
108 | backgroundColor: GameColors.kForeground,
109 | content: Text(
110 | 'Please enter different names',
111 | style: TextStyle(
112 | color: GameColors.kWhitish,
113 | ),
114 | ),
115 | ),
116 | );
117 |
118 | return;
119 | }
120 | FocusScope.of(context).unfocus(
121 | disposition: UnfocusDisposition
122 | .previouslyFocusedChild);
123 |
124 | Navigator.push(
125 | context,
126 | MaterialPageRoute(
127 | builder: (context) => GameBaseScreen(
128 | playerOName: playerOController.text
129 | .toLowerCase()
130 | .trim(),
131 | playerXName: playerXController.text
132 | .toLowerCase()
133 | .trim(),
134 | isAgainstAI: false,
135 | ),
136 | ),
137 | );
138 | },
139 | text: 'Start Game');
140 | }),
141 | ),
142 | ],
143 | ),
144 | ),
145 | ],
146 | ),
147 | ),
148 | ),
149 | ),
150 | );
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/lib/screens/game_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:google_fonts/google_fonts.dart';
3 | import 'package:hooks_riverpod/hooks_riverpod.dart';
4 |
5 | import '../game_logic/check_result.dart';
6 | import '../game_logic/minimax.dart';
7 | import '../model/history_hive_model.dart';
8 | import '../providers/game_providers.dart';
9 | import '../storage/history_box.dart';
10 | import '../theme/colors.dart';
11 | import '../widgets/alert_dialog.dart';
12 | import '../widgets/scoreboard.dart';
13 | import '../widgets/wrapper_container.dart';
14 |
15 | int playerXScore = 0;
16 | int playerOScore = 0;
17 | bool isAIPlaying = false;
18 |
19 | class GameScreen extends ConsumerStatefulWidget {
20 | final String playerXName;
21 | final String playerOName;
22 | final bool isAgainstAI;
23 |
24 | const GameScreen({
25 | Key? key,
26 | required this.playerXName,
27 | required this.playerOName,
28 | required this.isAgainstAI,
29 | }) : super(key: key);
30 |
31 | @override
32 | ConsumerState createState() => _GameScreenState();
33 | }
34 |
35 | class _GameScreenState extends ConsumerState {
36 | @override
37 | void dispose() {
38 | playerXScore = 0;
39 | playerOScore = 0;
40 | isAIPlaying = false;
41 | print('GameScreen disposed');
42 |
43 | super.dispose();
44 | }
45 |
46 | @override
47 | Widget build(BuildContext context) {
48 | final board = ref.watch(boardProvider);
49 | final currentPlayer = ref.watch(currentPlayerProvider);
50 | final winner = ref.watch(winnerProvider);
51 |
52 | void makeAIMove(boardNotifier, currentPlayerNotifier, winnerNotifier,
53 | BuildContext context) {
54 | int bestScore = -1000;
55 | int bestMoveRow = -1;
56 | int bestMoveCol = -1;
57 | for (int i = 0; i < 3; i++) {
58 | for (int j = 0; j < 3; j++) {
59 | if (board[i][j].isEmpty) {
60 | board[i][j] = 'O';
61 | int score = minimax(board, false);
62 | board[i][j] = '';
63 |
64 | if (score > bestScore) {
65 | bestScore = score;
66 | bestMoveRow = i;
67 | bestMoveCol = j;
68 | }
69 | }
70 | }
71 | }
72 |
73 | if (bestMoveRow != -1 && bestMoveCol != -1) {
74 | boardNotifier.updateBoard(bestMoveRow, bestMoveCol, 'O');
75 | if (checkWin(board, 'O')) {
76 | winnerNotifier.updateWinner('O');
77 | playerOScore++;
78 | showGameAlertDialog(
79 | "AI wins!",
80 | context,
81 | 'O',
82 | () => resetGame('O', ref, isAgainstAI: widget.isAgainstAI),
83 | );
84 | } else if (checkDraw(board)) {
85 | winnerNotifier.updateWinner('draw');
86 | showGameAlertDialog(
87 | "It's a draw!",
88 | context,
89 | 'draw',
90 | () => resetGame('draw', ref, isAgainstAI: widget.isAgainstAI),
91 | );
92 | } else {
93 | currentPlayerNotifier.togglePlayer();
94 | }
95 | }
96 | }
97 |
98 | void onCellTap(int row, int col) {
99 | if (isAIPlaying) return;
100 | final boardNotifier = ref.read(boardProvider.notifier);
101 | final currentPlayerNotifier = ref.read(currentPlayerProvider.notifier);
102 | final winnerNotifier = ref.read(winnerProvider.notifier);
103 |
104 | if (board[row][col].isEmpty && winner.isEmpty) {
105 | final currentPlayerValue = currentPlayer;
106 | boardNotifier.updateBoard(row, col, currentPlayerValue);
107 |
108 | if (checkWin(board, currentPlayerValue)) {
109 | winnerNotifier.updateWinner(currentPlayerValue);
110 | if (currentPlayerValue == 'X') {
111 | playerXScore++;
112 | } else {
113 | playerOScore++;
114 | }
115 | showGameAlertDialog(
116 | "Player $currentPlayerValue wins!",
117 | context,
118 | currentPlayerValue,
119 | () => resetGame(currentPlayerValue, ref),
120 | );
121 | HistoryBox.setHistory(HistoryModelHive(
122 | playerXName: widget.playerXName,
123 | playerOName: widget.playerOName,
124 | winner: currentPlayerValue,
125 | ));
126 | } else if (checkDraw(board)) {
127 | winnerNotifier.updateWinner('draw');
128 | showGameAlertDialog(
129 | "It's a draw!",
130 | context,
131 | "draw",
132 | () => resetGame(currentPlayerValue, ref),
133 | );
134 | HistoryBox.setHistory(HistoryModelHive(
135 | playerXName: widget.playerXName,
136 | playerOName: widget.playerOName,
137 | winner: 'draw',
138 | ));
139 | } else {
140 | currentPlayerNotifier.togglePlayer();
141 | if (widget.isAgainstAI && currentPlayerValue == 'X') {
142 | isAIPlaying = true;
143 | Future.delayed(const Duration(milliseconds: 500), () {
144 | makeAIMove(boardNotifier, currentPlayerNotifier, winnerNotifier,
145 | context);
146 | isAIPlaying = false;
147 | });
148 | }
149 | }
150 | }
151 | }
152 |
153 | return Scaffold(
154 | body: WrapperContainer(
155 | child: Center(
156 | child: SingleChildScrollView(
157 | child: SizedBox(
158 | height: MediaQuery.of(context).size.height - kToolbarHeight,
159 | child: Column(
160 | mainAxisAlignment: MainAxisAlignment.center,
161 | children: [
162 | Expanded(
163 | flex: 2,
164 | child: ScoreBoard(
165 | playerXName: widget.playerXName,
166 | playerOName: widget.playerOName,
167 | playerXScore: playerXScore,
168 | playerOScore: playerOScore,
169 | isTurn: currentPlayer == 'X',
170 | ),
171 | ),
172 | const Spacer(),
173 | Expanded(
174 | flex: 6,
175 | child: Container(
176 | margin: const EdgeInsets.all(16),
177 | child: GridView.builder(
178 | padding: const EdgeInsets.all(5.0),
179 | shrinkWrap: true,
180 | gridDelegate:
181 | const SliverGridDelegateWithFixedCrossAxisCount(
182 | crossAxisCount: 3,
183 | crossAxisSpacing: 10.0,
184 | mainAxisSpacing: 10.0,
185 | ),
186 | itemBuilder: (context, index) {
187 | int row = index ~/ 3;
188 | int col = index % 3;
189 |
190 | return GestureDetector(
191 | onTap: () {
192 | if (!isAIPlaying) {
193 | onCellTap(row, col);
194 | }
195 | },
196 | child: Container(
197 | decoration: const BoxDecoration(
198 | color: Colors.white,
199 | borderRadius: BorderRadius.all(
200 | Radius.circular(10.0),
201 | ),
202 | ),
203 | child: Center(
204 | child: Text(
205 | board[row][col],
206 | style: TextStyle(
207 | fontSize: 50,
208 | fontFamily: GoogleFonts.permanentMarker()
209 | .fontFamily,
210 | fontWeight: FontWeight.bold,
211 | color: board[row][col] == 'X'
212 | ? GameColors.kBlue
213 | : GameColors.kPurple,
214 | ),
215 | ),
216 | ),
217 | ),
218 | );
219 | },
220 | itemCount: 9,
221 | ),
222 | ),
223 | ),
224 | ],
225 | ),
226 | ),
227 | ),
228 | ),
229 | ),
230 | );
231 | }
232 | }
233 |
--------------------------------------------------------------------------------
/windows/runner/win32_window.cpp:
--------------------------------------------------------------------------------
1 | #include "win32_window.h"
2 |
3 | #include
4 | #include
5 |
6 | #include "resource.h"
7 |
8 | namespace {
9 |
10 | /// Window attribute that enables dark mode window decorations.
11 | ///
12 | /// Redefined in case the developer's machine has a Windows SDK older than
13 | /// version 10.0.22000.0.
14 | /// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
15 | #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
16 | #define DWMWA_USE_IMMERSIVE_DARK_MODE 20
17 | #endif
18 |
19 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
20 |
21 | /// Registry key for app theme preference.
22 | ///
23 | /// A value of 0 indicates apps should use dark mode. A non-zero or missing
24 | /// value indicates apps should use light mode.
25 | constexpr const wchar_t kGetPreferredBrightnessRegKey[] =
26 | L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
27 | constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";
28 |
29 | // The number of Win32Window objects that currently exist.
30 | static int g_active_window_count = 0;
31 |
32 | using EnableNonClientDpiScaling =
33 | BOOL __stdcall(HWND
34 | hwnd);
35 |
36 | // Scale helper to convert logical scaler values to physical using passed in
37 | // scale factor
38 | int Scale(int source, double scale_factor) {
39 | return static_cast(source * scale_factor);
40 | }
41 |
42 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
43 | // This API is only needed for PerMonitor V1 awareness mode.
44 | void EnableFullDpiSupportIfAvailable(HWND
45 | hwnd) {
46 | HMODULE user32_module = LoadLibraryA("User32.dll");
47 | if (!user32_module) {
48 | return;
49 | }
50 | auto enable_non_client_dpi_scaling =
51 | reinterpret_cast(
52 | GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
53 | if (enable_non_client_dpi_scaling != nullptr) {
54 | enable_non_client_dpi_scaling(hwnd);
55 | }
56 | FreeLibrary(user32_module);
57 | }
58 |
59 | } // namespace
60 |
61 | // Manages the Win32Window's window class registration.
62 | class WindowClassRegistrar {
63 | public:
64 | ~WindowClassRegistrar() = default;
65 |
66 | // Returns the singleton registrar instance.
67 | static WindowClassRegistrar *GetInstance() {
68 | if (!instance_) {
69 | instance_ = new WindowClassRegistrar();
70 | }
71 | return instance_;
72 | }
73 |
74 | // Returns the name of the window class, registering the class if it hasn't
75 | // previously been registered.
76 | const wchar_t *GetWindowClass();
77 |
78 | // Unregisters the window class. Should only be called if there are no
79 | // instances of the window.
80 | void UnregisterWindowClass();
81 |
82 | private:
83 | WindowClassRegistrar() = default;
84 |
85 | static WindowClassRegistrar *instance_;
86 |
87 | bool class_registered_ = false;
88 | };
89 |
90 | WindowClassRegistrar *WindowClassRegistrar::instance_ = nullptr;
91 |
92 | const wchar_t *WindowClassRegistrar::GetWindowClass() {
93 | if (!class_registered_) {
94 | WNDCLASS window_class{};
95 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
96 | window_class.lpszClassName = kWindowClassName;
97 | window_class.style = CS_HREDRAW | CS_VREDRAW;
98 | window_class.cbClsExtra = 0;
99 | window_class.cbWndExtra = 0;
100 | window_class.hInstance = GetModuleHandle(nullptr);
101 | window_class.hIcon =
102 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
103 | window_class.hbrBackground = 0;
104 | window_class.lpszMenuName = nullptr;
105 | window_class.lpfnWndProc = Win32Window::WndProc;
106 | RegisterClass(&window_class);
107 | class_registered_ = true;
108 | }
109 | return kWindowClassName;
110 | }
111 |
112 | void WindowClassRegistrar::UnregisterWindowClass() {
113 | UnregisterClass(kWindowClassName, nullptr);
114 | class_registered_ = false;
115 | }
116 |
117 | Win32Window::Win32Window() {
118 | ++g_active_window_count;
119 | }
120 |
121 | Win32Window::~Win32Window() {
122 | --g_active_window_count;
123 | Destroy();
124 | }
125 |
126 | bool Win32Window::Create(const std::wstring &title,
127 | const Point &origin,
128 | const Size &size) {
129 | Destroy();
130 |
131 | const wchar_t *window_class =
132 | WindowClassRegistrar::GetInstance()->GetWindowClass();
133 |
134 | const POINT target_point = {static_cast(origin.x),
135 | static_cast(origin.y)};
136 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
137 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
138 | double scale_factor = dpi / 96.0;
139 |
140 | HWND window = CreateWindow(
141 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
142 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
143 | Scale(size.width, scale_factor), Scale(size.height, scale_factor),
144 | nullptr, nullptr, GetModuleHandle(nullptr), this);
145 |
146 | if (!window) {
147 | return false;
148 | }
149 |
150 | UpdateTheme(window);
151 |
152 | return OnCreate();
153 | }
154 |
155 | bool Win32Window::Show() {
156 | return ShowWindow(window_handle_, SW_SHOWNORMAL);
157 | }
158 |
159 | // static
160 | LRESULT CALLBACK
161 | Win32Window::WndProc(HWND
162 | const window,
163 | UINT const message,
164 | WPARAM
165 | const wparam,
166 | LPARAM const lparam
167 | ) noexcept {
168 | if (message == WM_NCCREATE) {
169 | auto window_struct = reinterpret_cast(lparam);
170 | SetWindowLongPtr(window, GWLP_USERDATA,
171 | reinterpret_cast
172 | (window_struct
173 | ->lpCreateParams));
174 |
175 | auto that = static_cast(window_struct->lpCreateParams);
176 | EnableFullDpiSupportIfAvailable(window);
177 | that->
178 | window_handle_ = window;
179 | } else if (
180 | Win32Window *that = GetThisFromHandle(window)
181 | ) {
182 | return that->
183 | MessageHandler(window, message, wparam, lparam
184 | );
185 | }
186 |
187 | return
188 | DefWindowProc(window, message, wparam, lparam
189 | );
190 | }
191 |
192 | LRESULT
193 | Win32Window::MessageHandler(HWND
194 | hwnd,
195 | UINT const message,
196 | WPARAM
197 | const wparam,
198 | LPARAM const lparam
199 | ) noexcept {
200 | switch (message) {
201 | case WM_DESTROY:
202 | window_handle_ = nullptr;
203 |
204 | Destroy();
205 |
206 | if (quit_on_close_) {
207 | PostQuitMessage(0);
208 | }
209 | return 0;
210 |
211 | case WM_DPICHANGED: {
212 | auto newRectSize = reinterpret_cast(lparam);
213 | LONG newWidth = newRectSize->right - newRectSize->left;
214 | LONG newHeight = newRectSize->bottom - newRectSize->top;
215 |
216 | SetWindowPos(hwnd, nullptr, newRectSize
217 | ->left, newRectSize->top, newWidth,
218 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
219 |
220 | return 0;
221 | }
222 | case WM_SIZE: {
223 | RECT rect = GetClientArea();
224 | if (child_content_ != nullptr) {
225 | // Size and position the child window.
226 | MoveWindow(child_content_, rect
227 | .left, rect.top, rect.right - rect.left,
228 | rect.bottom - rect.top, TRUE);
229 | }
230 | return 0;
231 | }
232 |
233 | case WM_ACTIVATE:
234 | if (child_content_ != nullptr) {
235 | SetFocus(child_content_);
236 | }
237 | return 0;
238 |
239 | case WM_DWMCOLORIZATIONCOLORCHANGED:
240 | UpdateTheme(hwnd);
241 | return 0;
242 | }
243 |
244 | return
245 | DefWindowProc(window_handle_, message, wparam, lparam
246 | );
247 | }
248 |
249 | void Win32Window::Destroy() {
250 | OnDestroy();
251 |
252 | if (window_handle_) {
253 | DestroyWindow(window_handle_);
254 | window_handle_ = nullptr;
255 | }
256 | if (g_active_window_count == 0) {
257 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
258 | }
259 | }
260 |
261 | Win32Window *Win32Window::GetThisFromHandle(HWND const window)
262 |
263 | noexcept {
264 | return reinterpret_cast(
265 | GetWindowLongPtr(window, GWLP_USERDATA
266 | ));
267 | }
268 |
269 | void Win32Window::SetChildContent(HWND
270 | content) {
271 | child_content_ = content;
272 | SetParent(content, window_handle_
273 | );
274 | RECT frame = GetClientArea();
275 |
276 | MoveWindow(content, frame
277 | .left, frame.top, frame.right - frame.left,
278 | frame.bottom - frame.top, true);
279 |
280 | SetFocus(child_content_);
281 | }
282 |
283 | RECT Win32Window::GetClientArea() {
284 | RECT frame;
285 | GetClientRect(window_handle_, &frame);
286 | return frame;
287 | }
288 |
289 | HWND Win32Window::GetHandle() {
290 | return window_handle_;
291 | }
292 |
293 | void Win32Window::SetQuitOnClose(bool quit_on_close) {
294 | quit_on_close_ = quit_on_close;
295 | }
296 |
297 | bool Win32Window::OnCreate() {
298 | // No-op; provided for subclasses.
299 | return true;
300 | }
301 |
302 | void Win32Window::OnDestroy() {
303 | // No-op; provided for subclasses.
304 | }
305 |
306 | void Win32Window::UpdateTheme(HWND const window) {
307 | DWORD light_mode;
308 | DWORD light_mode_size = sizeof(light_mode);
309 | LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
310 | kGetPreferredBrightnessRegValue,
311 | RRF_RT_REG_DWORD, nullptr, &light_mode,
312 | &light_mode_size);
313 |
314 | if (result == ERROR_SUCCESS) {
315 | BOOL enable_dark_mode = light_mode == 0;
316 | DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,
317 | &enable_dark_mode, sizeof(enable_dark_mode));
318 | }
319 | }
320 |
--------------------------------------------------------------------------------
/macos/Runner/Base.lproj/MainMenu.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 54;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
16 | 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXContainerItemProxy section */
20 | 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
21 | isa = PBXContainerItemProxy;
22 | containerPortal = 97C146E61CF9000F007C117D /* Project object */;
23 | proxyType = 1;
24 | remoteGlobalIDString = 97C146ED1CF9000F007C117D;
25 | remoteInfo = Runner;
26 | };
27 | /* End PBXContainerItemProxy section */
28 |
29 | /* Begin PBXCopyFilesBuildPhase section */
30 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
31 | isa = PBXCopyFilesBuildPhase;
32 | buildActionMask = 2147483647;
33 | dstPath = "";
34 | dstSubfolderSpec = 10;
35 | files = (
36 | );
37 | name = "Embed Frameworks";
38 | runOnlyForDeploymentPostprocessing = 0;
39 | };
40 | /* End PBXCopyFilesBuildPhase section */
41 |
42 | /* Begin PBXFileReference section */
43 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
44 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
45 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
46 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
47 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
48 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
49 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
50 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
51 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
52 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
53 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
54 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
55 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
56 | 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
57 | 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
58 | /* End PBXFileReference section */
59 |
60 | /* Begin PBXFrameworksBuildPhase section */
61 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
62 | isa = PBXFrameworksBuildPhase;
63 | buildActionMask = 2147483647;
64 | files = (
65 | );
66 | runOnlyForDeploymentPostprocessing = 0;
67 | };
68 | /* End PBXFrameworksBuildPhase section */
69 |
70 | /* Begin PBXGroup section */
71 | 9740EEB11CF90186004384FC /* Flutter */ = {
72 | isa = PBXGroup;
73 | children = (
74 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
75 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
76 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
77 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
78 | );
79 | name = Flutter;
80 | sourceTree = "";
81 | };
82 | 331C8082294A63A400263BE5 /* RunnerTests */ = {
83 | isa = PBXGroup;
84 | children = (
85 | 331C807B294A618700263BE5 /* RunnerTests.swift */,
86 | );
87 | path = RunnerTests;
88 | sourceTree = "";
89 | };
90 | 97C146E51CF9000F007C117D = {
91 | isa = PBXGroup;
92 | children = (
93 | 9740EEB11CF90186004384FC /* Flutter */,
94 | 97C146F01CF9000F007C117D /* Runner */,
95 | 97C146EF1CF9000F007C117D /* Products */,
96 | 331C8082294A63A400263BE5 /* RunnerTests */,
97 | );
98 | sourceTree = "";
99 | };
100 | 97C146EF1CF9000F007C117D /* Products */ = {
101 | isa = PBXGroup;
102 | children = (
103 | 97C146EE1CF9000F007C117D /* Runner.app */,
104 | 331C8081294A63A400263BE5 /* RunnerTests.xctest */,
105 | );
106 | name = Products;
107 | sourceTree = "";
108 | };
109 | 97C146F01CF9000F007C117D /* Runner */ = {
110 | isa = PBXGroup;
111 | children = (
112 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
113 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
114 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
115 | 97C147021CF9000F007C117D /* Info.plist */,
116 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
117 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
118 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
119 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
120 | );
121 | path = Runner;
122 | sourceTree = "";
123 | };
124 | /* End PBXGroup section */
125 |
126 | /* Begin PBXNativeTarget section */
127 | 331C8080294A63A400263BE5 /* RunnerTests */ = {
128 | isa = PBXNativeTarget;
129 | buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
130 | buildPhases = (
131 | 331C807D294A63A400263BE5 /* Sources */,
132 | 331C807E294A63A400263BE5 /* Frameworks */,
133 | 331C807F294A63A400263BE5 /* Resources */,
134 | );
135 | buildRules = (
136 | );
137 | dependencies = (
138 | 331C8086294A63A400263BE5 /* PBXTargetDependency */,
139 | );
140 | name = RunnerTests;
141 | productName = RunnerTests;
142 | productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
143 | productType = "com.apple.product-type.bundle.unit-test";
144 | };
145 | 97C146ED1CF9000F007C117D /* Runner */ = {
146 | isa = PBXNativeTarget;
147 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
148 | buildPhases = (
149 | 9740EEB61CF901F6004384FC /* Run Script */,
150 | 97C146EA1CF9000F007C117D /* Sources */,
151 | 97C146EB1CF9000F007C117D /* Frameworks */,
152 | 97C146EC1CF9000F007C117D /* Resources */,
153 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
154 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
155 | );
156 | buildRules = (
157 | );
158 | dependencies = (
159 | );
160 | name = Runner;
161 | productName = Runner;
162 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
163 | productType = "com.apple.product-type.application";
164 | };
165 | /* End PBXNativeTarget section */
166 |
167 | /* Begin PBXProject section */
168 | 97C146E61CF9000F007C117D /* Project object */ = {
169 | isa = PBXProject;
170 | attributes = {
171 | LastUpgradeCheck = 1300;
172 | ORGANIZATIONNAME = "";
173 | TargetAttributes = {
174 | 331C8080294A63A400263BE5 = {
175 | CreatedOnToolsVersion = 14.0;
176 | TestTargetID = 97C146ED1CF9000F007C117D;
177 | };
178 | 97C146ED1CF9000F007C117D = {
179 | CreatedOnToolsVersion = 7.3.1;
180 | LastSwiftMigration = 1100;
181 | };
182 | };
183 | };
184 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
185 | compatibilityVersion = "Xcode 9.3";
186 | developmentRegion = en;
187 | hasScannedForEncodings = 0;
188 | knownRegions = (
189 | en,
190 | Base,
191 | );
192 | mainGroup = 97C146E51CF9000F007C117D;
193 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
194 | projectDirPath = "";
195 | projectRoot = "";
196 | targets = (
197 | 97C146ED1CF9000F007C117D /* Runner */,
198 | 331C8080294A63A400263BE5 /* RunnerTests */,
199 | );
200 | };
201 | /* End PBXProject section */
202 |
203 | /* Begin PBXResourcesBuildPhase section */
204 | 331C807F294A63A400263BE5 /* Resources */ = {
205 | isa = PBXResourcesBuildPhase;
206 | buildActionMask = 2147483647;
207 | files = (
208 | );
209 | runOnlyForDeploymentPostprocessing = 0;
210 | };
211 | 97C146EC1CF9000F007C117D /* Resources */ = {
212 | isa = PBXResourcesBuildPhase;
213 | buildActionMask = 2147483647;
214 | files = (
215 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
216 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
217 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
218 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
219 | );
220 | runOnlyForDeploymentPostprocessing = 0;
221 | };
222 | /* End PBXResourcesBuildPhase section */
223 |
224 | /* Begin PBXShellScriptBuildPhase section */
225 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
226 | isa = PBXShellScriptBuildPhase;
227 | alwaysOutOfDate = 1;
228 | buildActionMask = 2147483647;
229 | files = (
230 | );
231 | inputPaths = (
232 | "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
233 | );
234 | name = "Thin Binary";
235 | outputPaths = (
236 | );
237 | runOnlyForDeploymentPostprocessing = 0;
238 | shellPath = /bin/sh;
239 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
240 | };
241 | 9740EEB61CF901F6004384FC /* Run Script */ = {
242 | isa = PBXShellScriptBuildPhase;
243 | alwaysOutOfDate = 1;
244 | buildActionMask = 2147483647;
245 | files = (
246 | );
247 | inputPaths = (
248 | );
249 | name = "Run Script";
250 | outputPaths = (
251 | );
252 | runOnlyForDeploymentPostprocessing = 0;
253 | shellPath = /bin/sh;
254 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
255 | };
256 | /* End PBXShellScriptBuildPhase section */
257 |
258 | /* Begin PBXSourcesBuildPhase section */
259 | 331C807D294A63A400263BE5 /* Sources */ = {
260 | isa = PBXSourcesBuildPhase;
261 | buildActionMask = 2147483647;
262 | files = (
263 | 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
264 | );
265 | runOnlyForDeploymentPostprocessing = 0;
266 | };
267 | 97C146EA1CF9000F007C117D /* Sources */ = {
268 | isa = PBXSourcesBuildPhase;
269 | buildActionMask = 2147483647;
270 | files = (
271 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
272 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
273 | );
274 | runOnlyForDeploymentPostprocessing = 0;
275 | };
276 | /* End PBXSourcesBuildPhase section */
277 |
278 | /* Begin PBXTargetDependency section */
279 | 331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
280 | isa = PBXTargetDependency;
281 | target = 97C146ED1CF9000F007C117D /* Runner */;
282 | targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
283 | };
284 | /* End PBXTargetDependency section */
285 |
286 | /* Begin PBXVariantGroup section */
287 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
288 | isa = PBXVariantGroup;
289 | children = (
290 | 97C146FB1CF9000F007C117D /* Base */,
291 | );
292 | name = Main.storyboard;
293 | sourceTree = "";
294 | };
295 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
296 | isa = PBXVariantGroup;
297 | children = (
298 | 97C147001CF9000F007C117D /* Base */,
299 | );
300 | name = LaunchScreen.storyboard;
301 | sourceTree = "";
302 | };
303 | /* End PBXVariantGroup section */
304 |
305 | /* Begin XCBuildConfiguration section */
306 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
307 | isa = XCBuildConfiguration;
308 | buildSettings = {
309 | ALWAYS_SEARCH_USER_PATHS = NO;
310 | CLANG_ANALYZER_NONNULL = YES;
311 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
312 | CLANG_CXX_LIBRARY = "libc++";
313 | CLANG_ENABLE_MODULES = YES;
314 | CLANG_ENABLE_OBJC_ARC = YES;
315 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
316 | CLANG_WARN_BOOL_CONVERSION = YES;
317 | CLANG_WARN_COMMA = YES;
318 | CLANG_WARN_CONSTANT_CONVERSION = YES;
319 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
320 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
321 | CLANG_WARN_EMPTY_BODY = YES;
322 | CLANG_WARN_ENUM_CONVERSION = YES;
323 | CLANG_WARN_INFINITE_RECURSION = YES;
324 | CLANG_WARN_INT_CONVERSION = YES;
325 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
326 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
327 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
328 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
329 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
330 | CLANG_WARN_STRICT_PROTOTYPES = YES;
331 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
332 | CLANG_WARN_UNREACHABLE_CODE = YES;
333 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
334 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
335 | COPY_PHASE_STRIP = NO;
336 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
337 | ENABLE_NS_ASSERTIONS = NO;
338 | ENABLE_STRICT_OBJC_MSGSEND = YES;
339 | GCC_C_LANGUAGE_STANDARD = gnu99;
340 | GCC_NO_COMMON_BLOCKS = YES;
341 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
342 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
343 | GCC_WARN_UNDECLARED_SELECTOR = YES;
344 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
345 | GCC_WARN_UNUSED_FUNCTION = YES;
346 | GCC_WARN_UNUSED_VARIABLE = YES;
347 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
348 | MTL_ENABLE_DEBUG_INFO = NO;
349 | SDKROOT = iphoneos;
350 | SUPPORTED_PLATFORMS = iphoneos;
351 | TARGETED_DEVICE_FAMILY = "1,2";
352 | VALIDATE_PRODUCT = YES;
353 | };
354 | name = Profile;
355 | };
356 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
357 | isa = XCBuildConfiguration;
358 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
359 | buildSettings = {
360 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
361 | CLANG_ENABLE_MODULES = YES;
362 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
363 | ENABLE_BITCODE = NO;
364 | INFOPLIST_FILE = Runner/Info.plist;
365 | LD_RUNPATH_SEARCH_PATHS = (
366 | "$(inherited)",
367 | "@executable_path/Frameworks",
368 | );
369 | PRODUCT_BUNDLE_IDENTIFIER = com.tictactoe.flutterTicTacToe;
370 | PRODUCT_NAME = "$(TARGET_NAME)";
371 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
372 | SWIFT_VERSION = 5.0;
373 | VERSIONING_SYSTEM = "apple-generic";
374 | };
375 | name = Profile;
376 | };
377 | 331C8088294A63A400263BE5 /* Debug */ = {
378 | isa = XCBuildConfiguration;
379 | baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */;
380 | buildSettings = {
381 | BUNDLE_LOADER = "$(TEST_HOST)";
382 | CODE_SIGN_STYLE = Automatic;
383 | CURRENT_PROJECT_VERSION = 1;
384 | GENERATE_INFOPLIST_FILE = YES;
385 | MARKETING_VERSION = 1.0;
386 | PRODUCT_BUNDLE_IDENTIFIER = com.tictactoe.flutterTicTacToe.RunnerTests;
387 | PRODUCT_NAME = "$(TARGET_NAME)";
388 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
389 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
390 | SWIFT_VERSION = 5.0;
391 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
392 | };
393 | name = Debug;
394 | };
395 | 331C8089294A63A400263BE5 /* Release */ = {
396 | isa = XCBuildConfiguration;
397 | baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */;
398 | buildSettings = {
399 | BUNDLE_LOADER = "$(TEST_HOST)";
400 | CODE_SIGN_STYLE = Automatic;
401 | CURRENT_PROJECT_VERSION = 1;
402 | GENERATE_INFOPLIST_FILE = YES;
403 | MARKETING_VERSION = 1.0;
404 | PRODUCT_BUNDLE_IDENTIFIER = com.tictactoe.flutterTicTacToe.RunnerTests;
405 | PRODUCT_NAME = "$(TARGET_NAME)";
406 | SWIFT_VERSION = 5.0;
407 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
408 | };
409 | name = Release;
410 | };
411 | 331C808A294A63A400263BE5 /* Profile */ = {
412 | isa = XCBuildConfiguration;
413 | baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */;
414 | buildSettings = {
415 | BUNDLE_LOADER = "$(TEST_HOST)";
416 | CODE_SIGN_STYLE = Automatic;
417 | CURRENT_PROJECT_VERSION = 1;
418 | GENERATE_INFOPLIST_FILE = YES;
419 | MARKETING_VERSION = 1.0;
420 | PRODUCT_BUNDLE_IDENTIFIER = com.tictactoe.flutterTicTacToe.RunnerTests;
421 | PRODUCT_NAME = "$(TARGET_NAME)";
422 | SWIFT_VERSION = 5.0;
423 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
424 | };
425 | name = Profile;
426 | };
427 | 97C147031CF9000F007C117D /* Debug */ = {
428 | isa = XCBuildConfiguration;
429 | buildSettings = {
430 | ALWAYS_SEARCH_USER_PATHS = NO;
431 | CLANG_ANALYZER_NONNULL = YES;
432 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
433 | CLANG_CXX_LIBRARY = "libc++";
434 | CLANG_ENABLE_MODULES = YES;
435 | CLANG_ENABLE_OBJC_ARC = YES;
436 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
437 | CLANG_WARN_BOOL_CONVERSION = YES;
438 | CLANG_WARN_COMMA = YES;
439 | CLANG_WARN_CONSTANT_CONVERSION = YES;
440 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
441 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
442 | CLANG_WARN_EMPTY_BODY = YES;
443 | CLANG_WARN_ENUM_CONVERSION = YES;
444 | CLANG_WARN_INFINITE_RECURSION = YES;
445 | CLANG_WARN_INT_CONVERSION = YES;
446 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
447 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
448 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
449 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
450 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
451 | CLANG_WARN_STRICT_PROTOTYPES = YES;
452 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
453 | CLANG_WARN_UNREACHABLE_CODE = YES;
454 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
455 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
456 | COPY_PHASE_STRIP = NO;
457 | DEBUG_INFORMATION_FORMAT = dwarf;
458 | ENABLE_STRICT_OBJC_MSGSEND = YES;
459 | ENABLE_TESTABILITY = YES;
460 | GCC_C_LANGUAGE_STANDARD = gnu99;
461 | GCC_DYNAMIC_NO_PIC = NO;
462 | GCC_NO_COMMON_BLOCKS = YES;
463 | GCC_OPTIMIZATION_LEVEL = 0;
464 | GCC_PREPROCESSOR_DEFINITIONS = (
465 | "DEBUG=1",
466 | "$(inherited)",
467 | );
468 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
469 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
470 | GCC_WARN_UNDECLARED_SELECTOR = YES;
471 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
472 | GCC_WARN_UNUSED_FUNCTION = YES;
473 | GCC_WARN_UNUSED_VARIABLE = YES;
474 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
475 | MTL_ENABLE_DEBUG_INFO = YES;
476 | ONLY_ACTIVE_ARCH = YES;
477 | SDKROOT = iphoneos;
478 | TARGETED_DEVICE_FAMILY = "1,2";
479 | };
480 | name = Debug;
481 | };
482 | 97C147041CF9000F007C117D /* Release */ = {
483 | isa = XCBuildConfiguration;
484 | buildSettings = {
485 | ALWAYS_SEARCH_USER_PATHS = NO;
486 | CLANG_ANALYZER_NONNULL = YES;
487 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
488 | CLANG_CXX_LIBRARY = "libc++";
489 | CLANG_ENABLE_MODULES = YES;
490 | CLANG_ENABLE_OBJC_ARC = YES;
491 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
492 | CLANG_WARN_BOOL_CONVERSION = YES;
493 | CLANG_WARN_COMMA = YES;
494 | CLANG_WARN_CONSTANT_CONVERSION = YES;
495 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
496 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
497 | CLANG_WARN_EMPTY_BODY = YES;
498 | CLANG_WARN_ENUM_CONVERSION = YES;
499 | CLANG_WARN_INFINITE_RECURSION = YES;
500 | CLANG_WARN_INT_CONVERSION = YES;
501 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
502 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
503 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
504 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
505 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
506 | CLANG_WARN_STRICT_PROTOTYPES = YES;
507 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
508 | CLANG_WARN_UNREACHABLE_CODE = YES;
509 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
510 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
511 | COPY_PHASE_STRIP = NO;
512 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
513 | ENABLE_NS_ASSERTIONS = NO;
514 | ENABLE_STRICT_OBJC_MSGSEND = YES;
515 | GCC_C_LANGUAGE_STANDARD = gnu99;
516 | GCC_NO_COMMON_BLOCKS = YES;
517 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
518 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
519 | GCC_WARN_UNDECLARED_SELECTOR = YES;
520 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
521 | GCC_WARN_UNUSED_FUNCTION = YES;
522 | GCC_WARN_UNUSED_VARIABLE = YES;
523 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
524 | MTL_ENABLE_DEBUG_INFO = NO;
525 | SDKROOT = iphoneos;
526 | SUPPORTED_PLATFORMS = iphoneos;
527 | SWIFT_COMPILATION_MODE = wholemodule;
528 | SWIFT_OPTIMIZATION_LEVEL = "-O";
529 | TARGETED_DEVICE_FAMILY = "1,2";
530 | VALIDATE_PRODUCT = YES;
531 | };
532 | name = Release;
533 | };
534 | 97C147061CF9000F007C117D /* Debug */ = {
535 | isa = XCBuildConfiguration;
536 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
537 | buildSettings = {
538 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
539 | CLANG_ENABLE_MODULES = YES;
540 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
541 | ENABLE_BITCODE = NO;
542 | INFOPLIST_FILE = Runner/Info.plist;
543 | LD_RUNPATH_SEARCH_PATHS = (
544 | "$(inherited)",
545 | "@executable_path/Frameworks",
546 | );
547 | PRODUCT_BUNDLE_IDENTIFIER = com.tictactoe.flutterTicTacToe;
548 | PRODUCT_NAME = "$(TARGET_NAME)";
549 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
550 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
551 | SWIFT_VERSION = 5.0;
552 | VERSIONING_SYSTEM = "apple-generic";
553 | };
554 | name = Debug;
555 | };
556 | 97C147071CF9000F007C117D /* Release */ = {
557 | isa = XCBuildConfiguration;
558 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
559 | buildSettings = {
560 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
561 | CLANG_ENABLE_MODULES = YES;
562 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
563 | ENABLE_BITCODE = NO;
564 | INFOPLIST_FILE = Runner/Info.plist;
565 | LD_RUNPATH_SEARCH_PATHS = (
566 | "$(inherited)",
567 | "@executable_path/Frameworks",
568 | );
569 | PRODUCT_BUNDLE_IDENTIFIER = com.tictactoe.flutterTicTacToe;
570 | PRODUCT_NAME = "$(TARGET_NAME)";
571 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
572 | SWIFT_VERSION = 5.0;
573 | VERSIONING_SYSTEM = "apple-generic";
574 | };
575 | name = Release;
576 | };
577 | /* End XCBuildConfiguration section */
578 |
579 | /* Begin XCConfigurationList section */
580 | 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
581 | isa = XCConfigurationList;
582 | buildConfigurations = (
583 | 331C8088294A63A400263BE5 /* Debug */,
584 | 331C8089294A63A400263BE5 /* Release */,
585 | 331C808A294A63A400263BE5 /* Profile */,
586 | );
587 | defaultConfigurationIsVisible = 0;
588 | defaultConfigurationName = Release;
589 | };
590 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
591 | isa = XCConfigurationList;
592 | buildConfigurations = (
593 | 97C147031CF9000F007C117D /* Debug */,
594 | 97C147041CF9000F007C117D /* Release */,
595 | 249021D3217E4FDB00AE95B9 /* Profile */,
596 | );
597 | defaultConfigurationIsVisible = 0;
598 | defaultConfigurationName = Release;
599 | };
600 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
601 | isa = XCConfigurationList;
602 | buildConfigurations = (
603 | 97C147061CF9000F007C117D /* Debug */,
604 | 97C147071CF9000F007C117D /* Release */,
605 | 249021D4217E4FDB00AE95B9 /* Profile */,
606 | );
607 | defaultConfigurationIsVisible = 0;
608 | defaultConfigurationName = Release;
609 | };
610 | /* End XCConfigurationList section */
611 | };
612 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
613 | }
614 |
--------------------------------------------------------------------------------