├── .fvmrc
├── .github
├── dependabot.yml
└── workflows
│ ├── ci.yaml
│ └── ci_spm.yaml
├── .gitignore
├── .idea
├── .gitignore
├── libraries
│ ├── Dart_SDK.xml
│ └── Flutter_Plugins.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
├── runConfigurations
│ └── example_lib_main_dart.xml
└── vcs.xml
├── .metadata
├── .swift-format
├── .vscode
├── c_cpp_properties.json
├── launch.json
└── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── README_ZHTW.md
├── analysis_options.yaml
├── close.gif
├── example
├── .gitignore
├── .metadata
├── .vscode
│ ├── c_cpp_properties.json
│ └── settings.json
├── README.md
├── analysis_options.yaml
├── lib
│ └── main.dart
├── linux
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── flutter
│ │ ├── CMakeLists.txt
│ │ ├── generated_plugin_registrant.cc
│ │ ├── generated_plugin_registrant.h
│ │ └── generated_plugins.cmake
│ ├── main.cc
│ ├── my_application.cc
│ └── my_application.h
├── macos
│ ├── .gitignore
│ ├── Flutter
│ │ ├── Flutter-Debug.xcconfig
│ │ ├── Flutter-Release.xcconfig
│ │ └── GeneratedPluginRegistrant.swift
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ └── xcshareddata
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── Runner
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ └── AppIcon.appiconset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── app_icon_1024.png
│ │ │ │ ├── app_icon_128.png
│ │ │ │ ├── app_icon_16.png
│ │ │ │ ├── app_icon_256.png
│ │ │ │ ├── app_icon_32.png
│ │ │ │ ├── app_icon_512.png
│ │ │ │ └── app_icon_64.png
│ │ ├── Base.lproj
│ │ │ └── MainMenu.xib
│ │ ├── Configs
│ │ │ ├── AppInfo.xcconfig
│ │ │ ├── Debug.xcconfig
│ │ │ ├── Release.xcconfig
│ │ │ └── Warnings.xcconfig
│ │ ├── DebugProfile.entitlements
│ │ ├── Info.plist
│ │ ├── MainFlutterWindow.swift
│ │ └── Release.entitlements
│ └── RunnerTests
│ │ └── RunnerTests.swift
├── pubspec.lock
├── pubspec.yaml
├── test
│ └── widget_test.dart
├── web
│ ├── favicon.png
│ ├── icons
│ │ ├── Icon-192.png
│ │ ├── Icon-512.png
│ │ ├── Icon-maskable-192.png
│ │ └── Icon-maskable-512.png
│ ├── index.html
│ └── manifest.json
└── windows
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── flutter
│ ├── CMakeLists.txt
│ ├── generated_plugin_registrant.cc
│ ├── generated_plugin_registrant.h
│ └── generated_plugins.cmake
│ └── runner
│ ├── CMakeLists.txt
│ ├── Runner.rc
│ ├── flutter_window.cpp
│ ├── flutter_window.h
│ ├── main.cpp
│ ├── resource.h
│ ├── resources
│ └── app_icon.ico
│ ├── runner.exe.manifest
│ ├── utils.cpp
│ ├── utils.h
│ ├── win32_window.cpp
│ └── win32_window.h
├── flutter_window_close.iml
├── lib
├── flutter_window_close.dart
└── flutter_window_close_web.dart
├── linux
├── CMakeLists.txt
├── flutter_window_close_plugin.cc
└── include
│ └── flutter_window_close
│ └── flutter_window_close_plugin.h
├── macos
├── flutter_window_close.podspec
└── flutter_window_close
│ ├── Package.swift
│ └── Sources
│ └── flutter_window_close
│ ├── FlutterWindowClosePlugin.swift
│ └── Resources
│ └── .gitkeep
├── pubspec.lock
├── pubspec.yaml
├── test
└── flutter_window_close_test.dart
└── windows
├── .clang-format
├── .gitignore
├── CMakeLists.txt
├── flutter_window_close_plugin.cpp
└── include
└── flutter_window_close
└── flutter_window_close_plugin.h
/.fvmrc:
--------------------------------------------------------------------------------
1 | {
2 | "flutter": "3.24.0"
3 | }
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "pub" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | runs-on: ${{ matrix.os }}
8 | strategy:
9 | matrix:
10 | os: [macOS-latest, windows-2019, ubuntu-latest]
11 | version: ["3.10.x", "3.19.x", "3.22.x", "3.24.x"]
12 | include:
13 | - os: macOS-latest
14 | TARGET: macos
15 | - os: ubuntu-latest
16 | TARGET: linux
17 | - os: windows-2019
18 | TARGET: windows
19 | # Disable fail-fast; we want results from all OSes even if one fails.
20 | fail-fast: false
21 |
22 | steps:
23 | - uses: actions/checkout@v4
24 | - uses: subosito/flutter-action@v2
25 | with:
26 | flutter-version: ${{ matrix.version }}
27 | channel: "stable" # optional, default to: 'stable'
28 | - name: Install Linux dependencies
29 | if: startsWith(matrix.os, 'ubuntu')
30 | run: |
31 | sudo apt-get update
32 | sudo apt-get install -y libgtk-3-dev libx11-dev pkg-config cmake ninja-build libblkid-dev liblzma-dev
33 | - name: Enable desktop support
34 | run: |
35 | flutter config --enable-linux-desktop
36 | flutter config --enable-macos-desktop
37 | flutter config --enable-windows-desktop
38 | - name: Doctor
39 | # Run doctor, for ease of debugging any issues.
40 | run: flutter doctor -v
41 | - name: Install dependencies
42 | run: flutter packages get
43 | working-directory: example
44 | - name: Build macOS
45 | if: startsWith(matrix.os, 'macOS')
46 | run: flutter build macos
47 | working-directory: example
48 | - name: Build Linux
49 | if: startsWith(matrix.os, 'ubuntu')
50 | run: flutter build linux
51 | working-directory: example
52 | - name: Build Windows
53 | if: startsWith(matrix.os, 'windows')
54 | run: flutter build windows
55 | working-directory: example
56 |
--------------------------------------------------------------------------------
/.github/workflows/ci_spm.yaml:
--------------------------------------------------------------------------------
1 | name: SPM
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | runs-on: macOS-latest
8 | steps:
9 | - uses: actions/checkout@v4
10 | - uses: actions/setup-java@v4
11 | with:
12 | distribution: "oracle"
13 | java-version: "17"
14 | - uses: subosito/flutter-action@v2
15 | with:
16 | # flutter-version: 3.26.x
17 | channel: "main" # optional, default to: 'stable'
18 | - name: Enable SPM
19 | run: |
20 | flutter config --enable-swift-package-manager
21 | - name: Doctor
22 | # Run doctor, for ease of debugging any issues.
23 | run: flutter doctor -v
24 | - name: Install dependencies
25 | run: flutter packages get
26 | working-directory: example
27 | - name: Cleans up macOS project
28 | run: |
29 | pod deintegrate
30 | echo "#include \"Generated.xcconfig\"" > Flutter/Debug.xcconfig
31 | cat Flutter/Debug.xcconfig
32 | echo "#include \"Generated.xcconfig\"" > Flutter/Release.xcconfig
33 | cat Flutter/Release.xcconfig
34 | working-directory: example/macos
35 | - name: Build macOS
36 | run: flutter build macos
37 | working-directory: example
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | build/
8 |
9 | # FVM Version Cache
10 | .fvm/
11 | .build/
12 | .swiftpm/
13 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/libraries/Dart_SDK.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/.idea/libraries/Flutter_Plugins.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/example_lib_main_dart.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: "f83b08495a2f5c97dfe0b81ffeefc350f1d478be"
8 | channel: "main"
9 |
10 | project_type: plugin
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: f83b08495a2f5c97dfe0b81ffeefc350f1d478be
17 | base_revision: f83b08495a2f5c97dfe0b81ffeefc350f1d478be
18 | - platform: linux
19 | create_revision: f83b08495a2f5c97dfe0b81ffeefc350f1d478be
20 | base_revision: f83b08495a2f5c97dfe0b81ffeefc350f1d478be
21 | - platform: macos
22 | create_revision: f83b08495a2f5c97dfe0b81ffeefc350f1d478be
23 | base_revision: f83b08495a2f5c97dfe0b81ffeefc350f1d478be
24 | - platform: web
25 | create_revision: f83b08495a2f5c97dfe0b81ffeefc350f1d478be
26 | base_revision: f83b08495a2f5c97dfe0b81ffeefc350f1d478be
27 | - platform: windows
28 | create_revision: f83b08495a2f5c97dfe0b81ffeefc350f1d478be
29 | base_revision: f83b08495a2f5c97dfe0b81ffeefc350f1d478be
30 |
31 | # User provided section
32 |
33 | # List of Local paths (relative to this file) that should be
34 | # ignored by the migrate tool.
35 | #
36 | # Files that are not part of the templates will be ignored by default.
37 | unmanaged_files:
38 | - 'lib/main.dart'
39 | - 'ios/Runner.xcodeproj/project.pbxproj'
40 |
--------------------------------------------------------------------------------
/.swift-format:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "lineLength": 100,
4 | "indentation": {
5 | "spaces": 4
6 | },
7 | "maximumBlankLines": 1,
8 | "respectsExistingLineBreaks": true,
9 | "lineBreakBeforeControlFlowKeywords": false,
10 | "lineBreakBeforeEachArgument": false
11 | }
12 |
--------------------------------------------------------------------------------
/.vscode/c_cpp_properties.json:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": [{
3 | "name": "Linux",
4 | "includePath": [
5 | "${workspaceFolder}/**"
6 | ],
7 | "defines": [],
8 | "compilerPath": "/usr/bin/clang",
9 | "cStandard": "c17",
10 | "cppStandard": "c++14",
11 | "intelliSenseMode": "linux-clang-x64",
12 | "compileCommands": "${workspaceFolder}/build/compile_commands.json",
13 | "configurationProvider": "ms-vscode.cmake-tools"
14 | }
15 | ],
16 | "version": 4
17 | }
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Launch Flutter",
6 | "cwd": "example",
7 | "request": "launch",
8 | "type": "dart"
9 | },
10 | {
11 | "name": "Debug native",
12 | "type": "cppdbg",
13 | "request": "launch",
14 | "program": "${workspaceFolder}/example/build/linux/x64/debug/bundle/myapp_example",
15 | "cwd": "${workspaceFolder}"
16 | }
17 | ]
18 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cmake.sourceDirectory": "${workspaceFolder}/example/linux",
3 | "cmake.configureOnOpen": true,
4 | "files.associations": {
5 | "cstring": "cpp"
6 | },
7 | "dart.flutterSdkPath": ".fvm/versions/3.24.0"
8 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.2.0
2 |
3 | - SPM ready.
4 |
5 | ## 1.1.0
6 |
7 | - Modify the way to set the active window on macOS.
8 |
9 | ## 1.0.0
10 |
11 | - Prevents closing the window only after the plugin is initialized.
12 |
13 | ## 0.3.0
14 |
15 | - Fix the Linux plugin on Flutter 3.10 and above.
16 |
17 | ## 0.2.2
18 |
19 | - Allow setting the web return value to null. Thanks to [doppio](https://github.com/doppio)
20 |
21 | ## 0.2.1
22 |
23 | - Fix the issue that the window won't be closed when sending the message to close window after a delay on Windows.
24 |
25 | ## 0.2.0
26 |
27 | - Support Flutter Web
28 |
29 | ## 0.1.1
30 |
31 | - Update documentation
32 |
33 | ## 0.1.0
34 |
35 | - Initial release
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Weizhong Yang a.k.a zonble
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # flutter_window_close
2 |
3 | 2021 © Weizhong Yang a.k.a zonble
4 |
5 | [](https://pub.dartlang.org/packages/flutter_window_close) [](https://github.com/zonble/flutter_window_close/actions) [](https://github.com/zonble/flutter_window_close/blob/main/LICENSE)
6 |
7 | flutter_window_close lets your Flutter app has a chance to confirm if the user
8 | wants to close your app. It works on desktop platforms including Windows, macOS
9 | and Linux.
10 |
11 | 
12 | 
13 | 
14 |
15 | 
16 |
17 | ## Getting Started
18 |
19 | It is very common that an app would prompt a message like "Do you really want to
20 | quit" when users click on the close button, in order to notify that there are
21 | still undone tasks and the users may lose their data if they want to quit
22 | anyway. It prevents the users from losing data unwillingly.
23 |
24 | To let a Flutter desktop app to support that, the plug-in listens to the events
25 | from the window hosting Flutter's view, and send the events to Flutter. What you
26 | need to do is to assign an anonymous function that can answer if the window
27 | should be closed. For example, you can show an alert dialog to ask what the
28 | current user is willing to do:
29 |
30 | ```dart
31 | FlutterWindowClose.setWindowShouldCloseHandler(() async {
32 | return await showDialog(
33 | context: context,
34 | builder: (context) {
35 | return AlertDialog(
36 | title: const Text('Do you really want to quit?'),
37 | actions: [
38 | ElevatedButton(
39 | onPressed: () => Navigator.of(context).pop(true),
40 | child: const Text('Yes')),
41 | ElevatedButton(
42 | onPressed: () => Navigator.of(context).pop(false),
43 | child: const Text('No')),
44 | ]);
45 | });
46 | });
47 | ```
48 |
49 | The plugin bridges following APIs:
50 |
51 | - Windows:
52 | [WM_CLOSE](https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-close)
53 | message in WinProc
54 | - macOS:
55 | [windowShouldClose(\_:)](https://developer.apple.com/documentation/appkit/nswindowdelegate/1419380-windowshouldclose)
56 | in
57 | [NSWindowDelegate](https://developer.apple.com/documentation/appkit/nswindowdelegate)
58 | - Linux:
59 | [Widget::delete-event](https://docs.gtk.org/gtk3/signal.Widget.delete-event.html)
60 | signal
61 |
62 | It does not support mobile platforms, since there is no such event for closing
63 | windows. You can use a custom
64 | [WillPopScope](https://api.flutter.dev/flutter/widgets/WillPopScope-class.html)
65 | to capture if a user is leaving your app with a back key.
66 |
67 | ## macOS
68 |
69 | There could be some issues while using the package on macOS. Each platform has
70 | its paradigm and the developer framework macOS sees window objects in a
71 | different way from Windows and Linux.
72 |
73 | On Windows and Linux, windows are more like controllers in MVC pattern , and
74 | when it comes to Flutter, there would be always a root window in the process of
75 | an app, and our plugin could easily know which is the window to listen to. In
76 | the code level, we use
77 | [GetActiveWindow](https://docs.microsoft.com/windows/win32/api/winuser/nf-winuser-getactivewindow)
78 | while we can use
79 | [gtk_widget_get_ancestor](https://people.gnome.org/~shaunm/girdoc/C/Gtk.Widget.get_ancestor.html)
80 | or
81 | [gtk_widget_get_toplevel](https://people.gnome.org/~shaunm/girdoc/C/Gtk.Widget.get_toplevel.html).
82 |
83 | On the contrary, windows are more like views on macOS. An app can have multiple
84 | windows, and the app can stay still open event all windows are closed. We can
85 | also create an object with multiple IBOutlets to multiple windows. Flutter macOS
86 | does not tell plugins which window is the one running Flutter as well.
87 |
88 | The plugin listens to the first window in the
89 | [windows](https://developer.apple.com/documentation/appkit/nsapplication/1428402-windows)
90 | list of the singleton
91 | [NSApplication](https://developer.apple.com/documentation/appkit/nsapplication)
92 | object. It works if you have only one window in your macOS Flutter app. If you
93 | just create a new app using the official template for macOS, you may need not to
94 | change anything. However, if your app has multiple windows, the behavior of the
95 | plugin might be unexpectable.
96 |
97 | ## Flutter Web
98 |
99 | We can do little when a user is closing the tab or window hosting your Flutter
100 | Web app. The only way to let a user to confirm if he or she really want to close
101 | is to set the return value of the
102 | [onbeforeunload](https://developer.mozilla.org/docs/Web/API/WindowEventHandlers/onbeforeunload)
103 | event. You can use the `setWebReturnValue` method to set the return value.
104 |
105 | ## License
106 |
107 | The package is released under MIT license.
108 |
--------------------------------------------------------------------------------
/README_ZHTW.md:
--------------------------------------------------------------------------------
1 | # flutter_window_close
2 |
3 | 2021 © Weizhong Yang a.k.a zonble
4 |
5 | [](https://pub.dartlang.org/packages/flutter_window_close) [](https://github.com/zonble/flutter_window_close/actions) [](https://github.com/zonble/flutter_window_close/blob/main/LICENSE)
6 |
7 | flutter_window_close 元件可以讓用戶在關閉您所開發的 Flutter 桌面應用程式的視窗
8 | 時,詢問用戶是否確定要關閉。這個元件可以用在 Windows、macOS 以及 Linux 等桌面平
9 | 台。
10 |
11 | 
12 | 
13 | 
14 |
15 | 
16 |
17 | ## 使用說明
18 |
19 | 在開發桌面應用程式的時候,我們經常會在用戶按下視窗上的關閉按鈕時,跳出「您是否確
20 | 定要關閉」這樣的對話框,避免用戶只是因為不小心誤觸,而打斷用戶原本的操作,或是造
21 | 成用戶的資料遺失。這個套件可以讓您在使用 Flutter 開發桌面應用程式時,實現上述功
22 | 能。
23 |
24 | 這個套件會在各種桌面平台上,監聽視窗關閉的事件,然後將攔截到的事件送到 Flutter 內
25 | 部處理。在使用這個套件時,您只需要在 Flutter 應用程式中,設置一個用來處理視窗關
26 | 閉事件的函式,然後用一個 Future 回傳是否確實要關閉的 Boolean 值。比方說,你可以
27 | 回傳 Flutter 當中的 showAlert 的結果,範例如下:
28 |
29 | ```dart
30 | FlutterWindowClose.setWindowShouldCloseHandler(() async {
31 | return await showDialog(
32 | context: context,
33 | builder: (context) {
34 | return AlertDialog(
35 | title: const Text('Do you really want to quit?'),
36 | actions: [
37 | ElevatedButton(
38 | onPressed: () => Navigator.of(context).pop(true),
39 | child: const Text('Yes')),
40 | ElevatedButton(
41 | onPressed: () => Navigator.of(context).pop(false),
42 | child: const Text('No')),
43 | ]);
44 | });
45 | });
46 | ```
47 |
48 | 這個元件介接了以下 API:
49 |
50 | - Windows:我們攔截了
51 | [WM_CLOSE](https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-close)訊息
52 | - macOS:我們攔截了
53 | [NSWindowDelegate](https://developer.apple.com/documentation/appkit/nswindowdelegate)
54 | 的
55 | [windowShouldClose(\_:)](https://developer.apple.com/documentation/appkit/nswindowdelegate/1419380-windowshouldclose)
56 | 這個 delegate method
57 | - Linux:
58 | [Widget::delete-event](https://docs.gtk.org/gtk3/signal.Widget.delete-event.html)
59 | signal
60 |
61 | 這個套件不支援行動平台以及 Flutter Web。
62 |
63 | ## macOS
64 |
65 | 在 macOS 上,視窗在整個開發框架的定義以及開發觀念,與 Windows/Linux 上不太一樣,
66 | 所以,在 macOS 上,使用這個套件時,也需要特別留意一下下面的說明。
67 |
68 | 在 Windows 與 Linux 平台上,視窗比較像是 MVC 當中的 Controller 的角色,在每個視
69 | 窗當中發生的事件,往往會往視窗上面送,而且視窗中的元件也可以比較明確知道上層的
70 | Window 是什麼。在 Windows 上,我們可以用
71 | [GetActiveWindow](https://docs.microsoft.com/zh-tw/windows/win32/api/winuser/nf-winuser-getactivewindow)
72 | 拿到所屬視窗,在 Linux 上,則可以用
73 | [gtk_widget_get_ancestor](https://people.gnome.org/~shaunm/girdoc/C/Gtk.Widget.get_ancestor.html)
74 | 或是
75 | [gtk_widget_get_toplevel](https://people.gnome.org/~shaunm/girdoc/C/Gtk.Widget.get_toplevel.html),
76 | 但是在 macOS 則不然。
77 |
78 | 在 macOS 上,視窗比較像是 View 的角色,每個 App 中可以有多個視窗,每個視窗也不見
79 | 得都一定會有專屬的 Window Controller,任何一個物件只要建立了一個 IBOutlet,就可
80 | 以連接、控制某個視窗。在 Flutter 框架中,每個 Plug-in 目前也沒辦法正確拿到所屬的
81 | View 以及視窗物件。
82 |
83 | 在目前的實做中,會在 Plugin 啟動時,主動監聽 App 中所有視窗中的第一個,也就是
84 | [NSApplication](https://developer.apple.com/documentation/appkit/nsapplication)
85 | 的 singleton 物件中的
86 | [windows](https://developer.apple.com/documentation/appkit/nsapplication/1428402-windows)
87 | 列表的第一個。如果你在 macOS 上的 app,就只是根據 Flutter 官方範本產生的,大概沒
88 | 有什麼問題,但如果你有多個視窗,而你的第一個視窗又不是 Flutter 所在的視窗的話,
89 | 那你就不會收到視窗關閉的事件了。
90 |
91 | ## Flutter Web
92 |
93 | 如果你的 Flutter App 是在 Web 中運作,那,我們能做的相當有限。要讓用戶在關閉某個
94 | 瀏覽器分頁或是視窗時,跳出提示訊息,只能夠調整
95 | [onbeforeunload](https://developer.mozilla.org/zh-TW/docs/Web/API/WindowEventHandlers/onbeforeunload)
96 | 的回傳字串。你可以透過呼叫 `setWebReturnValue` 設置一個提示訊息。
97 |
98 | ## 授權
99 |
100 | 本專案使用 MIT 授權
101 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:flutter_lints/flutter.yaml
2 |
3 | # Additional information about this file can be found at
4 | # https://dart.dev/guides/language/analysis-options
5 |
--------------------------------------------------------------------------------
/close.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/close.gif
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Android Studio will place build artifacts here
44 | /android/app/debug
45 | /android/app/profile
46 | /android/app/release
47 |
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: "80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819"
8 | channel: "stable"
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
17 | base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
18 | - platform: android
19 | create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
20 | base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
21 | - platform: ios
22 | create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
23 | base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
24 | - platform: linux
25 | create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
26 | base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
27 | - platform: macos
28 | create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
29 | base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
30 | - platform: web
31 | create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
32 | base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
33 | - platform: windows
34 | create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
35 | base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
36 |
37 | # User provided section
38 |
39 | # List of Local paths (relative to this file) that should be
40 | # ignored by the migrate tool.
41 | #
42 | # Files that are not part of the templates will be ignored by default.
43 | unmanaged_files:
44 | - 'lib/main.dart'
45 | - 'ios/Runner.xcodeproj/project.pbxproj'
46 |
--------------------------------------------------------------------------------
/example/.vscode/c_cpp_properties.json:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": [
3 | {
4 | "name": "Linux",
5 | "includePath": [
6 | "${workspaceFolder}/**"
7 | ],
8 | "defines": [],
9 | "compilerPath": "/usr/bin/clang",
10 | "cStandard": "c17",
11 | "cppStandard": "c++14",
12 | "intelliSenseMode": "linux-clang-x64",
13 | "compileCommands": "/home/zonble/Works/flutter_window_close/build/compile_commands.json"
14 | }
15 | ],
16 | "version": 4
17 | }
--------------------------------------------------------------------------------
/example/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cmake.sourceDirectory": "${workspaceFolder}/linux"
3 | }
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # flutter_window_close_example
2 |
3 | Demonstrates how to use the flutter_window_close plugin.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/example/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/foundation.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_platform_alert/flutter_platform_alert.dart';
6 | import 'package:flutter_window_close/flutter_window_close.dart';
7 |
8 | void main() {
9 | runApp(const MyApp());
10 | }
11 |
12 | class MyApp extends StatefulWidget {
13 | const MyApp({Key? key}) : super(key: key);
14 |
15 | @override
16 | State createState() => _MyAppState();
17 | }
18 |
19 | class _MyAppState extends State {
20 | @override
21 | Widget build(BuildContext context) {
22 | return const MaterialApp(home: MainPage());
23 | }
24 | }
25 |
26 | class MainPage extends StatefulWidget {
27 | const MainPage({Key? key}) : super(key: key);
28 |
29 | @override
30 | State createState() => _MainPageState();
31 | }
32 |
33 | class _MainPageState extends State {
34 | var _alertShowing = false;
35 | var _index = 0;
36 |
37 | @override
38 | void initState() {
39 | super.initState();
40 |
41 | if (kIsWeb) {
42 | FlutterWindowClose.setWebReturnValue('Are you sure?');
43 | return;
44 | }
45 |
46 | FlutterWindowClose.setWindowShouldCloseHandler(() async {
47 | if (_index == 0) {
48 | if (_alertShowing) return false;
49 | _alertShowing = true;
50 |
51 | return await showDialog(
52 | context: context,
53 | builder: (context) {
54 | return AlertDialog(
55 | title: const Text('Do you really want to quit?'),
56 | actions: [
57 | ElevatedButton(
58 | onPressed: () {
59 | Navigator.of(context).pop(true);
60 | _alertShowing = false;
61 | },
62 | child: const Text('Yes')),
63 | ElevatedButton(
64 | onPressed: () {
65 | Navigator.of(context).pop(false);
66 | _alertShowing = false;
67 | },
68 | child: const Text('No'))
69 | ]);
70 | });
71 | } else if (_index == 1) {
72 | final result = await FlutterPlatformAlert.showCustomAlert(
73 | windowTitle: "Really?",
74 | text: "Do you really want to quit?",
75 | positiveButtonTitle: "Quit",
76 | negativeButtonTitle: "Cancel",
77 | );
78 | return result == CustomButton.positiveButton;
79 | } else if (_index == 3) {
80 | return await Future.delayed(const Duration(seconds: 1), () => true);
81 | }
82 | return true;
83 | });
84 | }
85 |
86 | @override
87 | Widget build(BuildContext context) {
88 | if (kIsWeb) {
89 | return Scaffold(
90 | appBar: AppBar(title: const Text('flutter_window_close')),
91 | body: const Center(child: Text('Please try to close the tab/window.')),
92 | );
93 | }
94 |
95 | final platformMenus = [
96 | PlatformMenu(label: 'Flutter Window Close Example', menus: [
97 | PlatformMenuItem(
98 | label: 'Quit Flutter Window Close Example',
99 | onSelected: () => FlutterWindowClose.closeWindow())
100 | ]),
101 | PlatformMenu(label: 'Help', menus: [
102 | PlatformMenuItem(
103 | label: 'About',
104 | onSelected: () => FlutterPlatformAlert.showCustomAlert(
105 | windowTitle: 'About',
106 | text:
107 | 'flutter_window_close\n\nhttps://pub.dev/packages/flutter_window_close',
108 | ))
109 | ]),
110 | ];
111 |
112 | final menu = MenuBar(
113 | style: MenuStyle(
114 | elevation: MaterialStateProperty.resolveWith((states) => 0),
115 | backgroundColor:
116 | MaterialStateColor.resolveWith((states) => Colors.white),
117 | ),
118 | children: [
119 | SubmenuButton(
120 | menuChildren: [
121 | MenuItemButton(
122 | child: const Text('Exit'),
123 | onPressed: () => FlutterWindowClose.closeWindow()),
124 | ],
125 | child: const Text('File'),
126 | ),
127 | SubmenuButton(
128 | menuChildren: [
129 | MenuItemButton(
130 | child: const Text('About'),
131 | onPressed: () {
132 | FlutterPlatformAlert.showCustomAlert(
133 | windowTitle: 'About',
134 | text:
135 | 'flutter_window_close\n\nhttps://pub.dev/packages/flutter_window_close',
136 | );
137 | }),
138 | ],
139 | child: const Text('Help'),
140 | ),
141 | ]);
142 |
143 | var inner = Scaffold(
144 | body: Column(
145 | crossAxisAlignment: CrossAxisAlignment.start,
146 | children: [
147 | if (Platform.isLinux || Platform.isWindows) menu,
148 | Expanded(
149 | child: Scaffold(
150 | appBar: AppBar(title: const Text('flutter_window_close')),
151 | body: Center(
152 | child: Column(
153 | mainAxisAlignment: MainAxisAlignment.center,
154 | children: [
155 | ListTile(
156 | leading: Radio(
157 | groupValue: _index,
158 | value: 0,
159 | onChanged: (int? value) =>
160 | setState(() => _index = value ?? 0),
161 | ),
162 | title: const Text('Confirm Closing Using Flutter'),
163 | ),
164 | ListTile(
165 | leading: Radio(
166 | groupValue: _index,
167 | value: 1,
168 | onChanged: (int? value) =>
169 | setState(() => _index = value ?? 1),
170 | ),
171 | title:
172 | const Text('Confirm Closing Using Native Alert Dialog'),
173 | ),
174 | ListTile(
175 | leading: Radio(
176 | groupValue: _index,
177 | value: 2,
178 | onChanged: (int? value) =>
179 | setState(() => _index = value ?? 2),
180 | ),
181 | title: const Text('No Confirm'),
182 | ),
183 | ListTile(
184 | leading: Radio(
185 | groupValue: _index,
186 | value: 3,
187 | onChanged: (int? value) =>
188 | setState(() => _index = value ?? 2),
189 | ),
190 | title: const Text('No Confirm with Delay'),
191 | ),
192 | const SizedBox(height: 30),
193 | ElevatedButton(
194 | onPressed: () => FlutterWindowClose.closeWindow(),
195 | child: const Text('Close Window')),
196 | ],
197 | )),
198 | ),
199 | ),
200 | ],
201 | ),
202 | );
203 |
204 | if (Platform.isMacOS) {
205 | return PlatformMenuBar(
206 | menus: platformMenus,
207 | child: inner,
208 | );
209 | }
210 |
211 | return inner;
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/example/linux/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/ephemeral
2 |
--------------------------------------------------------------------------------
/example/linux/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10)
2 | project(runner LANGUAGES CXX)
3 |
4 | set(BINARY_NAME "flutter_window_close_example")
5 | set(APPLICATION_ID "com.example.flutter_window_close")
6 |
7 | cmake_policy(SET CMP0063 NEW)
8 |
9 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
10 |
11 | # Root filesystem for cross-building.
12 | if(FLUTTER_TARGET_PLATFORM_SYSROOT)
13 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
14 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
16 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
17 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
18 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
19 | endif()
20 |
21 | # Configure build options.
22 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
23 | set(CMAKE_BUILD_TYPE "Debug" CACHE
24 | STRING "Flutter build mode" FORCE)
25 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
26 | "Debug" "Profile" "Release")
27 | endif()
28 |
29 | # Compilation settings that should be applied to most targets.
30 | function(APPLY_STANDARD_SETTINGS TARGET)
31 | target_compile_features(${TARGET} PUBLIC cxx_std_14)
32 | target_compile_options(${TARGET} PRIVATE -Wall -Werror)
33 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
34 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
35 | endfunction()
36 |
37 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
38 |
39 | # Flutter library and tool build rules.
40 | add_subdirectory(${FLUTTER_MANAGED_DIR})
41 |
42 | # System-level dependencies.
43 | find_package(PkgConfig REQUIRED)
44 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
45 |
46 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
47 |
48 | # Application build
49 | add_executable(${BINARY_NAME}
50 | "main.cc"
51 | "my_application.cc"
52 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
53 | )
54 | apply_standard_settings(${BINARY_NAME})
55 | target_link_libraries(${BINARY_NAME} PRIVATE flutter)
56 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
57 | add_dependencies(${BINARY_NAME} flutter_assemble)
58 | # Only the install-generated bundle's copy of the executable will launch
59 | # correctly, since the resources must in the right relative locations. To avoid
60 | # people trying to run the unbundled copy, put it in a subdirectory instead of
61 | # the default top-level location.
62 | set_target_properties(${BINARY_NAME}
63 | PROPERTIES
64 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
65 | )
66 |
67 | # Generated plugin build rules, which manage building the plugins and adding
68 | # them to the application.
69 | include(flutter/generated_plugins.cmake)
70 |
71 |
72 | # === Installation ===
73 | # By default, "installing" just makes a relocatable bundle in the build
74 | # directory.
75 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
76 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
77 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
78 | endif()
79 |
80 | # Start with a clean build bundle directory every time.
81 | install(CODE "
82 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
83 | " COMPONENT Runtime)
84 |
85 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
86 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
87 |
88 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
89 | COMPONENT Runtime)
90 |
91 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
92 | COMPONENT Runtime)
93 |
94 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
95 | COMPONENT Runtime)
96 |
97 | if(PLUGIN_BUNDLED_LIBRARIES)
98 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
99 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
100 | COMPONENT Runtime)
101 | endif()
102 |
103 | # Fully re-copy the assets directory on each build to avoid having stale files
104 | # from a previous install.
105 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
106 | install(CODE "
107 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
108 | " COMPONENT Runtime)
109 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
110 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
111 |
112 | # Install the AOT library on non-Debug builds only.
113 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
114 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
115 | COMPONENT Runtime)
116 | endif()
117 |
--------------------------------------------------------------------------------
/example/linux/flutter/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10)
2 |
3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
4 |
5 | # Configuration provided via flutter tool.
6 | include(${EPHEMERAL_DIR}/generated_config.cmake)
7 |
8 | # TODO: Move the rest of this into files in ephemeral. See
9 | # https://github.com/flutter/flutter/issues/57146.
10 |
11 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...),
12 | # which isn't available in 3.10.
13 | function(list_prepend LIST_NAME PREFIX)
14 | set(NEW_LIST "")
15 | foreach(element ${${LIST_NAME}})
16 | list(APPEND NEW_LIST "${PREFIX}${element}")
17 | endforeach(element)
18 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
19 | endfunction()
20 |
21 | # === Flutter Library ===
22 | # System-level dependencies.
23 | find_package(PkgConfig REQUIRED)
24 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
25 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
26 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
27 |
28 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
29 |
30 | # Published to parent scope for install step.
31 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
32 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
33 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
34 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
35 |
36 | list(APPEND FLUTTER_LIBRARY_HEADERS
37 | "fl_basic_message_channel.h"
38 | "fl_binary_codec.h"
39 | "fl_binary_messenger.h"
40 | "fl_dart_project.h"
41 | "fl_engine.h"
42 | "fl_json_message_codec.h"
43 | "fl_json_method_codec.h"
44 | "fl_message_codec.h"
45 | "fl_method_call.h"
46 | "fl_method_channel.h"
47 | "fl_method_codec.h"
48 | "fl_method_response.h"
49 | "fl_plugin_registrar.h"
50 | "fl_plugin_registry.h"
51 | "fl_standard_message_codec.h"
52 | "fl_standard_method_codec.h"
53 | "fl_string_codec.h"
54 | "fl_value.h"
55 | "fl_view.h"
56 | "flutter_linux.h"
57 | )
58 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
59 | add_library(flutter INTERFACE)
60 | target_include_directories(flutter INTERFACE
61 | "${EPHEMERAL_DIR}"
62 | )
63 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
64 | target_link_libraries(flutter INTERFACE
65 | PkgConfig::GTK
66 | PkgConfig::GLIB
67 | PkgConfig::GIO
68 | )
69 | add_dependencies(flutter flutter_assemble)
70 |
71 | # === Flutter tool backend ===
72 | # _phony_ is a non-existent file to force this command to run every time,
73 | # since currently there's no way to get a full input/output list from the
74 | # flutter tool.
75 | add_custom_command(
76 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
77 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_
78 | COMMAND ${CMAKE_COMMAND} -E env
79 | ${FLUTTER_TOOL_ENVIRONMENT}
80 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
81 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
82 | VERBATIM
83 | )
84 | add_custom_target(flutter_assemble DEPENDS
85 | "${FLUTTER_LIBRARY}"
86 | ${FLUTTER_LIBRARY_HEADERS}
87 | )
88 |
--------------------------------------------------------------------------------
/example/linux/flutter/generated_plugin_registrant.cc:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #include "generated_plugin_registrant.h"
8 |
9 | #include
10 | #include
11 |
12 | void fl_register_plugins(FlPluginRegistry* registry) {
13 | g_autoptr(FlPluginRegistrar) flutter_platform_alert_registrar =
14 | fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterPlatformAlertPlugin");
15 | flutter_platform_alert_plugin_register_with_registrar(flutter_platform_alert_registrar);
16 | g_autoptr(FlPluginRegistrar) flutter_window_close_registrar =
17 | fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterWindowClosePlugin");
18 | flutter_window_close_plugin_register_with_registrar(flutter_window_close_registrar);
19 | }
20 |
--------------------------------------------------------------------------------
/example/linux/flutter/generated_plugin_registrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #ifndef GENERATED_PLUGIN_REGISTRANT_
8 | #define GENERATED_PLUGIN_REGISTRANT_
9 |
10 | #include
11 |
12 | // Registers Flutter plugins.
13 | void fl_register_plugins(FlPluginRegistry* registry);
14 |
15 | #endif // GENERATED_PLUGIN_REGISTRANT_
16 |
--------------------------------------------------------------------------------
/example/linux/flutter/generated_plugins.cmake:
--------------------------------------------------------------------------------
1 | #
2 | # Generated file, do not edit.
3 | #
4 |
5 | list(APPEND FLUTTER_PLUGIN_LIST
6 | flutter_platform_alert
7 | flutter_window_close
8 | )
9 |
10 | list(APPEND FLUTTER_FFI_PLUGIN_LIST
11 | )
12 |
13 | set(PLUGIN_BUNDLED_LIBRARIES)
14 |
15 | foreach(plugin ${FLUTTER_PLUGIN_LIST})
16 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
17 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
20 | endforeach(plugin)
21 |
22 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
23 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
24 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
25 | endforeach(ffi_plugin)
26 |
--------------------------------------------------------------------------------
/example/linux/main.cc:
--------------------------------------------------------------------------------
1 | #include "my_application.h"
2 |
3 | int main(int argc, char** argv) {
4 | g_autoptr(MyApplication) app = my_application_new();
5 | return g_application_run(G_APPLICATION(app), argc, argv);
6 | }
7 |
--------------------------------------------------------------------------------
/example/linux/my_application.cc:
--------------------------------------------------------------------------------
1 | #include "my_application.h"
2 |
3 | #include
4 | #ifdef GDK_WINDOWING_X11
5 | #include
6 | #endif
7 |
8 | #include "flutter/generated_plugin_registrant.h"
9 |
10 | struct _MyApplication {
11 | GtkApplication parent_instance;
12 | char** dart_entrypoint_arguments;
13 | };
14 |
15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
16 |
17 | // Implements GApplication::activate.
18 | static void my_application_activate(GApplication* application)
19 | {
20 | MyApplication* self = MY_APPLICATION(application);
21 | GtkWindow* window = GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
22 |
23 | // Use a header bar when running in GNOME as this is the common style used
24 | // by applications and is the setup most users will be using (e.g. Ubuntu
25 | // desktop).
26 | // If running on X and not using GNOME then just use a traditional title bar
27 | // in case the window manager does more exotic layout, e.g. tiling.
28 | // If running on Wayland assume the header bar will work (may need changing
29 | // if future cases occur).
30 | gboolean use_header_bar = FALSE;
31 | #ifdef GDK_WINDOWING_X11
32 | GdkScreen* screen = gtk_window_get_screen(window);
33 | if (GDK_IS_X11_SCREEN(screen)) {
34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
36 | use_header_bar = FALSE;
37 | }
38 | }
39 | #endif
40 | if (use_header_bar) {
41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
42 | gtk_widget_show(GTK_WIDGET(header_bar));
43 | gtk_header_bar_set_title(header_bar, "flutter_window_close_example");
44 | gtk_header_bar_set_show_close_button(header_bar, TRUE);
45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
46 | } else {
47 | gtk_window_set_title(window, "flutter_window_close_example");
48 | }
49 |
50 | gtk_window_set_default_size(window, 1280, 720);
51 | gtk_widget_show(GTK_WIDGET(window));
52 |
53 | g_autoptr(FlDartProject) project = fl_dart_project_new();
54 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
55 |
56 | FlView* view = fl_view_new(project);
57 | gtk_widget_show(GTK_WIDGET(view));
58 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
59 |
60 | fl_register_plugins(FL_PLUGIN_REGISTRY(view));
61 |
62 | gtk_widget_grab_focus(GTK_WIDGET(view));
63 | }
64 |
65 | // Implements GApplication::local_command_line.
66 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status)
67 | {
68 | MyApplication* self = MY_APPLICATION(application);
69 | // Strip out the first argument as it is the binary name.
70 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
71 |
72 | g_autoptr(GError) error = nullptr;
73 | if (!g_application_register(application, nullptr, &error)) {
74 | g_warning("Failed to register: %s", error->message);
75 | *exit_status = 1;
76 | return TRUE;
77 | }
78 |
79 | g_application_activate(application);
80 | *exit_status = 0;
81 |
82 | return TRUE;
83 | }
84 |
85 | // Implements GObject::dispose.
86 | static void my_application_dispose(GObject* object)
87 | {
88 | MyApplication* self = MY_APPLICATION(object);
89 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
90 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
91 | }
92 |
93 | static void my_application_class_init(MyApplicationClass* klass)
94 | {
95 | G_APPLICATION_CLASS(klass)->activate = my_application_activate;
96 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
97 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
98 | }
99 |
100 | static void my_application_init(MyApplication* self) { }
101 |
102 | MyApplication* my_application_new()
103 | {
104 | return MY_APPLICATION(g_object_new(my_application_get_type(),
105 | "application-id", APPLICATION_ID,
106 | "flags", G_APPLICATION_NON_UNIQUE,
107 | nullptr));
108 | }
109 |
--------------------------------------------------------------------------------
/example/linux/my_application.h:
--------------------------------------------------------------------------------
1 | #ifndef FLUTTER_MY_APPLICATION_H_
2 | #define FLUTTER_MY_APPLICATION_H_
3 |
4 | #include
5 |
6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
7 | GtkApplication)
8 |
9 | /**
10 | * my_application_new:
11 | *
12 | * Creates a new Flutter-based application.
13 | *
14 | * Returns: a new #MyApplication.
15 | */
16 | MyApplication* my_application_new();
17 |
18 | #endif // FLUTTER_MY_APPLICATION_H_
19 |
--------------------------------------------------------------------------------
/example/macos/.gitignore:
--------------------------------------------------------------------------------
1 | # Flutter-related
2 | **/Flutter/ephemeral/
3 | **/Pods/
4 |
5 | # Xcode-related
6 | **/dgph
7 | **/xcuserdata/
8 |
--------------------------------------------------------------------------------
/example/macos/Flutter/Flutter-Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "ephemeral/Flutter-Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/macos/Flutter/Flutter-Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "ephemeral/Flutter-Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/macos/Flutter/GeneratedPluginRegistrant.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | import FlutterMacOS
6 | import Foundation
7 |
8 | import flutter_platform_alert
9 | import flutter_window_close
10 |
11 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
12 | FlutterPlatformAlertPlugin.register(with: registry.registrar(forPlugin: "FlutterPlatformAlertPlugin"))
13 | FlutterWindowClosePlugin.register(with: registry.registrar(forPlugin: "FlutterWindowClosePlugin"))
14 | }
15 |
--------------------------------------------------------------------------------
/example/macos/Podfile:
--------------------------------------------------------------------------------
1 | platform :osx, '10.14'
2 |
3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
5 |
6 | project 'Runner', {
7 | 'Debug' => :debug,
8 | 'Profile' => :release,
9 | 'Release' => :release,
10 | }
11 |
12 | def flutter_root
13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
14 | unless File.exist?(generated_xcode_build_settings_path)
15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
16 | end
17 |
18 | File.foreach(generated_xcode_build_settings_path) do |line|
19 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
20 | return matches[1].strip if matches
21 | end
22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
23 | end
24 |
25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
26 |
27 | flutter_macos_podfile_setup
28 |
29 | target 'Runner' do
30 | use_frameworks!
31 | use_modular_headers!
32 |
33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
34 | target 'RunnerTests' do
35 | inherit! :search_paths
36 | end
37 | end
38 |
39 | post_install do |installer|
40 | installer.pods_project.targets.each do |target|
41 | flutter_additional_macos_build_settings(target)
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/example/macos/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - flutter_platform_alert (0.0.1):
3 | - FlutterMacOS
4 | - flutter_window_close (0.0.1):
5 | - FlutterMacOS
6 | - FlutterMacOS (1.0.0)
7 |
8 | DEPENDENCIES:
9 | - flutter_platform_alert (from `Flutter/ephemeral/.symlinks/plugins/flutter_platform_alert/macos`)
10 | - flutter_window_close (from `Flutter/ephemeral/.symlinks/plugins/flutter_window_close/macos`)
11 | - FlutterMacOS (from `Flutter/ephemeral`)
12 |
13 | EXTERNAL SOURCES:
14 | flutter_platform_alert:
15 | :path: Flutter/ephemeral/.symlinks/plugins/flutter_platform_alert/macos
16 | flutter_window_close:
17 | :path: Flutter/ephemeral/.symlinks/plugins/flutter_window_close/macos
18 | FlutterMacOS:
19 | :path: Flutter/ephemeral
20 |
21 | SPEC CHECKSUMS:
22 | flutter_platform_alert: 0373c95e18cdeda110eddcc3cb78fdd64f59dce0
23 | flutter_window_close: bd408414cbbf0d39f0d3076c4da0cdbf1c527168
24 | FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
25 |
26 | PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
27 |
28 | COCOAPODS: 1.16.2
29 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 54;
7 | objects = {
8 |
9 | /* Begin PBXAggregateTarget section */
10 | 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
11 | isa = PBXAggregateTarget;
12 | buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
13 | buildPhases = (
14 | 33CC111E2044C6BF0003C045 /* ShellScript */,
15 | );
16 | dependencies = (
17 | );
18 | name = "Flutter Assemble";
19 | productName = FLX;
20 | };
21 | /* End PBXAggregateTarget section */
22 |
23 | /* Begin PBXBuildFile section */
24 | 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
25 | 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
26 | 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
27 | 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
28 | 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
29 | 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
30 | 4C1D26F2605A25E8ED11FEB2 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62DF2BE92490DF74160148CA /* Pods_RunnerTests.framework */; };
31 | 906427C3774C0331A56736E8 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E379A9463D9CEDC184E6C45 /* Pods_Runner.framework */; };
32 | /* End PBXBuildFile section */
33 |
34 | /* Begin PBXContainerItemProxy section */
35 | 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
36 | isa = PBXContainerItemProxy;
37 | containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
38 | proxyType = 1;
39 | remoteGlobalIDString = 33CC10EC2044A3C60003C045;
40 | remoteInfo = Runner;
41 | };
42 | 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
43 | isa = PBXContainerItemProxy;
44 | containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
45 | proxyType = 1;
46 | remoteGlobalIDString = 33CC111A2044C6BA0003C045;
47 | remoteInfo = FLX;
48 | };
49 | /* End PBXContainerItemProxy section */
50 |
51 | /* Begin PBXCopyFilesBuildPhase section */
52 | 33CC110E2044A8840003C045 /* Bundle Framework */ = {
53 | isa = PBXCopyFilesBuildPhase;
54 | buildActionMask = 2147483647;
55 | dstPath = "";
56 | dstSubfolderSpec = 10;
57 | files = (
58 | );
59 | name = "Bundle Framework";
60 | runOnlyForDeploymentPostprocessing = 0;
61 | };
62 | /* End PBXCopyFilesBuildPhase section */
63 |
64 | /* Begin PBXFileReference section */
65 | 1E379A9463D9CEDC184E6C45 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
66 | 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
67 | 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
68 | 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; };
69 | 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; };
70 | 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; };
71 | 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
72 | 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; };
73 | 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
74 | 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; };
75 | 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; };
76 | 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; };
77 | 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; };
78 | 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; };
79 | 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; };
80 | 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; };
81 | 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; };
82 | 44CA3DB5F2A74E0D3E9261D8 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
83 | 46CC52690C05A4543A2E6866 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
84 | 62DF2BE92490DF74160148CA /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
85 | 6F04307FAA48958CEBC3CEB9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; };
86 | 734E0E604A08CD0278DFA6CF /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
87 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; };
88 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; };
89 | DDBA8768CD788F2207C26A24 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
90 | E60C5EA8B2D72FECCC151536 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
91 | /* End PBXFileReference section */
92 |
93 | /* Begin PBXFrameworksBuildPhase section */
94 | 331C80D2294CF70F00263BE5 /* Frameworks */ = {
95 | isa = PBXFrameworksBuildPhase;
96 | buildActionMask = 2147483647;
97 | files = (
98 | 4C1D26F2605A25E8ED11FEB2 /* Pods_RunnerTests.framework in Frameworks */,
99 | );
100 | runOnlyForDeploymentPostprocessing = 0;
101 | };
102 | 33CC10EA2044A3C60003C045 /* Frameworks */ = {
103 | isa = PBXFrameworksBuildPhase;
104 | buildActionMask = 2147483647;
105 | files = (
106 | 906427C3774C0331A56736E8 /* Pods_Runner.framework in Frameworks */,
107 | );
108 | runOnlyForDeploymentPostprocessing = 0;
109 | };
110 | /* End PBXFrameworksBuildPhase section */
111 |
112 | /* Begin PBXGroup section */
113 | 331C80D6294CF71000263BE5 /* RunnerTests */ = {
114 | isa = PBXGroup;
115 | children = (
116 | 331C80D7294CF71000263BE5 /* RunnerTests.swift */,
117 | );
118 | path = RunnerTests;
119 | sourceTree = "";
120 | };
121 | 33BA886A226E78AF003329D5 /* Configs */ = {
122 | isa = PBXGroup;
123 | children = (
124 | 33E5194F232828860026EE4D /* AppInfo.xcconfig */,
125 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
126 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
127 | 333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
128 | );
129 | path = Configs;
130 | sourceTree = "";
131 | };
132 | 33CC10E42044A3C60003C045 = {
133 | isa = PBXGroup;
134 | children = (
135 | 33FAB671232836740065AC1E /* Runner */,
136 | 33CEB47122A05771004F2AC0 /* Flutter */,
137 | 331C80D6294CF71000263BE5 /* RunnerTests */,
138 | 33CC10EE2044A3C60003C045 /* Products */,
139 | D73912EC22F37F3D000D13A0 /* Frameworks */,
140 | A1B25D3C13CC1A4B110A0ED4 /* Pods */,
141 | );
142 | sourceTree = "";
143 | };
144 | 33CC10EE2044A3C60003C045 /* Products */ = {
145 | isa = PBXGroup;
146 | children = (
147 | 33CC10ED2044A3C60003C045 /* example.app */,
148 | 331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
149 | );
150 | name = Products;
151 | sourceTree = "";
152 | };
153 | 33CC11242044D66E0003C045 /* Resources */ = {
154 | isa = PBXGroup;
155 | children = (
156 | 33CC10F22044A3C60003C045 /* Assets.xcassets */,
157 | 33CC10F42044A3C60003C045 /* MainMenu.xib */,
158 | 33CC10F72044A3C60003C045 /* Info.plist */,
159 | );
160 | name = Resources;
161 | path = ..;
162 | sourceTree = "";
163 | };
164 | 33CEB47122A05771004F2AC0 /* Flutter */ = {
165 | isa = PBXGroup;
166 | children = (
167 | 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
168 | 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
169 | 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
170 | 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
171 | );
172 | path = Flutter;
173 | sourceTree = "";
174 | };
175 | 33FAB671232836740065AC1E /* Runner */ = {
176 | isa = PBXGroup;
177 | children = (
178 | 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
179 | 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
180 | 33E51913231747F40026EE4D /* DebugProfile.entitlements */,
181 | 33E51914231749380026EE4D /* Release.entitlements */,
182 | 33CC11242044D66E0003C045 /* Resources */,
183 | 33BA886A226E78AF003329D5 /* Configs */,
184 | );
185 | path = Runner;
186 | sourceTree = "";
187 | };
188 | A1B25D3C13CC1A4B110A0ED4 /* Pods */ = {
189 | isa = PBXGroup;
190 | children = (
191 | 44CA3DB5F2A74E0D3E9261D8 /* Pods-Runner.debug.xcconfig */,
192 | DDBA8768CD788F2207C26A24 /* Pods-Runner.release.xcconfig */,
193 | E60C5EA8B2D72FECCC151536 /* Pods-Runner.profile.xcconfig */,
194 | 6F04307FAA48958CEBC3CEB9 /* Pods-RunnerTests.debug.xcconfig */,
195 | 46CC52690C05A4543A2E6866 /* Pods-RunnerTests.release.xcconfig */,
196 | 734E0E604A08CD0278DFA6CF /* Pods-RunnerTests.profile.xcconfig */,
197 | );
198 | name = Pods;
199 | path = Pods;
200 | sourceTree = "";
201 | };
202 | D73912EC22F37F3D000D13A0 /* Frameworks */ = {
203 | isa = PBXGroup;
204 | children = (
205 | 1E379A9463D9CEDC184E6C45 /* Pods_Runner.framework */,
206 | 62DF2BE92490DF74160148CA /* Pods_RunnerTests.framework */,
207 | );
208 | name = Frameworks;
209 | sourceTree = "";
210 | };
211 | /* End PBXGroup section */
212 |
213 | /* Begin PBXNativeTarget section */
214 | 331C80D4294CF70F00263BE5 /* RunnerTests */ = {
215 | isa = PBXNativeTarget;
216 | buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
217 | buildPhases = (
218 | 6DA52BE4AC52804517130F8D /* [CP] Check Pods Manifest.lock */,
219 | 331C80D1294CF70F00263BE5 /* Sources */,
220 | 331C80D2294CF70F00263BE5 /* Frameworks */,
221 | 331C80D3294CF70F00263BE5 /* Resources */,
222 | );
223 | buildRules = (
224 | );
225 | dependencies = (
226 | 331C80DA294CF71000263BE5 /* PBXTargetDependency */,
227 | );
228 | name = RunnerTests;
229 | productName = RunnerTests;
230 | productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
231 | productType = "com.apple.product-type.bundle.unit-test";
232 | };
233 | 33CC10EC2044A3C60003C045 /* Runner */ = {
234 | isa = PBXNativeTarget;
235 | buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
236 | buildPhases = (
237 | 279D181F2C20510F4216139C /* [CP] Check Pods Manifest.lock */,
238 | 33CC10E92044A3C60003C045 /* Sources */,
239 | 33CC10EA2044A3C60003C045 /* Frameworks */,
240 | 33CC10EB2044A3C60003C045 /* Resources */,
241 | 33CC110E2044A8840003C045 /* Bundle Framework */,
242 | 3399D490228B24CF009A79C7 /* ShellScript */,
243 | 18D2BE9D7DD6FAAF1CD00FD4 /* [CP] Embed Pods Frameworks */,
244 | );
245 | buildRules = (
246 | );
247 | dependencies = (
248 | 33CC11202044C79F0003C045 /* PBXTargetDependency */,
249 | );
250 | name = Runner;
251 | productName = Runner;
252 | productReference = 33CC10ED2044A3C60003C045 /* example.app */;
253 | productType = "com.apple.product-type.application";
254 | };
255 | /* End PBXNativeTarget section */
256 |
257 | /* Begin PBXProject section */
258 | 33CC10E52044A3C60003C045 /* Project object */ = {
259 | isa = PBXProject;
260 | attributes = {
261 | BuildIndependentTargetsInParallel = YES;
262 | LastSwiftUpdateCheck = 0920;
263 | LastUpgradeCheck = 1510;
264 | ORGANIZATIONNAME = "";
265 | TargetAttributes = {
266 | 331C80D4294CF70F00263BE5 = {
267 | CreatedOnToolsVersion = 14.0;
268 | TestTargetID = 33CC10EC2044A3C60003C045;
269 | };
270 | 33CC10EC2044A3C60003C045 = {
271 | CreatedOnToolsVersion = 9.2;
272 | LastSwiftMigration = 1100;
273 | ProvisioningStyle = Automatic;
274 | SystemCapabilities = {
275 | com.apple.Sandbox = {
276 | enabled = 1;
277 | };
278 | };
279 | };
280 | 33CC111A2044C6BA0003C045 = {
281 | CreatedOnToolsVersion = 9.2;
282 | ProvisioningStyle = Manual;
283 | };
284 | };
285 | };
286 | buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
287 | compatibilityVersion = "Xcode 9.3";
288 | developmentRegion = en;
289 | hasScannedForEncodings = 0;
290 | knownRegions = (
291 | en,
292 | Base,
293 | );
294 | mainGroup = 33CC10E42044A3C60003C045;
295 | productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
296 | projectDirPath = "";
297 | projectRoot = "";
298 | targets = (
299 | 33CC10EC2044A3C60003C045 /* Runner */,
300 | 331C80D4294CF70F00263BE5 /* RunnerTests */,
301 | 33CC111A2044C6BA0003C045 /* Flutter Assemble */,
302 | );
303 | };
304 | /* End PBXProject section */
305 |
306 | /* Begin PBXResourcesBuildPhase section */
307 | 331C80D3294CF70F00263BE5 /* Resources */ = {
308 | isa = PBXResourcesBuildPhase;
309 | buildActionMask = 2147483647;
310 | files = (
311 | );
312 | runOnlyForDeploymentPostprocessing = 0;
313 | };
314 | 33CC10EB2044A3C60003C045 /* Resources */ = {
315 | isa = PBXResourcesBuildPhase;
316 | buildActionMask = 2147483647;
317 | files = (
318 | 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
319 | 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
320 | );
321 | runOnlyForDeploymentPostprocessing = 0;
322 | };
323 | /* End PBXResourcesBuildPhase section */
324 |
325 | /* Begin PBXShellScriptBuildPhase section */
326 | 18D2BE9D7DD6FAAF1CD00FD4 /* [CP] Embed Pods Frameworks */ = {
327 | isa = PBXShellScriptBuildPhase;
328 | buildActionMask = 2147483647;
329 | files = (
330 | );
331 | inputFileListPaths = (
332 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
333 | );
334 | name = "[CP] Embed Pods Frameworks";
335 | outputFileListPaths = (
336 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
337 | );
338 | runOnlyForDeploymentPostprocessing = 0;
339 | shellPath = /bin/sh;
340 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
341 | showEnvVarsInLog = 0;
342 | };
343 | 279D181F2C20510F4216139C /* [CP] Check Pods Manifest.lock */ = {
344 | isa = PBXShellScriptBuildPhase;
345 | buildActionMask = 2147483647;
346 | files = (
347 | );
348 | inputFileListPaths = (
349 | );
350 | inputPaths = (
351 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
352 | "${PODS_ROOT}/Manifest.lock",
353 | );
354 | name = "[CP] Check Pods Manifest.lock";
355 | outputFileListPaths = (
356 | );
357 | outputPaths = (
358 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
359 | );
360 | runOnlyForDeploymentPostprocessing = 0;
361 | shellPath = /bin/sh;
362 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
363 | showEnvVarsInLog = 0;
364 | };
365 | 3399D490228B24CF009A79C7 /* ShellScript */ = {
366 | isa = PBXShellScriptBuildPhase;
367 | alwaysOutOfDate = 1;
368 | buildActionMask = 2147483647;
369 | files = (
370 | );
371 | inputFileListPaths = (
372 | );
373 | inputPaths = (
374 | );
375 | outputFileListPaths = (
376 | );
377 | outputPaths = (
378 | );
379 | runOnlyForDeploymentPostprocessing = 0;
380 | shellPath = /bin/sh;
381 | shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
382 | };
383 | 33CC111E2044C6BF0003C045 /* ShellScript */ = {
384 | isa = PBXShellScriptBuildPhase;
385 | buildActionMask = 2147483647;
386 | files = (
387 | );
388 | inputFileListPaths = (
389 | Flutter/ephemeral/FlutterInputs.xcfilelist,
390 | );
391 | inputPaths = (
392 | Flutter/ephemeral/tripwire,
393 | );
394 | outputFileListPaths = (
395 | Flutter/ephemeral/FlutterOutputs.xcfilelist,
396 | );
397 | outputPaths = (
398 | );
399 | runOnlyForDeploymentPostprocessing = 0;
400 | shellPath = /bin/sh;
401 | shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
402 | };
403 | 6DA52BE4AC52804517130F8D /* [CP] Check Pods Manifest.lock */ = {
404 | isa = PBXShellScriptBuildPhase;
405 | buildActionMask = 2147483647;
406 | files = (
407 | );
408 | inputFileListPaths = (
409 | );
410 | inputPaths = (
411 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
412 | "${PODS_ROOT}/Manifest.lock",
413 | );
414 | name = "[CP] Check Pods Manifest.lock";
415 | outputFileListPaths = (
416 | );
417 | outputPaths = (
418 | "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
419 | );
420 | runOnlyForDeploymentPostprocessing = 0;
421 | shellPath = /bin/sh;
422 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
423 | showEnvVarsInLog = 0;
424 | };
425 | /* End PBXShellScriptBuildPhase section */
426 |
427 | /* Begin PBXSourcesBuildPhase section */
428 | 331C80D1294CF70F00263BE5 /* Sources */ = {
429 | isa = PBXSourcesBuildPhase;
430 | buildActionMask = 2147483647;
431 | files = (
432 | 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
433 | );
434 | runOnlyForDeploymentPostprocessing = 0;
435 | };
436 | 33CC10E92044A3C60003C045 /* Sources */ = {
437 | isa = PBXSourcesBuildPhase;
438 | buildActionMask = 2147483647;
439 | files = (
440 | 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
441 | 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
442 | 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
443 | );
444 | runOnlyForDeploymentPostprocessing = 0;
445 | };
446 | /* End PBXSourcesBuildPhase section */
447 |
448 | /* Begin PBXTargetDependency section */
449 | 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
450 | isa = PBXTargetDependency;
451 | target = 33CC10EC2044A3C60003C045 /* Runner */;
452 | targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
453 | };
454 | 33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
455 | isa = PBXTargetDependency;
456 | target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
457 | targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
458 | };
459 | /* End PBXTargetDependency section */
460 |
461 | /* Begin PBXVariantGroup section */
462 | 33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
463 | isa = PBXVariantGroup;
464 | children = (
465 | 33CC10F52044A3C60003C045 /* Base */,
466 | );
467 | name = MainMenu.xib;
468 | path = Runner;
469 | sourceTree = "";
470 | };
471 | /* End PBXVariantGroup section */
472 |
473 | /* Begin XCBuildConfiguration section */
474 | 331C80DB294CF71000263BE5 /* Debug */ = {
475 | isa = XCBuildConfiguration;
476 | baseConfigurationReference = 6F04307FAA48958CEBC3CEB9 /* Pods-RunnerTests.debug.xcconfig */;
477 | buildSettings = {
478 | BUNDLE_LOADER = "$(TEST_HOST)";
479 | CURRENT_PROJECT_VERSION = 1;
480 | GENERATE_INFOPLIST_FILE = YES;
481 | MARKETING_VERSION = 1.0;
482 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
483 | PRODUCT_NAME = "$(TARGET_NAME)";
484 | SWIFT_VERSION = 5.0;
485 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example";
486 | };
487 | name = Debug;
488 | };
489 | 331C80DC294CF71000263BE5 /* Release */ = {
490 | isa = XCBuildConfiguration;
491 | baseConfigurationReference = 46CC52690C05A4543A2E6866 /* Pods-RunnerTests.release.xcconfig */;
492 | buildSettings = {
493 | BUNDLE_LOADER = "$(TEST_HOST)";
494 | CURRENT_PROJECT_VERSION = 1;
495 | GENERATE_INFOPLIST_FILE = YES;
496 | MARKETING_VERSION = 1.0;
497 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
498 | PRODUCT_NAME = "$(TARGET_NAME)";
499 | SWIFT_VERSION = 5.0;
500 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example";
501 | };
502 | name = Release;
503 | };
504 | 331C80DD294CF71000263BE5 /* Profile */ = {
505 | isa = XCBuildConfiguration;
506 | baseConfigurationReference = 734E0E604A08CD0278DFA6CF /* Pods-RunnerTests.profile.xcconfig */;
507 | buildSettings = {
508 | BUNDLE_LOADER = "$(TEST_HOST)";
509 | CURRENT_PROJECT_VERSION = 1;
510 | GENERATE_INFOPLIST_FILE = YES;
511 | MARKETING_VERSION = 1.0;
512 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
513 | PRODUCT_NAME = "$(TARGET_NAME)";
514 | SWIFT_VERSION = 5.0;
515 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example";
516 | };
517 | name = Profile;
518 | };
519 | 338D0CE9231458BD00FA5F75 /* Profile */ = {
520 | isa = XCBuildConfiguration;
521 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
522 | buildSettings = {
523 | ALWAYS_SEARCH_USER_PATHS = NO;
524 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
525 | CLANG_ANALYZER_NONNULL = YES;
526 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
527 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
528 | CLANG_CXX_LIBRARY = "libc++";
529 | CLANG_ENABLE_MODULES = YES;
530 | CLANG_ENABLE_OBJC_ARC = YES;
531 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
532 | CLANG_WARN_BOOL_CONVERSION = YES;
533 | CLANG_WARN_CONSTANT_CONVERSION = YES;
534 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
535 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
536 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
537 | CLANG_WARN_EMPTY_BODY = YES;
538 | CLANG_WARN_ENUM_CONVERSION = YES;
539 | CLANG_WARN_INFINITE_RECURSION = YES;
540 | CLANG_WARN_INT_CONVERSION = YES;
541 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
542 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
543 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
544 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
545 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
546 | CODE_SIGN_IDENTITY = "-";
547 | COPY_PHASE_STRIP = NO;
548 | DEAD_CODE_STRIPPING = YES;
549 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
550 | ENABLE_NS_ASSERTIONS = NO;
551 | ENABLE_STRICT_OBJC_MSGSEND = YES;
552 | ENABLE_USER_SCRIPT_SANDBOXING = NO;
553 | GCC_C_LANGUAGE_STANDARD = gnu11;
554 | GCC_NO_COMMON_BLOCKS = YES;
555 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
556 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
557 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
558 | GCC_WARN_UNUSED_FUNCTION = YES;
559 | GCC_WARN_UNUSED_VARIABLE = YES;
560 | MACOSX_DEPLOYMENT_TARGET = 10.14;
561 | MTL_ENABLE_DEBUG_INFO = NO;
562 | SDKROOT = macosx;
563 | SWIFT_COMPILATION_MODE = wholemodule;
564 | SWIFT_OPTIMIZATION_LEVEL = "-O";
565 | };
566 | name = Profile;
567 | };
568 | 338D0CEA231458BD00FA5F75 /* Profile */ = {
569 | isa = XCBuildConfiguration;
570 | baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
571 | buildSettings = {
572 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
573 | CLANG_ENABLE_MODULES = YES;
574 | CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
575 | CODE_SIGN_STYLE = Automatic;
576 | COMBINE_HIDPI_IMAGES = YES;
577 | INFOPLIST_FILE = Runner/Info.plist;
578 | LD_RUNPATH_SEARCH_PATHS = (
579 | "$(inherited)",
580 | "@executable_path/../Frameworks",
581 | );
582 | PROVISIONING_PROFILE_SPECIFIER = "";
583 | SWIFT_VERSION = 5.0;
584 | };
585 | name = Profile;
586 | };
587 | 338D0CEB231458BD00FA5F75 /* Profile */ = {
588 | isa = XCBuildConfiguration;
589 | buildSettings = {
590 | CODE_SIGN_STYLE = Manual;
591 | PRODUCT_NAME = "$(TARGET_NAME)";
592 | };
593 | name = Profile;
594 | };
595 | 33CC10F92044A3C60003C045 /* Debug */ = {
596 | isa = XCBuildConfiguration;
597 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
598 | buildSettings = {
599 | ALWAYS_SEARCH_USER_PATHS = NO;
600 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
601 | CLANG_ANALYZER_NONNULL = YES;
602 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
603 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
604 | CLANG_CXX_LIBRARY = "libc++";
605 | CLANG_ENABLE_MODULES = YES;
606 | CLANG_ENABLE_OBJC_ARC = YES;
607 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
608 | CLANG_WARN_BOOL_CONVERSION = YES;
609 | CLANG_WARN_CONSTANT_CONVERSION = YES;
610 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
611 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
612 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
613 | CLANG_WARN_EMPTY_BODY = YES;
614 | CLANG_WARN_ENUM_CONVERSION = YES;
615 | CLANG_WARN_INFINITE_RECURSION = YES;
616 | CLANG_WARN_INT_CONVERSION = YES;
617 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
618 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
619 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
620 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
621 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
622 | CODE_SIGN_IDENTITY = "-";
623 | COPY_PHASE_STRIP = NO;
624 | DEAD_CODE_STRIPPING = YES;
625 | DEBUG_INFORMATION_FORMAT = dwarf;
626 | ENABLE_STRICT_OBJC_MSGSEND = YES;
627 | ENABLE_TESTABILITY = YES;
628 | ENABLE_USER_SCRIPT_SANDBOXING = NO;
629 | GCC_C_LANGUAGE_STANDARD = gnu11;
630 | GCC_DYNAMIC_NO_PIC = NO;
631 | GCC_NO_COMMON_BLOCKS = YES;
632 | GCC_OPTIMIZATION_LEVEL = 0;
633 | GCC_PREPROCESSOR_DEFINITIONS = (
634 | "DEBUG=1",
635 | "$(inherited)",
636 | );
637 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
638 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
639 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
640 | GCC_WARN_UNUSED_FUNCTION = YES;
641 | GCC_WARN_UNUSED_VARIABLE = YES;
642 | MACOSX_DEPLOYMENT_TARGET = 10.14;
643 | MTL_ENABLE_DEBUG_INFO = YES;
644 | ONLY_ACTIVE_ARCH = YES;
645 | SDKROOT = macosx;
646 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
647 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
648 | };
649 | name = Debug;
650 | };
651 | 33CC10FA2044A3C60003C045 /* Release */ = {
652 | isa = XCBuildConfiguration;
653 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
654 | buildSettings = {
655 | ALWAYS_SEARCH_USER_PATHS = NO;
656 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
657 | CLANG_ANALYZER_NONNULL = YES;
658 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
659 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
660 | CLANG_CXX_LIBRARY = "libc++";
661 | CLANG_ENABLE_MODULES = YES;
662 | CLANG_ENABLE_OBJC_ARC = YES;
663 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
664 | CLANG_WARN_BOOL_CONVERSION = YES;
665 | CLANG_WARN_CONSTANT_CONVERSION = YES;
666 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
667 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
668 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
669 | CLANG_WARN_EMPTY_BODY = YES;
670 | CLANG_WARN_ENUM_CONVERSION = YES;
671 | CLANG_WARN_INFINITE_RECURSION = YES;
672 | CLANG_WARN_INT_CONVERSION = YES;
673 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
674 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
675 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
676 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
677 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
678 | CODE_SIGN_IDENTITY = "-";
679 | COPY_PHASE_STRIP = NO;
680 | DEAD_CODE_STRIPPING = YES;
681 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
682 | ENABLE_NS_ASSERTIONS = NO;
683 | ENABLE_STRICT_OBJC_MSGSEND = YES;
684 | ENABLE_USER_SCRIPT_SANDBOXING = NO;
685 | GCC_C_LANGUAGE_STANDARD = gnu11;
686 | GCC_NO_COMMON_BLOCKS = YES;
687 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
688 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
689 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
690 | GCC_WARN_UNUSED_FUNCTION = YES;
691 | GCC_WARN_UNUSED_VARIABLE = YES;
692 | MACOSX_DEPLOYMENT_TARGET = 10.14;
693 | MTL_ENABLE_DEBUG_INFO = NO;
694 | SDKROOT = macosx;
695 | SWIFT_COMPILATION_MODE = wholemodule;
696 | SWIFT_OPTIMIZATION_LEVEL = "-O";
697 | };
698 | name = Release;
699 | };
700 | 33CC10FC2044A3C60003C045 /* Debug */ = {
701 | isa = XCBuildConfiguration;
702 | baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
703 | buildSettings = {
704 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
705 | CLANG_ENABLE_MODULES = YES;
706 | CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
707 | CODE_SIGN_STYLE = Automatic;
708 | COMBINE_HIDPI_IMAGES = YES;
709 | INFOPLIST_FILE = Runner/Info.plist;
710 | LD_RUNPATH_SEARCH_PATHS = (
711 | "$(inherited)",
712 | "@executable_path/../Frameworks",
713 | );
714 | PROVISIONING_PROFILE_SPECIFIER = "";
715 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
716 | SWIFT_VERSION = 5.0;
717 | };
718 | name = Debug;
719 | };
720 | 33CC10FD2044A3C60003C045 /* Release */ = {
721 | isa = XCBuildConfiguration;
722 | baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
723 | buildSettings = {
724 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
725 | CLANG_ENABLE_MODULES = YES;
726 | CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
727 | CODE_SIGN_STYLE = Automatic;
728 | COMBINE_HIDPI_IMAGES = YES;
729 | INFOPLIST_FILE = Runner/Info.plist;
730 | LD_RUNPATH_SEARCH_PATHS = (
731 | "$(inherited)",
732 | "@executable_path/../Frameworks",
733 | );
734 | PROVISIONING_PROFILE_SPECIFIER = "";
735 | SWIFT_VERSION = 5.0;
736 | };
737 | name = Release;
738 | };
739 | 33CC111C2044C6BA0003C045 /* Debug */ = {
740 | isa = XCBuildConfiguration;
741 | buildSettings = {
742 | CODE_SIGN_STYLE = Manual;
743 | PRODUCT_NAME = "$(TARGET_NAME)";
744 | };
745 | name = Debug;
746 | };
747 | 33CC111D2044C6BA0003C045 /* Release */ = {
748 | isa = XCBuildConfiguration;
749 | buildSettings = {
750 | CODE_SIGN_STYLE = Automatic;
751 | PRODUCT_NAME = "$(TARGET_NAME)";
752 | };
753 | name = Release;
754 | };
755 | /* End XCBuildConfiguration section */
756 |
757 | /* Begin XCConfigurationList section */
758 | 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
759 | isa = XCConfigurationList;
760 | buildConfigurations = (
761 | 331C80DB294CF71000263BE5 /* Debug */,
762 | 331C80DC294CF71000263BE5 /* Release */,
763 | 331C80DD294CF71000263BE5 /* Profile */,
764 | );
765 | defaultConfigurationIsVisible = 0;
766 | defaultConfigurationName = Release;
767 | };
768 | 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
769 | isa = XCConfigurationList;
770 | buildConfigurations = (
771 | 33CC10F92044A3C60003C045 /* Debug */,
772 | 33CC10FA2044A3C60003C045 /* Release */,
773 | 338D0CE9231458BD00FA5F75 /* Profile */,
774 | );
775 | defaultConfigurationIsVisible = 0;
776 | defaultConfigurationName = Release;
777 | };
778 | 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
779 | isa = XCConfigurationList;
780 | buildConfigurations = (
781 | 33CC10FC2044A3C60003C045 /* Debug */,
782 | 33CC10FD2044A3C60003C045 /* Release */,
783 | 338D0CEA231458BD00FA5F75 /* Profile */,
784 | );
785 | defaultConfigurationIsVisible = 0;
786 | defaultConfigurationName = Release;
787 | };
788 | 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
789 | isa = XCConfigurationList;
790 | buildConfigurations = (
791 | 33CC111C2044C6BA0003C045 /* Debug */,
792 | 33CC111D2044C6BA0003C045 /* Release */,
793 | 338D0CEB231458BD00FA5F75 /* Profile */,
794 | );
795 | defaultConfigurationIsVisible = 0;
796 | defaultConfigurationName = Release;
797 | };
798 | /* End XCConfigurationList section */
799 | };
800 | rootObject = 33CC10E52044A3C60003C045 /* Project object */;
801 | }
802 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/macos/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | @main
5 | class AppDelegate: FlutterAppDelegate {
6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
7 | return true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "16x16",
5 | "idiom" : "mac",
6 | "filename" : "app_icon_16.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "16x16",
11 | "idiom" : "mac",
12 | "filename" : "app_icon_32.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "32x32",
17 | "idiom" : "mac",
18 | "filename" : "app_icon_32.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "32x32",
23 | "idiom" : "mac",
24 | "filename" : "app_icon_64.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "128x128",
29 | "idiom" : "mac",
30 | "filename" : "app_icon_128.png",
31 | "scale" : "1x"
32 | },
33 | {
34 | "size" : "128x128",
35 | "idiom" : "mac",
36 | "filename" : "app_icon_256.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "256x256",
41 | "idiom" : "mac",
42 | "filename" : "app_icon_256.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "256x256",
47 | "idiom" : "mac",
48 | "filename" : "app_icon_512.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "512x512",
53 | "idiom" : "mac",
54 | "filename" : "app_icon_512.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "512x512",
59 | "idiom" : "mac",
60 | "filename" : "app_icon_1024.png",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
--------------------------------------------------------------------------------
/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/AppInfo.xcconfig:
--------------------------------------------------------------------------------
1 | // Application-level settings for the Runner target.
2 | //
3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
4 | // future. If not, the values below would default to using the project name when this becomes a
5 | // 'flutter create' template.
6 |
7 | // The application's name. By default this is also the title of the Flutter window.
8 | PRODUCT_NAME = example
9 |
10 | // The application's bundle identifier
11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example
12 |
13 | // The copyright displayed in application information
14 | PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved.
15 |
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Debug.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Release.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/macos/Runner/Configs/Warnings.xcconfig:
--------------------------------------------------------------------------------
1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
2 | GCC_WARN_UNDECLARED_SELECTOR = YES
3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
6 | CLANG_WARN_PRAGMA_PACK = YES
7 | CLANG_WARN_STRICT_PROTOTYPES = YES
8 | CLANG_WARN_COMMA = YES
9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES
10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
12 | GCC_WARN_SHADOW = YES
13 | CLANG_WARN_UNREACHABLE_CODE = YES
14 |
--------------------------------------------------------------------------------
/example/macos/Runner/DebugProfile.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.cs.allow-jit
8 |
9 | com.apple.security.network.server
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/example/macos/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | $(PRODUCT_COPYRIGHT)
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/example/macos/Runner/MainFlutterWindow.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | class MainFlutterWindow: NSWindow {
5 | override func awakeFromNib() {
6 | let flutterViewController = FlutterViewController()
7 | let windowFrame = self.frame
8 | self.contentViewController = flutterViewController
9 | self.setFrame(windowFrame, display: true)
10 |
11 | RegisterGeneratedPlugins(registry: flutterViewController)
12 |
13 | super.awakeFromNib()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/example/macos/Runner/Release.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/macos/RunnerTests/RunnerTests.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 | import XCTest
4 |
5 | class RunnerTests: XCTestCase {
6 |
7 | func testExample() {
8 | // If you add code to the Runner application, consider adding tests here.
9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "2.11.0"
12 | boolean_selector:
13 | dependency: transitive
14 | description:
15 | name: boolean_selector
16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.1.1"
20 | characters:
21 | dependency: transitive
22 | description:
23 | name: characters
24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "1.3.0"
28 | clock:
29 | dependency: transitive
30 | description:
31 | name: clock
32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "1.1.1"
36 | collection:
37 | dependency: transitive
38 | description:
39 | name: collection
40 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.18.0"
44 | cupertino_icons:
45 | dependency: "direct main"
46 | description:
47 | name: cupertino_icons
48 | sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.0.6"
52 | fake_async:
53 | dependency: transitive
54 | description:
55 | name: fake_async
56 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
57 | url: "https://pub.dev"
58 | source: hosted
59 | version: "1.3.1"
60 | flutter:
61 | dependency: "direct main"
62 | description: flutter
63 | source: sdk
64 | version: "0.0.0"
65 | flutter_lints:
66 | dependency: "direct dev"
67 | description:
68 | name: flutter_lints
69 | sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
70 | url: "https://pub.dev"
71 | source: hosted
72 | version: "2.0.3"
73 | flutter_platform_alert:
74 | dependency: "direct main"
75 | description:
76 | name: flutter_platform_alert
77 | sha256: "097a99579e0b3b5c1d079daf80659b60e328a6133c22dc5f65ae3298701c0118"
78 | url: "https://pub.dev"
79 | source: hosted
80 | version: "0.5.0"
81 | flutter_test:
82 | dependency: "direct dev"
83 | description: flutter
84 | source: sdk
85 | version: "0.0.0"
86 | flutter_web_plugins:
87 | dependency: transitive
88 | description: flutter
89 | source: sdk
90 | version: "0.0.0"
91 | flutter_window_close:
92 | dependency: "direct main"
93 | description:
94 | path: ".."
95 | relative: true
96 | source: path
97 | version: "1.2.0"
98 | leak_tracker:
99 | dependency: transitive
100 | description:
101 | name: leak_tracker
102 | sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
103 | url: "https://pub.dev"
104 | source: hosted
105 | version: "10.0.5"
106 | leak_tracker_flutter_testing:
107 | dependency: transitive
108 | description:
109 | name: leak_tracker_flutter_testing
110 | sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
111 | url: "https://pub.dev"
112 | source: hosted
113 | version: "3.0.5"
114 | leak_tracker_testing:
115 | dependency: transitive
116 | description:
117 | name: leak_tracker_testing
118 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
119 | url: "https://pub.dev"
120 | source: hosted
121 | version: "3.0.1"
122 | lints:
123 | dependency: transitive
124 | description:
125 | name: lints
126 | sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
127 | url: "https://pub.dev"
128 | source: hosted
129 | version: "2.0.1"
130 | matcher:
131 | dependency: transitive
132 | description:
133 | name: matcher
134 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
135 | url: "https://pub.dev"
136 | source: hosted
137 | version: "0.12.16+1"
138 | material_color_utilities:
139 | dependency: transitive
140 | description:
141 | name: material_color_utilities
142 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
143 | url: "https://pub.dev"
144 | source: hosted
145 | version: "0.11.1"
146 | meta:
147 | dependency: transitive
148 | description:
149 | name: meta
150 | sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
151 | url: "https://pub.dev"
152 | source: hosted
153 | version: "1.15.0"
154 | path:
155 | dependency: transitive
156 | description:
157 | name: path
158 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
159 | url: "https://pub.dev"
160 | source: hosted
161 | version: "1.9.0"
162 | sky_engine:
163 | dependency: transitive
164 | description: flutter
165 | source: sdk
166 | version: "0.0.99"
167 | source_span:
168 | dependency: transitive
169 | description:
170 | name: source_span
171 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
172 | url: "https://pub.dev"
173 | source: hosted
174 | version: "1.10.0"
175 | stack_trace:
176 | dependency: transitive
177 | description:
178 | name: stack_trace
179 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
180 | url: "https://pub.dev"
181 | source: hosted
182 | version: "1.11.1"
183 | stream_channel:
184 | dependency: transitive
185 | description:
186 | name: stream_channel
187 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
188 | url: "https://pub.dev"
189 | source: hosted
190 | version: "2.1.2"
191 | string_scanner:
192 | dependency: transitive
193 | description:
194 | name: string_scanner
195 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
196 | url: "https://pub.dev"
197 | source: hosted
198 | version: "1.2.0"
199 | term_glyph:
200 | dependency: transitive
201 | description:
202 | name: term_glyph
203 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
204 | url: "https://pub.dev"
205 | source: hosted
206 | version: "1.2.1"
207 | test_api:
208 | dependency: transitive
209 | description:
210 | name: test_api
211 | sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
212 | url: "https://pub.dev"
213 | source: hosted
214 | version: "0.7.2"
215 | vector_math:
216 | dependency: transitive
217 | description:
218 | name: vector_math
219 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
220 | url: "https://pub.dev"
221 | source: hosted
222 | version: "2.1.4"
223 | vm_service:
224 | dependency: transitive
225 | description:
226 | name: vm_service
227 | sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
228 | url: "https://pub.dev"
229 | source: hosted
230 | version: "14.2.4"
231 | sdks:
232 | dart: ">=3.3.0 <4.0.0"
233 | flutter: ">=3.18.0-18.0.pre.54"
234 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_window_close_example
2 | description: Demonstrates how to use the flutter_window_close plugin.
3 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
4 |
5 | environment:
6 | sdk: ">=2.17.0 <4.0.0"
7 |
8 | dependencies:
9 | flutter:
10 | sdk: flutter
11 | flutter_platform_alert: ^0.5.0
12 | flutter_window_close:
13 | path: ../
14 | cupertino_icons: ^1.0.6
15 |
16 | dev_dependencies:
17 | flutter_test:
18 | sdk: flutter
19 | flutter_lints:
20 |
21 | flutter:
22 | uses-material-design: true
23 |
24 | # To add assets to your application, add an assets section, like this:
25 | # assets:
26 | # - images/a_dot_burr.jpeg
27 | # - images/a_dot_ham.jpeg
28 |
29 | # An image asset can refer to one or more resolution-specific "variants", see
30 | # https://flutter.dev/assets-and-images/#resolution-aware.
31 |
32 | # For details regarding adding assets from package dependencies, see
33 | # https://flutter.dev/assets-and-images/#from-packages
34 |
35 | # To add custom fonts to your application, add a fonts section here,
36 | # in this "flutter" section. Each entry in this list should have a
37 | # "family" key with the font family name, and a "fonts" key with a
38 | # list giving the asset and other descriptors for the font. For
39 | # example:
40 | # fonts:
41 | # - family: Schyler
42 | # fonts:
43 | # - asset: fonts/Schyler-Regular.ttf
44 | # - asset: fonts/Schyler-Italic.ttf
45 | # style: italic
46 | # - family: Trajan Pro
47 | # fonts:
48 | # - asset: fonts/TrajanPro.ttf
49 | # - asset: fonts/TrajanPro_Bold.ttf
50 | # weight: 700
51 | #
52 | # For details regarding fonts from package dependencies,
53 | # see https://flutter.dev/custom-fonts/#from-packages
54 |
--------------------------------------------------------------------------------
/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:flutter_window_close_example/main.dart';
12 |
13 | void main() {
14 | testWidgets('Verify Platform version', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(const MyApp());
17 |
18 | // Verify that platform version is retrieved.
19 | expect(
20 | find.byWidgetPredicate(
21 | (Widget widget) =>
22 | widget is Text && widget.data!.startsWith('Running on:'),
23 | ),
24 | findsOneWidget,
25 | );
26 | });
27 | }
28 |
--------------------------------------------------------------------------------
/example/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/example/web/favicon.png
--------------------------------------------------------------------------------
/example/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/example/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/example/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/example/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/example/web/icons/Icon-maskable-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/example/web/icons/Icon-maskable-192.png
--------------------------------------------------------------------------------
/example/web/icons/Icon-maskable-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/example/web/icons/Icon-maskable-512.png
--------------------------------------------------------------------------------
/example/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | example
30 |
31 |
32 |
33 |
36 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/example/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "short_name": "example",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "A new Flutter project.",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | },
22 | {
23 | "src": "icons/Icon-maskable-192.png",
24 | "sizes": "192x192",
25 | "type": "image/png",
26 | "purpose": "maskable"
27 | },
28 | {
29 | "src": "icons/Icon-maskable-512.png",
30 | "sizes": "512x512",
31 | "type": "image/png",
32 | "purpose": "maskable"
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/example/windows/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/ephemeral/
2 |
3 | # Visual Studio user-specific files.
4 | *.suo
5 | *.user
6 | *.userosscache
7 | *.sln.docstates
8 |
9 | # Visual Studio build-related files.
10 | x64/
11 | x86/
12 |
13 | # Visual Studio cache files
14 | # files ending in .cache can be ignored
15 | *.[Cc]ache
16 | # but keep track of directories ending in .cache
17 | !*.[Cc]ache/
18 |
--------------------------------------------------------------------------------
/example/windows/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.15)
2 | project(flutter_window_close_example LANGUAGES CXX)
3 |
4 | set(BINARY_NAME "flutter_window_close_example")
5 |
6 | cmake_policy(SET CMP0063 NEW)
7 |
8 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
9 |
10 | # Configure build options.
11 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
12 | if(IS_MULTICONFIG)
13 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
14 | CACHE STRING "" FORCE)
15 | else()
16 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
17 | set(CMAKE_BUILD_TYPE "Debug" CACHE
18 | STRING "Flutter build mode" FORCE)
19 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
20 | "Debug" "Profile" "Release")
21 | endif()
22 | endif()
23 |
24 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
25 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
26 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
27 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
28 |
29 | # Use Unicode for all projects.
30 | add_definitions(-DUNICODE -D_UNICODE)
31 |
32 | # Compilation settings that should be applied to most targets.
33 | function(APPLY_STANDARD_SETTINGS TARGET)
34 | target_compile_features(${TARGET} PUBLIC cxx_std_17)
35 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
36 | target_compile_options(${TARGET} PRIVATE /EHsc)
37 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
38 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>")
39 | endfunction()
40 |
41 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
42 |
43 | # Flutter library and tool build rules.
44 | add_subdirectory(${FLUTTER_MANAGED_DIR})
45 |
46 | # Application build
47 | add_subdirectory("runner")
48 |
49 | # Generated plugin build rules, which manage building the plugins and adding
50 | # them to the application.
51 | include(flutter/generated_plugins.cmake)
52 |
53 |
54 | # === Installation ===
55 | # Support files are copied into place next to the executable, so that it can
56 | # run in place. This is done instead of making a separate bundle (as on Linux)
57 | # so that building and running from within Visual Studio will work.
58 | set(BUILD_BUNDLE_DIR "$")
59 | # Make the "install" step default, as it's required to run.
60 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
61 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
62 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
63 | endif()
64 |
65 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
66 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
67 |
68 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
69 | COMPONENT Runtime)
70 |
71 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
72 | COMPONENT Runtime)
73 |
74 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
75 | COMPONENT Runtime)
76 |
77 | if(PLUGIN_BUNDLED_LIBRARIES)
78 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
79 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
80 | COMPONENT Runtime)
81 | endif()
82 |
83 | # Fully re-copy the assets directory on each build to avoid having stale files
84 | # from a previous install.
85 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
86 | install(CODE "
87 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
88 | " COMPONENT Runtime)
89 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
90 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
91 |
92 | # Install the AOT library on non-Debug builds only.
93 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
94 | CONFIGURATIONS Profile;Release
95 | COMPONENT Runtime)
96 |
--------------------------------------------------------------------------------
/example/windows/flutter/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.15)
2 |
3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
4 |
5 | # Configuration provided via flutter tool.
6 | include(${EPHEMERAL_DIR}/generated_config.cmake)
7 |
8 | # TODO: Move the rest of this into files in ephemeral. See
9 | # https://github.com/flutter/flutter/issues/57146.
10 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
11 |
12 | # === Flutter Library ===
13 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
14 |
15 | # Published to parent scope for install step.
16 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
17 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
18 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
19 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
20 |
21 | list(APPEND FLUTTER_LIBRARY_HEADERS
22 | "flutter_export.h"
23 | "flutter_windows.h"
24 | "flutter_messenger.h"
25 | "flutter_plugin_registrar.h"
26 | "flutter_texture_registrar.h"
27 | )
28 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
29 | add_library(flutter INTERFACE)
30 | target_include_directories(flutter INTERFACE
31 | "${EPHEMERAL_DIR}"
32 | )
33 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
34 | add_dependencies(flutter flutter_assemble)
35 |
36 | # === Wrapper ===
37 | list(APPEND CPP_WRAPPER_SOURCES_CORE
38 | "core_implementations.cc"
39 | "standard_codec.cc"
40 | )
41 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
42 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
43 | "plugin_registrar.cc"
44 | )
45 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
46 | list(APPEND CPP_WRAPPER_SOURCES_APP
47 | "flutter_engine.cc"
48 | "flutter_view_controller.cc"
49 | )
50 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
51 |
52 | # Wrapper sources needed for a plugin.
53 | add_library(flutter_wrapper_plugin STATIC
54 | ${CPP_WRAPPER_SOURCES_CORE}
55 | ${CPP_WRAPPER_SOURCES_PLUGIN}
56 | )
57 | apply_standard_settings(flutter_wrapper_plugin)
58 | set_target_properties(flutter_wrapper_plugin PROPERTIES
59 | POSITION_INDEPENDENT_CODE ON)
60 | set_target_properties(flutter_wrapper_plugin PROPERTIES
61 | CXX_VISIBILITY_PRESET hidden)
62 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
63 | target_include_directories(flutter_wrapper_plugin PUBLIC
64 | "${WRAPPER_ROOT}/include"
65 | )
66 | add_dependencies(flutter_wrapper_plugin flutter_assemble)
67 |
68 | # Wrapper sources needed for the runner.
69 | add_library(flutter_wrapper_app STATIC
70 | ${CPP_WRAPPER_SOURCES_CORE}
71 | ${CPP_WRAPPER_SOURCES_APP}
72 | )
73 | apply_standard_settings(flutter_wrapper_app)
74 | target_link_libraries(flutter_wrapper_app PUBLIC flutter)
75 | target_include_directories(flutter_wrapper_app PUBLIC
76 | "${WRAPPER_ROOT}/include"
77 | )
78 | add_dependencies(flutter_wrapper_app flutter_assemble)
79 |
80 | # === Flutter tool backend ===
81 | # _phony_ is a non-existent file to force this command to run every time,
82 | # since currently there's no way to get a full input/output list from the
83 | # flutter tool.
84 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
85 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
86 | add_custom_command(
87 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
88 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
89 | ${CPP_WRAPPER_SOURCES_APP}
90 | ${PHONY_OUTPUT}
91 | COMMAND ${CMAKE_COMMAND} -E env
92 | ${FLUTTER_TOOL_ENVIRONMENT}
93 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
94 | windows-x64 $
95 | VERBATIM
96 | )
97 | add_custom_target(flutter_assemble DEPENDS
98 | "${FLUTTER_LIBRARY}"
99 | ${FLUTTER_LIBRARY_HEADERS}
100 | ${CPP_WRAPPER_SOURCES_CORE}
101 | ${CPP_WRAPPER_SOURCES_PLUGIN}
102 | ${CPP_WRAPPER_SOURCES_APP}
103 | )
104 |
--------------------------------------------------------------------------------
/example/windows/flutter/generated_plugin_registrant.cc:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #include "generated_plugin_registrant.h"
8 |
9 | #include
10 | #include
11 |
12 | void RegisterPlugins(flutter::PluginRegistry* registry) {
13 | FlutterPlatformAlertPluginRegisterWithRegistrar(
14 | registry->GetRegistrarForPlugin("FlutterPlatformAlertPlugin"));
15 | FlutterWindowClosePluginRegisterWithRegistrar(
16 | registry->GetRegistrarForPlugin("FlutterWindowClosePlugin"));
17 | }
18 |
--------------------------------------------------------------------------------
/example/windows/flutter/generated_plugin_registrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #ifndef GENERATED_PLUGIN_REGISTRANT_
8 | #define GENERATED_PLUGIN_REGISTRANT_
9 |
10 | #include
11 |
12 | // Registers Flutter plugins.
13 | void RegisterPlugins(flutter::PluginRegistry* registry);
14 |
15 | #endif // GENERATED_PLUGIN_REGISTRANT_
16 |
--------------------------------------------------------------------------------
/example/windows/flutter/generated_plugins.cmake:
--------------------------------------------------------------------------------
1 | #
2 | # Generated file, do not edit.
3 | #
4 |
5 | list(APPEND FLUTTER_PLUGIN_LIST
6 | flutter_platform_alert
7 | flutter_window_close
8 | )
9 |
10 | list(APPEND FLUTTER_FFI_PLUGIN_LIST
11 | )
12 |
13 | set(PLUGIN_BUNDLED_LIBRARIES)
14 |
15 | foreach(plugin ${FLUTTER_PLUGIN_LIST})
16 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
17 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
20 | endforeach(plugin)
21 |
22 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
23 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
24 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
25 | endforeach(ffi_plugin)
26 |
--------------------------------------------------------------------------------
/example/windows/runner/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.15)
2 | project(runner LANGUAGES CXX)
3 |
4 | add_executable(${BINARY_NAME} WIN32
5 | "flutter_window.cpp"
6 | "main.cpp"
7 | "utils.cpp"
8 | "win32_window.cpp"
9 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
10 | "Runner.rc"
11 | "runner.exe.manifest"
12 | )
13 | apply_standard_settings(${BINARY_NAME})
14 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
15 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
16 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
17 | add_dependencies(${BINARY_NAME} flutter_assemble)
18 |
--------------------------------------------------------------------------------
/example/windows/runner/Runner.rc:
--------------------------------------------------------------------------------
1 | // Microsoft Visual C++ generated resource script.
2 | //
3 | #pragma code_page(65001)
4 | #include "resource.h"
5 |
6 | #define APSTUDIO_READONLY_SYMBOLS
7 | /////////////////////////////////////////////////////////////////////////////
8 | //
9 | // Generated from the TEXTINCLUDE 2 resource.
10 | //
11 | #include "winres.h"
12 |
13 | /////////////////////////////////////////////////////////////////////////////
14 | #undef APSTUDIO_READONLY_SYMBOLS
15 |
16 | /////////////////////////////////////////////////////////////////////////////
17 | // English (United States) resources
18 |
19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
21 |
22 | #ifdef APSTUDIO_INVOKED
23 | /////////////////////////////////////////////////////////////////////////////
24 | //
25 | // TEXTINCLUDE
26 | //
27 |
28 | 1 TEXTINCLUDE
29 | BEGIN
30 | "resource.h\0"
31 | END
32 |
33 | 2 TEXTINCLUDE
34 | BEGIN
35 | "#include ""winres.h""\r\n"
36 | "\0"
37 | END
38 |
39 | 3 TEXTINCLUDE
40 | BEGIN
41 | "\r\n"
42 | "\0"
43 | END
44 |
45 | #endif // APSTUDIO_INVOKED
46 |
47 |
48 | /////////////////////////////////////////////////////////////////////////////
49 | //
50 | // Icon
51 | //
52 |
53 | // Icon with lowest ID value placed first to ensure application icon
54 | // remains consistent on all systems.
55 | IDI_APP_ICON ICON "resources\\app_icon.ico"
56 |
57 |
58 | /////////////////////////////////////////////////////////////////////////////
59 | //
60 | // Version
61 | //
62 |
63 | #ifdef FLUTTER_BUILD_NUMBER
64 | #define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
65 | #else
66 | #define VERSION_AS_NUMBER 1,0,0
67 | #endif
68 |
69 | #ifdef FLUTTER_BUILD_NAME
70 | #define VERSION_AS_STRING #FLUTTER_BUILD_NAME
71 | #else
72 | #define VERSION_AS_STRING "1.0.0"
73 | #endif
74 |
75 | VS_VERSION_INFO VERSIONINFO
76 | FILEVERSION VERSION_AS_NUMBER
77 | PRODUCTVERSION VERSION_AS_NUMBER
78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
79 | #ifdef _DEBUG
80 | FILEFLAGS VS_FF_DEBUG
81 | #else
82 | FILEFLAGS 0x0L
83 | #endif
84 | FILEOS VOS__WINDOWS32
85 | FILETYPE VFT_APP
86 | FILESUBTYPE 0x0L
87 | BEGIN
88 | BLOCK "StringFileInfo"
89 | BEGIN
90 | BLOCK "040904e4"
91 | BEGIN
92 | VALUE "CompanyName", "net.zonble" "\0"
93 | VALUE "FileDescription", "Demonstrates how to use the flutter_window_close plugin." "\0"
94 | VALUE "FileVersion", VERSION_AS_STRING "\0"
95 | VALUE "InternalName", "flutter_window_close_example" "\0"
96 | VALUE "LegalCopyright", "Copyright (C) 2021 net.zonble. All rights reserved." "\0"
97 | VALUE "OriginalFilename", "flutter_window_close_example.exe" "\0"
98 | VALUE "ProductName", "flutter_window_close_example" "\0"
99 | VALUE "ProductVersion", VERSION_AS_STRING "\0"
100 | END
101 | END
102 | BLOCK "VarFileInfo"
103 | BEGIN
104 | VALUE "Translation", 0x409, 1252
105 | END
106 | END
107 |
108 | #endif // English (United States) resources
109 | /////////////////////////////////////////////////////////////////////////////
110 |
111 |
112 |
113 | #ifndef APSTUDIO_INVOKED
114 | /////////////////////////////////////////////////////////////////////////////
115 | //
116 | // Generated from the TEXTINCLUDE 3 resource.
117 | //
118 |
119 |
120 | /////////////////////////////////////////////////////////////////////////////
121 | #endif // not APSTUDIO_INVOKED
122 |
--------------------------------------------------------------------------------
/example/windows/runner/flutter_window.cpp:
--------------------------------------------------------------------------------
1 | #include "flutter_window.h"
2 |
3 | #include
4 |
5 | #include "flutter/generated_plugin_registrant.h"
6 |
7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project)
8 | : project_(project) {}
9 |
10 | FlutterWindow::~FlutterWindow() {}
11 |
12 | bool FlutterWindow::OnCreate() {
13 | if (!Win32Window::OnCreate()) {
14 | return false;
15 | }
16 |
17 | RECT frame = GetClientArea();
18 |
19 | // The size here must match the window dimensions to avoid unnecessary surface
20 | // creation / destruction in the startup path.
21 | flutter_controller_ = std::make_unique(
22 | frame.right - frame.left, frame.bottom - frame.top, project_);
23 | // Ensure that basic setup of the controller was successful.
24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) {
25 | return false;
26 | }
27 | RegisterPlugins(flutter_controller_->engine());
28 | SetChildContent(flutter_controller_->view()->GetNativeWindow());
29 | return true;
30 | }
31 |
32 | void FlutterWindow::OnDestroy() {
33 | if (flutter_controller_) {
34 | flutter_controller_ = nullptr;
35 | }
36 |
37 | Win32Window::OnDestroy();
38 | }
39 |
40 | LRESULT
41 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
42 | WPARAM const wparam,
43 | LPARAM const lparam) noexcept {
44 | // Give Flutter, including plugins, an opportunity to handle window messages.
45 | if (flutter_controller_) {
46 | std::optional result =
47 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
48 | lparam);
49 | if (result) {
50 | return *result;
51 | }
52 | }
53 |
54 | switch (message) {
55 | case WM_FONTCHANGE:
56 | flutter_controller_->engine()->ReloadSystemFonts();
57 | break;
58 | }
59 |
60 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
61 | }
62 |
--------------------------------------------------------------------------------
/example/windows/runner/flutter_window.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_FLUTTER_WINDOW_H_
2 | #define RUNNER_FLUTTER_WINDOW_H_
3 |
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | #include "win32_window.h"
10 |
11 | // A window that does nothing but host a Flutter view.
12 | class FlutterWindow : public Win32Window {
13 | public:
14 | // Creates a new FlutterWindow hosting a Flutter view running |project|.
15 | explicit FlutterWindow(const flutter::DartProject& project);
16 | virtual ~FlutterWindow();
17 |
18 | protected:
19 | // Win32Window:
20 | bool OnCreate() override;
21 | void OnDestroy() override;
22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
23 | LPARAM const lparam) noexcept override;
24 |
25 | private:
26 | // The project to run.
27 | flutter::DartProject project_;
28 |
29 | // The Flutter instance hosted by this window.
30 | std::unique_ptr flutter_controller_;
31 | };
32 |
33 | #endif // RUNNER_FLUTTER_WINDOW_H_
34 |
--------------------------------------------------------------------------------
/example/windows/runner/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "flutter_window.h"
6 | #include "utils.h"
7 |
8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
9 | _In_ wchar_t *command_line, _In_ int show_command) {
10 | // Attach to console when present (e.g., 'flutter run') or create a
11 | // new console when running with a debugger.
12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
13 | CreateAndAttachConsole();
14 | }
15 |
16 | // Initialize COM, so that it is available for use in the library and/or
17 | // plugins.
18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
19 |
20 | flutter::DartProject project(L"data");
21 |
22 | std::vector command_line_arguments =
23 | GetCommandLineArguments();
24 |
25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
26 |
27 | FlutterWindow window(project);
28 | Win32Window::Point origin(10, 10);
29 | Win32Window::Size size(1280, 720);
30 | if (!window.CreateAndShow(L"flutter_window_close_example", origin, size)) {
31 | return EXIT_FAILURE;
32 | }
33 | window.SetQuitOnClose(true);
34 |
35 | ::MSG msg;
36 | while (::GetMessage(&msg, nullptr, 0, 0)) {
37 | ::TranslateMessage(&msg);
38 | ::DispatchMessage(&msg);
39 | }
40 |
41 | ::CoUninitialize();
42 | return EXIT_SUCCESS;
43 | }
44 |
--------------------------------------------------------------------------------
/example/windows/runner/resource.h:
--------------------------------------------------------------------------------
1 | //{{NO_DEPENDENCIES}}
2 | // Microsoft Visual C++ generated include file.
3 | // Used by Runner.rc
4 | //
5 | #define IDI_APP_ICON 101
6 |
7 | // Next default values for new objects
8 | //
9 | #ifdef APSTUDIO_INVOKED
10 | #ifndef APSTUDIO_READONLY_SYMBOLS
11 | #define _APS_NEXT_RESOURCE_VALUE 102
12 | #define _APS_NEXT_COMMAND_VALUE 40001
13 | #define _APS_NEXT_CONTROL_VALUE 1001
14 | #define _APS_NEXT_SYMED_VALUE 101
15 | #endif
16 | #endif
17 |
--------------------------------------------------------------------------------
/example/windows/runner/resources/app_icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/example/windows/runner/resources/app_icon.ico
--------------------------------------------------------------------------------
/example/windows/runner/runner.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PerMonitorV2
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/example/windows/runner/utils.cpp:
--------------------------------------------------------------------------------
1 | #include "utils.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 |
10 | void CreateAndAttachConsole() {
11 | if (::AllocConsole()) {
12 | FILE *unused;
13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
14 | _dup2(_fileno(stdout), 1);
15 | }
16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
17 | _dup2(_fileno(stdout), 2);
18 | }
19 | std::ios::sync_with_stdio();
20 | FlutterDesktopResyncOutputStreams();
21 | }
22 | }
23 |
24 | std::vector GetCommandLineArguments() {
25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
26 | int argc;
27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
28 | if (argv == nullptr) {
29 | return std::vector();
30 | }
31 |
32 | std::vector 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 | if (target_length == 0) {
52 | return std::string();
53 | }
54 | std::string utf8_string;
55 | utf8_string.resize(target_length);
56 | int converted_length = ::WideCharToMultiByte(
57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
58 | -1, utf8_string.data(),
59 | target_length, nullptr, nullptr);
60 | if (converted_length == 0) {
61 | return std::string();
62 | }
63 | return utf8_string;
64 | }
65 |
--------------------------------------------------------------------------------
/example/windows/runner/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_UTILS_H_
2 | #define RUNNER_UTILS_H_
3 |
4 | #include
5 | #include
6 |
7 | // Creates a console for the process, and redirects stdout and stderr to
8 | // it for both the runner and the Flutter library.
9 | void CreateAndAttachConsole();
10 |
11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
12 | // encoded in UTF-8. Returns an empty std::string on failure.
13 | std::string Utf8FromUtf16(const wchar_t* utf16_string);
14 |
15 | // Gets the command line arguments passed in as a std::vector,
16 | // encoded in UTF-8. Returns an empty std::vector on failure.
17 | std::vector GetCommandLineArguments();
18 |
19 | #endif // RUNNER_UTILS_H_
20 |
--------------------------------------------------------------------------------
/example/windows/runner/win32_window.cpp:
--------------------------------------------------------------------------------
1 | #include "win32_window.h"
2 |
3 | #include
4 |
5 | #include "resource.h"
6 |
7 | namespace {
8 |
9 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
10 |
11 | // The number of Win32Window objects that currently exist.
12 | static int g_active_window_count = 0;
13 |
14 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
15 |
16 | // Scale helper to convert logical scaler values to physical using passed in
17 | // scale factor
18 | int Scale(int source, double scale_factor) {
19 | return static_cast(source * scale_factor);
20 | }
21 |
22 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
23 | // This API is only needed for PerMonitor V1 awareness mode.
24 | void EnableFullDpiSupportIfAvailable(HWND hwnd) {
25 | HMODULE user32_module = LoadLibraryA("User32.dll");
26 | if (!user32_module) {
27 | return;
28 | }
29 | auto enable_non_client_dpi_scaling =
30 | reinterpret_cast(
31 | GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
32 | if (enable_non_client_dpi_scaling != nullptr) {
33 | enable_non_client_dpi_scaling(hwnd);
34 | FreeLibrary(user32_module);
35 | }
36 | }
37 |
38 | } // namespace
39 |
40 | // Manages the Win32Window's window class registration.
41 | class WindowClassRegistrar {
42 | public:
43 | ~WindowClassRegistrar() = default;
44 |
45 | // Returns the singleton registar instance.
46 | static WindowClassRegistrar* GetInstance() {
47 | if (!instance_) {
48 | instance_ = new WindowClassRegistrar();
49 | }
50 | return instance_;
51 | }
52 |
53 | // Returns the name of the window class, registering the class if it hasn't
54 | // previously been registered.
55 | const wchar_t* GetWindowClass();
56 |
57 | // Unregisters the window class. Should only be called if there are no
58 | // instances of the window.
59 | void UnregisterWindowClass();
60 |
61 | private:
62 | WindowClassRegistrar() = default;
63 |
64 | static WindowClassRegistrar* instance_;
65 |
66 | bool class_registered_ = false;
67 | };
68 |
69 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
70 |
71 | const wchar_t* WindowClassRegistrar::GetWindowClass() {
72 | if (!class_registered_) {
73 | WNDCLASS window_class{};
74 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
75 | window_class.lpszClassName = kWindowClassName;
76 | window_class.style = CS_HREDRAW | CS_VREDRAW;
77 | window_class.cbClsExtra = 0;
78 | window_class.cbWndExtra = 0;
79 | window_class.hInstance = GetModuleHandle(nullptr);
80 | window_class.hIcon =
81 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
82 | window_class.hbrBackground = 0;
83 | window_class.lpszMenuName = nullptr;
84 | window_class.lpfnWndProc = Win32Window::WndProc;
85 | RegisterClass(&window_class);
86 | class_registered_ = true;
87 | }
88 | return kWindowClassName;
89 | }
90 |
91 | void WindowClassRegistrar::UnregisterWindowClass() {
92 | UnregisterClass(kWindowClassName, nullptr);
93 | class_registered_ = false;
94 | }
95 |
96 | Win32Window::Win32Window() {
97 | ++g_active_window_count;
98 | }
99 |
100 | Win32Window::~Win32Window() {
101 | --g_active_window_count;
102 | Destroy();
103 | }
104 |
105 | bool Win32Window::CreateAndShow(const std::wstring& title,
106 | const Point& origin,
107 | const Size& size) {
108 | Destroy();
109 |
110 | const wchar_t* window_class =
111 | WindowClassRegistrar::GetInstance()->GetWindowClass();
112 |
113 | const POINT target_point = {static_cast(origin.x),
114 | static_cast(origin.y)};
115 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
116 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
117 | double scale_factor = dpi / 96.0;
118 |
119 | HWND window = CreateWindow(
120 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
121 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
122 | Scale(size.width, scale_factor), Scale(size.height, scale_factor),
123 | nullptr, nullptr, GetModuleHandle(nullptr), this);
124 |
125 | if (!window) {
126 | return false;
127 | }
128 |
129 | return OnCreate();
130 | }
131 |
132 | // static
133 | LRESULT CALLBACK Win32Window::WndProc(HWND const window,
134 | UINT const message,
135 | WPARAM const wparam,
136 | LPARAM const lparam) noexcept {
137 | if (message == WM_NCCREATE) {
138 | auto window_struct = reinterpret_cast(lparam);
139 | SetWindowLongPtr(window, GWLP_USERDATA,
140 | reinterpret_cast(window_struct->lpCreateParams));
141 |
142 | auto that = static_cast(window_struct->lpCreateParams);
143 | EnableFullDpiSupportIfAvailable(window);
144 | that->window_handle_ = window;
145 | } else if (Win32Window* that = GetThisFromHandle(window)) {
146 | return that->MessageHandler(window, message, wparam, lparam);
147 | }
148 |
149 | return DefWindowProc(window, message, wparam, lparam);
150 | }
151 |
152 | LRESULT
153 | Win32Window::MessageHandler(HWND hwnd,
154 | UINT const message,
155 | WPARAM const wparam,
156 | LPARAM const lparam) noexcept {
157 | switch (message) {
158 | case WM_DESTROY:
159 | window_handle_ = nullptr;
160 | Destroy();
161 | if (quit_on_close_) {
162 | PostQuitMessage(0);
163 | }
164 | return 0;
165 |
166 | case WM_DPICHANGED: {
167 | auto newRectSize = reinterpret_cast(lparam);
168 | LONG newWidth = newRectSize->right - newRectSize->left;
169 | LONG newHeight = newRectSize->bottom - newRectSize->top;
170 |
171 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
172 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
173 |
174 | return 0;
175 | }
176 | case WM_SIZE: {
177 | RECT rect = GetClientArea();
178 | if (child_content_ != nullptr) {
179 | // Size and position the child window.
180 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
181 | rect.bottom - rect.top, TRUE);
182 | }
183 | return 0;
184 | }
185 |
186 | case WM_ACTIVATE:
187 | if (child_content_ != nullptr) {
188 | SetFocus(child_content_);
189 | }
190 | return 0;
191 | }
192 |
193 | return DefWindowProc(window_handle_, message, wparam, lparam);
194 | }
195 |
196 | void Win32Window::Destroy() {
197 | OnDestroy();
198 |
199 | if (window_handle_) {
200 | DestroyWindow(window_handle_);
201 | window_handle_ = nullptr;
202 | }
203 | if (g_active_window_count == 0) {
204 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
205 | }
206 | }
207 |
208 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
209 | return reinterpret_cast(
210 | GetWindowLongPtr(window, GWLP_USERDATA));
211 | }
212 |
213 | void Win32Window::SetChildContent(HWND content) {
214 | child_content_ = content;
215 | SetParent(content, window_handle_);
216 | RECT frame = GetClientArea();
217 |
218 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
219 | frame.bottom - frame.top, true);
220 |
221 | SetFocus(child_content_);
222 | }
223 |
224 | RECT Win32Window::GetClientArea() {
225 | RECT frame;
226 | GetClientRect(window_handle_, &frame);
227 | return frame;
228 | }
229 |
230 | HWND Win32Window::GetHandle() {
231 | return window_handle_;
232 | }
233 |
234 | void Win32Window::SetQuitOnClose(bool quit_on_close) {
235 | quit_on_close_ = quit_on_close;
236 | }
237 |
238 | bool Win32Window::OnCreate() {
239 | // No-op; provided for subclasses.
240 | return true;
241 | }
242 |
243 | void Win32Window::OnDestroy() {
244 | // No-op; provided for subclasses.
245 | }
246 |
--------------------------------------------------------------------------------
/example/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 | Point(unsigned int x, unsigned int y) : x(x), y(y) {}
19 | };
20 |
21 | struct Size {
22 | unsigned int width;
23 | unsigned int height;
24 | Size(unsigned int width, unsigned int height)
25 | : width(width), height(height) {}
26 | };
27 |
28 | Win32Window();
29 | virtual ~Win32Window();
30 |
31 | // Creates and shows a win32 window with |title| and position and size using
32 | // |origin| and |size|. New windows are created on the default monitor. Window
33 | // sizes are specified to the OS in physical pixels, hence to ensure a
34 | // consistent size to will treat the width height passed in to this function
35 | // as logical pixels and scale to appropriate for the default monitor. Returns
36 | // true if the window was created successfully.
37 | bool CreateAndShow(const std::wstring& title,
38 | const Point& origin,
39 | const Size& size);
40 |
41 | // Release OS resources associated with window.
42 | void Destroy();
43 |
44 | // Inserts |content| into the window tree.
45 | void SetChildContent(HWND content);
46 |
47 | // Returns the backing Window handle to enable clients to set icon and other
48 | // window properties. Returns nullptr if the window has been destroyed.
49 | HWND GetHandle();
50 |
51 | // If true, closing this window will quit the application.
52 | void SetQuitOnClose(bool quit_on_close);
53 |
54 | // Return a RECT representing the bounds of the current client area.
55 | RECT GetClientArea();
56 |
57 | protected:
58 | // Processes and route salient window messages for mouse handling,
59 | // size change and DPI. Delegates handling of these to member overloads that
60 | // inheriting classes can handle.
61 | virtual LRESULT MessageHandler(HWND window,
62 | UINT const message,
63 | WPARAM const wparam,
64 | LPARAM const lparam) noexcept;
65 |
66 | // Called when CreateAndShow is called, allowing subclass window-related
67 | // setup. Subclasses should return false if setup fails.
68 | virtual bool OnCreate();
69 |
70 | // Called when Destroy is called.
71 | virtual void OnDestroy();
72 |
73 | private:
74 | friend class WindowClassRegistrar;
75 |
76 | // OS callback called by message pump. Handles the WM_NCCREATE message which
77 | // is passed when the non-client area is being created and enables automatic
78 | // non-client DPI scaling so that the non-client area automatically
79 | // responsponds to changes in DPI. All other messages are handled by
80 | // MessageHandler.
81 | static LRESULT CALLBACK WndProc(HWND const window,
82 | UINT const message,
83 | WPARAM const wparam,
84 | LPARAM const lparam) noexcept;
85 |
86 | // Retrieves a class instance pointer for |window|
87 | static Win32Window* GetThisFromHandle(HWND const window) noexcept;
88 |
89 | bool quit_on_close_ = false;
90 |
91 | // window handle for top level window.
92 | HWND window_handle_ = nullptr;
93 |
94 | // window handle for hosted content.
95 | HWND child_content_ = nullptr;
96 | };
97 |
98 | #endif // RUNNER_WIN32_WINDOW_H_
99 |
--------------------------------------------------------------------------------
/flutter_window_close.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/lib/flutter_window_close.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/foundation.dart';
4 | import 'package:flutter/services.dart';
5 | import 'package:flutter/widgets.dart';
6 |
7 | /// A plug-in that helps your Flutter desktop app to handle window close event.
8 | ///
9 | /// It supports Windows, macOS and Linux.
10 | class FlutterWindowClose {
11 | FlutterWindowClose._();
12 |
13 | static Future Function()? _onWindowShouldClose;
14 | static MethodChannel? _notificationChannel;
15 | static const MethodChannel _channel = MethodChannel('flutter_window_close');
16 |
17 | static Future _initIfRequired() async {
18 | if (_notificationChannel != null) {
19 | return;
20 | }
21 |
22 | WidgetsFlutterBinding.ensureInitialized();
23 | await _channel.invokeMethod('init');
24 |
25 | var channel = const MethodChannel('flutter_window_close_notification');
26 | channel.setMethodCallHandler((call) async {
27 | if (call.method == 'onWindowClose') {
28 | final handler = FlutterWindowClose._onWindowShouldClose;
29 |
30 | // Note: the 'destroyWindow' method just close the window without
31 | // any confirming.
32 | if (handler != null) {
33 | final result = await handler();
34 | if (result) _channel.invokeMethod('destroyWindow');
35 | } else {
36 | _channel.invokeMethod('destroyWindow');
37 | }
38 | }
39 | return null;
40 | });
41 | _notificationChannel = _channel;
42 | }
43 |
44 | /// Sets a function to handle window close events.
45 | ///
46 | /// When a user click on the close button on a window, the plug-in redirects
47 | /// the event to your function. The function should return a future that
48 | /// returns a boolean to tell the plug-in whether the user really wants to
49 | /// close the window or not. True will let the window to be closed, while
50 | /// false let the window to remain open.
51 | ///
52 | /// By default there is no handler, and the window will be directly closed
53 | /// when a window close event happens. You can also reset the handler by
54 | /// passing null to the method.
55 | ///
56 | /// Example:
57 | ///
58 | /// ``` dart
59 | /// FlutterWindowClose.setWindowShouldCloseHandler(() async {
60 | /// return await showDialog(
61 | /// context: context,
62 | /// builder: (context) {
63 | /// return AlertDialog(
64 | /// title: const Text('Do you really want to quit?'),
65 | /// actions: [
66 | /// ElevatedButton(
67 | /// onPressed: () => Navigator.of(context).pop(true),
68 | /// child: const Text('Yes')),
69 | /// ElevatedButton(
70 | /// onPressed: () => Navigator.of(context).pop(false),
71 | /// child: const Text('No')),
72 | /// ]);
73 | /// });
74 | /// });
75 | /// ```
76 | ///
77 | /// The method does not support Flutter Web.
78 | static Future setWindowShouldCloseHandler(
79 | Future Function()? handler) async {
80 | if (kIsWeb) throw Exception('The method does not work in Flutter Web.');
81 | _onWindowShouldClose = handler;
82 | await _initIfRequired();
83 | }
84 |
85 | /// Sends a message to close the window hosting your Flutter app.
86 | ///
87 | /// - On Windows, it calls `PostMessage(handle, WM_CLOSE, 0, 0)`. See
88 | /// [WM_CLOSE](https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-close)
89 | /// - On macOS, it calls [-\[NSWindow performClose:\]](https://developer.apple.com/documentation/appkit/nswindow/1419288-performclose?language=objc)
90 | /// - On Linux, it calls [gtk_window_close](https://gnome.pages.gitlab.gnome.org/gtk/gtk4/method.Window.close.html)
91 | /// - The method does not support Flutter Web.
92 | static Future closeWindow() async {
93 | if (kIsWeb) throw Exception('The method does not work in Flutter Web.');
94 | await _initIfRequired();
95 | await _channel.invokeMethod('closeWindow');
96 | }
97 |
98 | static Future destroyWindow() async {
99 | if (kIsWeb) throw Exception('The method does not work in Flutter Web.');
100 | await _initIfRequired();
101 | await _channel.invokeMethod('destroyWindow');
102 | }
103 |
104 | /// Sets a return value when the current window or tab is being closed
105 | /// when your app is running in Flutter Web.
106 | static Future setWebReturnValue(String? returnValue) async {
107 | if (!kIsWeb) throw Exception('The method only works in Flutter Web.');
108 | await _channel.invokeMethod('setWebReturnValue', returnValue);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/lib/flutter_window_close_web.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | // In order to *not* need this ignore, consider extracting the "web" version
4 | // of your plugin as a separate package, instead of inlining it in the same
5 | // package as the core of your plugin.
6 | // ignore: avoid_web_libraries_in_flutter
7 | import 'dart:html' as html;
8 |
9 | import 'package:flutter/services.dart';
10 | import 'package:flutter_web_plugins/flutter_web_plugins.dart';
11 |
12 | /// A web implementation of the FlutterHi plugin.
13 | class FlutterWindowClosePluginWeb {
14 | static void registerWith(Registrar registrar) {
15 | final MethodChannel channel = MethodChannel(
16 | 'flutter_window_close',
17 | const StandardMethodCodec(),
18 | registrar,
19 | );
20 |
21 | final pluginInstance = FlutterWindowClosePluginWeb();
22 | channel.setMethodCallHandler(pluginInstance.handleMethodCall);
23 | }
24 |
25 | FlutterWindowClosePluginWeb() {
26 | html.window.onBeforeUnload.listen((event) {
27 | if (event is html.BeforeUnloadEvent) {
28 | if (_returnValue != null) {
29 | event.returnValue = _returnValue;
30 | }
31 | }
32 | });
33 | }
34 |
35 | String? _returnValue;
36 |
37 | Future handleMethodCall(MethodCall call) async {
38 | switch (call.method) {
39 | case 'setWebReturnValue':
40 | _returnValue = call.arguments;
41 | break;
42 | default:
43 | throw PlatformException(
44 | code: 'Unimplemented',
45 | details: 'flutter_hi for web doesn\'t implement \'${call.method}\'',
46 | );
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/linux/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.10)
2 | set(PROJECT_NAME "flutter_window_close")
3 | project(${PROJECT_NAME} LANGUAGES CXX)
4 |
5 | # This value is used when generating builds using this plugin, so it must
6 | # not be changed
7 | set(PLUGIN_NAME "flutter_window_close_plugin")
8 |
9 | add_library(${PLUGIN_NAME} SHARED
10 | "flutter_window_close_plugin.cc"
11 | )
12 | apply_standard_settings(${PLUGIN_NAME})
13 | set_target_properties(${PLUGIN_NAME} PROPERTIES
14 | CXX_VISIBILITY_PRESET hidden)
15 | target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
16 | target_include_directories(${PLUGIN_NAME} INTERFACE
17 | "${CMAKE_CURRENT_SOURCE_DIR}/include")
18 | target_link_libraries(${PLUGIN_NAME} PRIVATE flutter)
19 | target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK)
20 |
21 | # List of absolute paths to libraries that should be bundled with the plugin
22 | set(flutter_window_close_bundled_libraries
23 | ""
24 | PARENT_SCOPE
25 | )
26 |
--------------------------------------------------------------------------------
/linux/flutter_window_close_plugin.cc:
--------------------------------------------------------------------------------
1 | #include "include/flutter_window_close/flutter_window_close_plugin.h"
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | #include
8 | #include
9 |
10 | #define FLUTTER_WINDOW_CLOSE_PLUGIN(obj) \
11 | (G_TYPE_CHECK_INSTANCE_CAST((obj), flutter_window_close_plugin_get_type(), \
12 | FlutterWindowClosePlugin))
13 |
14 | struct _FlutterWindowClosePlugin {
15 | GObject parent_instance;
16 | GtkWidget *widget;
17 | bool initialized;
18 | };
19 |
20 | G_DEFINE_TYPE(FlutterWindowClosePlugin, flutter_window_close_plugin,
21 | g_object_get_type())
22 |
23 | // Called when a method call is received from Flutter.
24 | static void
25 | flutter_window_close_plugin_handle_method_call(FlutterWindowClosePlugin *self,
26 | FlMethodCall *method_call) {
27 |
28 | const gchar *method = fl_method_call_get_name(method_call);
29 |
30 | if (strcmp(method, "closeWindow") == 0) {
31 | g_autoptr(FlMethodResponse) response = nullptr;
32 | gtk_window_close((GtkWindow *)self->widget);
33 | response = FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
34 | fl_method_call_respond(method_call, response, nullptr);
35 | } else if (strcmp(method, "destroyWindow") == 0) {
36 | g_autoptr(FlMethodResponse) response = nullptr;
37 | response = FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
38 | fl_method_call_respond(method_call, response, nullptr);
39 | std::thread([=]() {
40 | g_signal_emit_by_name(G_OBJECT((GtkWindow *)self->widget), "destroy");
41 | }).detach();
42 | } else if (strcmp(method, "init") == 0) {
43 | self->initialized = true;
44 | g_autoptr(FlMethodResponse) response = nullptr;
45 | response = FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
46 | fl_method_call_respond(method_call, response, nullptr);
47 | } else {
48 | g_autoptr(FlMethodResponse) response = nullptr;
49 | response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
50 | fl_method_call_respond(method_call, response, nullptr);
51 | }
52 | }
53 |
54 | static void flutter_window_close_plugin_dispose(GObject *object) {
55 | G_OBJECT_CLASS(flutter_window_close_plugin_parent_class)->dispose(object);
56 | }
57 |
58 | static void
59 | flutter_window_close_plugin_class_init(FlutterWindowClosePluginClass *klass) {
60 | G_OBJECT_CLASS(klass)->dispose = flutter_window_close_plugin_dispose;
61 | }
62 |
63 | static void flutter_window_close_plugin_init(FlutterWindowClosePlugin *self) {}
64 |
65 | static void method_call_cb(FlMethodChannel *channel, FlMethodCall *method_call,
66 | gpointer user_data) {
67 | FlutterWindowClosePlugin *plugin = FLUTTER_WINDOW_CLOSE_PLUGIN(user_data);
68 | flutter_window_close_plugin_handle_method_call(plugin, method_call);
69 | }
70 |
71 | static FlMethodChannel *notificationChannel;
72 |
73 | static gboolean main_window_close(GtkWidget *window, GdkEvent *event,
74 | gpointer user_data) {
75 | FlutterWindowClosePlugin *plugin = FLUTTER_WINDOW_CLOSE_PLUGIN(user_data);
76 | if (plugin->initialized == false) {
77 | return FALSE;
78 | }
79 |
80 | FlValue *value = fl_value_new_null();
81 | fl_method_channel_invoke_method(notificationChannel, "onWindowClose", value,
82 | NULL, NULL, NULL);
83 | return TRUE;
84 | }
85 |
86 | void flutter_window_close_plugin_register_with_registrar(
87 | FlPluginRegistrar *registrar) {
88 | FlutterWindowClosePlugin *plugin = FLUTTER_WINDOW_CLOSE_PLUGIN(
89 | g_object_new(flutter_window_close_plugin_get_type(), nullptr));
90 | GtkWidget *window = gtk_widget_get_ancestor(
91 | (GtkWidget *)fl_plugin_registrar_get_view(registrar), GTK_TYPE_WINDOW);
92 |
93 | plugin->widget = window;
94 |
95 | // See
96 | // https://github.com/leanflutter/window_manager/blob/main/linux/window_manager_plugin.cc
97 | //
98 | // Disconnect all delete-event handlers first in flutter 3.10.1, which
99 | // causes delete_event not working. Issues from flutter/engine:
100 | // https://github.com/flutter/engine/pull/40033
101 | guint handler_id =
102 | g_signal_handler_find(window, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
103 | fl_plugin_registrar_get_view(registrar));
104 | if (handler_id > 0) {
105 | g_signal_handler_disconnect(window, handler_id);
106 | }
107 |
108 | g_signal_connect(G_OBJECT(window), "delete_event",
109 | G_CALLBACK(main_window_close), g_object_ref(plugin));
110 |
111 | g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
112 | g_autoptr(FlMethodChannel) channel =
113 | fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar),
114 | "flutter_window_close", FL_METHOD_CODEC(codec));
115 |
116 | fl_method_channel_set_method_call_handler(
117 | channel, method_call_cb, g_object_ref(plugin), g_object_unref);
118 |
119 | g_object_unref(plugin);
120 |
121 | notificationChannel = fl_method_channel_new(
122 | fl_plugin_registrar_get_messenger(registrar),
123 | "flutter_window_close_notification", FL_METHOD_CODEC(codec));
124 | }
125 |
--------------------------------------------------------------------------------
/linux/include/flutter_window_close/flutter_window_close_plugin.h:
--------------------------------------------------------------------------------
1 | #ifndef FLUTTER_PLUGIN_FLUTTER_WINDOW_CLOSE_PLUGIN_H_
2 | #define FLUTTER_PLUGIN_FLUTTER_WINDOW_CLOSE_PLUGIN_H_
3 |
4 | #include
5 |
6 | G_BEGIN_DECLS
7 |
8 | #ifdef FLUTTER_PLUGIN_IMPL
9 | #define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default")))
10 | #else
11 | #define FLUTTER_PLUGIN_EXPORT
12 | #endif
13 |
14 | typedef struct _FlutterWindowClosePlugin FlutterWindowClosePlugin;
15 | typedef struct {
16 | GObjectClass parent_class;
17 | } FlutterWindowClosePluginClass;
18 |
19 | FLUTTER_PLUGIN_EXPORT GType flutter_window_close_plugin_get_type();
20 |
21 | FLUTTER_PLUGIN_EXPORT void flutter_window_close_plugin_register_with_registrar(
22 | FlPluginRegistrar* registrar);
23 |
24 | G_END_DECLS
25 |
26 | #endif // FLUTTER_PLUGIN_FLUTTER_WINDOW_CLOSE_PLUGIN_H_
27 |
--------------------------------------------------------------------------------
/macos/flutter_window_close.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
3 | # Run `pod lib lint flutter_window_close.podspec` to validate before publishing.
4 | #
5 | Pod::Spec.new do |s|
6 | s.name = 'flutter_window_close'
7 | s.version = '0.0.1'
8 | s.summary = 'A new Flutter plugin project.'
9 | s.description = <<-DESC
10 | A new Flutter plugin project.
11 | DESC
12 | s.homepage = 'http://example.com'
13 | s.license = { :file => '../LICENSE' }
14 | s.author = { 'Your Company' => 'email@example.com' }
15 |
16 | s.source = { :path => '.' }
17 | s.source_files = 'flutter_window_close/Sources/flutter_window_close/**/*'
18 | s.dependency 'FlutterMacOS'
19 |
20 | s.platform = :osx, '10.11'
21 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
22 | s.swift_version = '5.0'
23 | end
24 |
--------------------------------------------------------------------------------
/macos/flutter_window_close/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.9
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "flutter_window_close",
8 | platforms: [
9 | .macOS("10.14")
10 | ],
11 | products: [
12 | .library(name: "flutter-window-close", targets: ["flutter_window_close"])
13 | ],
14 | dependencies: [],
15 | targets: [
16 | .target(
17 | name: "flutter_window_close",
18 | dependencies: [],
19 | resources: [
20 | .process("Resources"),
21 | ]
22 | )
23 | ]
24 | )
25 |
--------------------------------------------------------------------------------
/macos/flutter_window_close/Sources/flutter_window_close/FlutterWindowClosePlugin.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | public class FlutterWindowClosePlugin: NSObject, FlutterPlugin, NSWindowDelegate {
5 | /// The window that contains the view that is registering the plugins.
6 | private var window: NSWindow?
7 | /// The method channel to notify the Flutter side.
8 | private var notificationChannel: FlutterMethodChannel?
9 | /// If the plugin is initialized.
10 | private var initialized = false
11 |
12 | public static func register(with registrar: FlutterPluginRegistrar) {
13 | let channel = FlutterMethodChannel(
14 | name: "flutter_window_close", binaryMessenger: registrar.messenger)
15 | let instance = FlutterWindowClosePlugin()
16 | instance.notificationChannel = FlutterMethodChannel(
17 | name: "flutter_window_close_notification", binaryMessenger: registrar.messenger)
18 | instance.window = registrar.view?.window
19 | instance.window?.delegate = instance
20 | registrar.addMethodCallDelegate(instance, channel: channel)
21 | }
22 |
23 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
24 | switch call.method {
25 | case "closeWindow":
26 | self.window?.performClose(nil)
27 | result(nil)
28 | case "destroyWindow":
29 | self.window?.close()
30 | result(nil)
31 | case "init":
32 | initialized = true
33 | result(nil)
34 | default:
35 | result(FlutterMethodNotImplemented)
36 | }
37 | }
38 |
39 | //MARK: - NSWindow delegate
40 |
41 | public func windowShouldClose(_ sender: NSWindow) -> Bool {
42 | if initialized == false {
43 | return true
44 | }
45 |
46 | notificationChannel?.invokeMethod("onWindowClose", arguments: nil)
47 | return false
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/macos/flutter_window_close/Sources/flutter_window_close/Resources/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zonble/flutter_window_close/cbfaf0e7e7071bb6ba3cb6b3505598649de1beba/macos/flutter_window_close/Sources/flutter_window_close/Resources/.gitkeep
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "2.11.0"
12 | boolean_selector:
13 | dependency: transitive
14 | description:
15 | name: boolean_selector
16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.1.1"
20 | characters:
21 | dependency: transitive
22 | description:
23 | name: characters
24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "1.3.0"
28 | clock:
29 | dependency: transitive
30 | description:
31 | name: clock
32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "1.1.1"
36 | collection:
37 | dependency: transitive
38 | description:
39 | name: collection
40 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.18.0"
44 | fake_async:
45 | dependency: transitive
46 | description:
47 | name: fake_async
48 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.3.1"
52 | flutter:
53 | dependency: "direct main"
54 | description: flutter
55 | source: sdk
56 | version: "0.0.0"
57 | flutter_lints:
58 | dependency: "direct dev"
59 | description:
60 | name: flutter_lints
61 | sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
62 | url: "https://pub.dev"
63 | source: hosted
64 | version: "5.0.0"
65 | flutter_test:
66 | dependency: "direct dev"
67 | description: flutter
68 | source: sdk
69 | version: "0.0.0"
70 | flutter_web_plugins:
71 | dependency: "direct main"
72 | description: flutter
73 | source: sdk
74 | version: "0.0.0"
75 | leak_tracker:
76 | dependency: transitive
77 | description:
78 | name: leak_tracker
79 | sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
80 | url: "https://pub.dev"
81 | source: hosted
82 | version: "10.0.5"
83 | leak_tracker_flutter_testing:
84 | dependency: transitive
85 | description:
86 | name: leak_tracker_flutter_testing
87 | sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
88 | url: "https://pub.dev"
89 | source: hosted
90 | version: "3.0.5"
91 | leak_tracker_testing:
92 | dependency: transitive
93 | description:
94 | name: leak_tracker_testing
95 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
96 | url: "https://pub.dev"
97 | source: hosted
98 | version: "3.0.1"
99 | lints:
100 | dependency: transitive
101 | description:
102 | name: lints
103 | sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413"
104 | url: "https://pub.dev"
105 | source: hosted
106 | version: "5.0.0"
107 | matcher:
108 | dependency: transitive
109 | description:
110 | name: matcher
111 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
112 | url: "https://pub.dev"
113 | source: hosted
114 | version: "0.12.16+1"
115 | material_color_utilities:
116 | dependency: transitive
117 | description:
118 | name: material_color_utilities
119 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
120 | url: "https://pub.dev"
121 | source: hosted
122 | version: "0.11.1"
123 | meta:
124 | dependency: transitive
125 | description:
126 | name: meta
127 | sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
128 | url: "https://pub.dev"
129 | source: hosted
130 | version: "1.15.0"
131 | path:
132 | dependency: transitive
133 | description:
134 | name: path
135 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
136 | url: "https://pub.dev"
137 | source: hosted
138 | version: "1.9.0"
139 | sky_engine:
140 | dependency: transitive
141 | description: flutter
142 | source: sdk
143 | version: "0.0.99"
144 | source_span:
145 | dependency: transitive
146 | description:
147 | name: source_span
148 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
149 | url: "https://pub.dev"
150 | source: hosted
151 | version: "1.10.0"
152 | stack_trace:
153 | dependency: transitive
154 | description:
155 | name: stack_trace
156 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
157 | url: "https://pub.dev"
158 | source: hosted
159 | version: "1.11.1"
160 | stream_channel:
161 | dependency: transitive
162 | description:
163 | name: stream_channel
164 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
165 | url: "https://pub.dev"
166 | source: hosted
167 | version: "2.1.2"
168 | string_scanner:
169 | dependency: transitive
170 | description:
171 | name: string_scanner
172 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
173 | url: "https://pub.dev"
174 | source: hosted
175 | version: "1.2.0"
176 | term_glyph:
177 | dependency: transitive
178 | description:
179 | name: term_glyph
180 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
181 | url: "https://pub.dev"
182 | source: hosted
183 | version: "1.2.1"
184 | test_api:
185 | dependency: transitive
186 | description:
187 | name: test_api
188 | sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
189 | url: "https://pub.dev"
190 | source: hosted
191 | version: "0.7.2"
192 | vector_math:
193 | dependency: transitive
194 | description:
195 | name: vector_math
196 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
197 | url: "https://pub.dev"
198 | source: hosted
199 | version: "2.1.4"
200 | vm_service:
201 | dependency: transitive
202 | description:
203 | name: vm_service
204 | sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
205 | url: "https://pub.dev"
206 | source: hosted
207 | version: "14.2.5"
208 | sdks:
209 | dart: ">=3.3.0 <4.0.0"
210 | flutter: ">=3.18.0-18.0.pre.54"
211 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_window_close
2 | description: Let your Flutter app has a chance to confirm if the user wants to close your app.
3 | version: 1.2.0
4 | homepage: https://github.com/zonble/flutter_window_close
5 |
6 | environment:
7 | sdk: ">=2.17.0 <4.0.0"
8 | flutter: ">=1.20.0"
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 | flutter_web_plugins:
14 | sdk: flutter
15 |
16 | dev_dependencies:
17 | flutter_test:
18 | sdk: flutter
19 | flutter_lints: any
20 |
21 | flutter:
22 | plugin:
23 | platforms:
24 | macos:
25 | pluginClass: FlutterWindowClosePlugin
26 | windows:
27 | pluginClass: FlutterWindowClosePlugin
28 | linux:
29 | pluginClass: FlutterWindowClosePlugin
30 | web:
31 | pluginClass: FlutterWindowClosePluginWeb
32 | fileName: flutter_window_close_web.dart
33 |
--------------------------------------------------------------------------------
/test/flutter_window_close_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/services.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 | import 'package:flutter_window_close/flutter_window_close.dart';
4 |
5 | void main() {
6 | // const MethodChannel channel = MethodChannel('flutter_window_close');
7 | //
8 | // TestWidgetsFlutterBinding.ensureInitialized();
9 | //
10 | // setUp(() {
11 | // channel.setMockMethodCallHandler((MethodCall methodCall) async {
12 | // return '42';
13 | // });
14 | // });
15 | //
16 | // tearDown(() {
17 | // channel.setMockMethodCallHandler(null);
18 | // });
19 | //
20 | // test('getPlatformVersion', () async {
21 | // expect(await FlutterWindowClose.platformVersion, '42');
22 | // });
23 | }
24 |
--------------------------------------------------------------------------------
/windows/.clang-format:
--------------------------------------------------------------------------------
1 | BasedOnStyle: Google
2 | DerivePointerAlignment: false
3 | PointerAlignment: Left
4 |
--------------------------------------------------------------------------------
/windows/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/
2 |
3 | # Visual Studio user-specific files.
4 | *.suo
5 | *.user
6 | *.userosscache
7 | *.sln.docstates
8 |
9 | # Visual Studio build-related files.
10 | x64/
11 | x86/
12 |
13 | # Visual Studio cache files
14 | # files ending in .cache can be ignored
15 | *.[Cc]ache
16 | # but keep track of directories ending in .cache
17 | !*.[Cc]ache/
18 |
--------------------------------------------------------------------------------
/windows/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.15)
2 | set(PROJECT_NAME "flutter_window_close")
3 | project(${PROJECT_NAME} LANGUAGES CXX)
4 |
5 | # This value is used when generating builds using this plugin, so it must
6 | # not be changed
7 | set(PLUGIN_NAME "flutter_window_close_plugin")
8 |
9 | add_library(${PLUGIN_NAME} SHARED
10 | "flutter_window_close_plugin.cpp"
11 | )
12 | apply_standard_settings(${PLUGIN_NAME})
13 | set_target_properties(${PLUGIN_NAME} PROPERTIES
14 | CXX_VISIBILITY_PRESET hidden)
15 | target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
16 | target_include_directories(${PLUGIN_NAME} INTERFACE
17 | "${CMAKE_CURRENT_SOURCE_DIR}/include")
18 | target_link_libraries(
19 | ${PLUGIN_NAME} PRIVATE
20 | flutter
21 | flutter_wrapper_plugin
22 | "comctl32.lib"
23 | )
24 |
25 | # List of absolute paths to libraries that should be bundled with the plugin
26 | set(flutter_window_close_bundled_libraries
27 | ""
28 | PARENT_SCOPE
29 | )
30 |
--------------------------------------------------------------------------------
/windows/flutter_window_close_plugin.cpp:
--------------------------------------------------------------------------------
1 | #include "include/flutter_window_close/flutter_window_close_plugin.h"
2 |
3 | #include
4 |
5 | // These headers must be present after |Windows.h|
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | namespace {
12 |
13 | class FlutterWindowClosePlugin : public flutter::Plugin {
14 | public:
15 | static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar);
16 |
17 | FlutterWindowClosePlugin(flutter::PluginRegistrarWindows* registrar);
18 |
19 | virtual ~FlutterWindowClosePlugin();
20 |
21 | HWND GetWindow();
22 |
23 | private:
24 | void HandleMethodCall(
25 | const flutter::MethodCall& method_call,
26 | std::unique_ptr> result);
27 |
28 | std::optional WindowProcDelegate(HWND hwnd, UINT message,
29 | WPARAM wparam, LPARAM lparam);
30 |
31 | flutter::PluginRegistrarWindows* registrar_;
32 | std::unique_ptr>
33 | notification_channel_;
34 | int64_t window_proc_delegate_id_ = -1;
35 | };
36 |
37 | void FlutterWindowClosePlugin::RegisterWithRegistrar(
38 | flutter::PluginRegistrarWindows* registrar) {
39 | auto plugin = std::make_unique(registrar);
40 | auto channel =
41 | std::make_unique>(
42 | registrar->messenger(), "flutter_window_close",
43 | &flutter::StandardMethodCodec::GetInstance());
44 | channel->SetMethodCallHandler(
45 | [plugin_pointer = plugin.get()](const auto& call, auto result) {
46 | plugin_pointer->HandleMethodCall(call, std::move(result));
47 | });
48 | registrar->AddPlugin(std::move(plugin));
49 | }
50 |
51 | FlutterWindowClosePlugin::FlutterWindowClosePlugin(
52 | flutter::PluginRegistrarWindows* registrar)
53 | : registrar_(registrar),
54 | notification_channel_(std::move(
55 | std::make_unique>(
56 | registrar->messenger(), "flutter_window_close_notification",
57 | &flutter::StandardMethodCodec::GetInstance()))) {}
58 |
59 | FlutterWindowClosePlugin::~FlutterWindowClosePlugin() {
60 | if (window_proc_delegate_id_ != -1) {
61 | registrar_->UnregisterTopLevelWindowProcDelegate(
62 | static_cast(window_proc_delegate_id_));
63 | }
64 | }
65 |
66 | HWND FlutterWindowClosePlugin::GetWindow() {
67 | return ::GetAncestor(registrar_->GetView()->GetNativeWindow(), GA_ROOT);
68 | }
69 |
70 | void FlutterWindowClosePlugin::HandleMethodCall(
71 | const flutter::MethodCall& method_call,
72 | std::unique_ptr> result) {
73 | if (method_call.method_name().compare("closeWindow") == 0) {
74 | ::PostMessage(GetWindow(), WM_CLOSE, 0, 0);
75 | result->Success(flutter::EncodableValue(nullptr));
76 | } else if (method_call.method_name().compare("destroyWindow") == 0) {
77 | ::DestroyWindow(GetWindow());
78 | result->Success(flutter::EncodableValue(nullptr));
79 | } else if (method_call.method_name().compare("init") == 0) {
80 | if (window_proc_delegate_id_ == -1) {
81 | window_proc_delegate_id_ = registrar_->RegisterTopLevelWindowProcDelegate(
82 | std::bind(&FlutterWindowClosePlugin::WindowProcDelegate, this,
83 | std::placeholders::_1, std::placeholders::_2,
84 | std::placeholders::_3, std::placeholders::_4));
85 | }
86 | result->Success(flutter::EncodableValue(nullptr));
87 | } else {
88 | result->NotImplemented();
89 | }
90 | }
91 |
92 | std::optional FlutterWindowClosePlugin::WindowProcDelegate(
93 | HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
94 | switch (message) {
95 | case WM_CLOSE: {
96 | notification_channel_->InvokeMethod(
97 | "onWindowClose", std::make_unique(nullptr));
98 | return 0;
99 | }
100 | }
101 | return std::nullopt;
102 | }
103 |
104 | } // namespace
105 |
106 | void FlutterWindowClosePluginRegisterWithRegistrar(
107 | FlutterDesktopPluginRegistrarRef registrar) {
108 | FlutterWindowClosePlugin::RegisterWithRegistrar(
109 | flutter::PluginRegistrarManager::GetInstance()
110 | ->GetRegistrar(registrar));
111 | }
112 |
--------------------------------------------------------------------------------
/windows/include/flutter_window_close/flutter_window_close_plugin.h:
--------------------------------------------------------------------------------
1 | #ifndef FLUTTER_PLUGIN_FLUTTER_WINDOW_CLOSE_PLUGIN_H_
2 | #define FLUTTER_PLUGIN_FLUTTER_WINDOW_CLOSE_PLUGIN_H_
3 |
4 | #include
5 |
6 | #ifdef FLUTTER_PLUGIN_IMPL
7 | #define FLUTTER_PLUGIN_EXPORT __declspec(dllexport)
8 | #else
9 | #define FLUTTER_PLUGIN_EXPORT __declspec(dllimport)
10 | #endif
11 |
12 | #if defined(__cplusplus)
13 | extern "C" {
14 | #endif
15 |
16 | FLUTTER_PLUGIN_EXPORT void FlutterWindowClosePluginRegisterWithRegistrar(
17 | FlutterDesktopPluginRegistrarRef registrar);
18 |
19 | #if defined(__cplusplus)
20 | } // extern "C"
21 | #endif
22 |
23 | #endif // FLUTTER_PLUGIN_FLUTTER_WINDOW_CLOSE_PLUGIN_H_
24 |
--------------------------------------------------------------------------------