Skip to content

Commit 4c0a239

Browse files
committed
Refactor multiple window example for macOS and update config
Replaces MainFlutterWindow.swift with NSWindow+Swizzle.swift for window customization via method swizzling. Updates AppDelegate to initialize FlutterEngine and enable swizzling. Refactors main.dart to support multiple windows and updates project files, Xcode scheme, and workspace for new structure. Adds nativeapi dependency and cleans up pubspec.yaml. Removes pubspec.lock and updates plugin registration for FFI plugins.
1 parent 1387b89 commit 4c0a239

13 files changed

Lines changed: 193 additions & 419 deletions

File tree

examples/multiple_window_example/lib/main.dart

Lines changed: 64 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
// ignore_for_file: invalid_use_of_internal_member, implementation_imports
2+
13
import 'package:flutter/material.dart';
4+
import 'package:flutter/src/widgets/_window.dart';
5+
import 'package:nativeapi/nativeapi.dart';
26

37
void main() {
48
runApp(const MyApp());
@@ -11,111 +15,82 @@ class MyApp extends StatelessWidget {
1115
@override
1216
Widget build(BuildContext context) {
1317
return MaterialApp(
14-
title: 'Flutter Demo',
18+
title: 'Multiple Window Example',
1519
theme: ThemeData(
16-
// This is the theme of your application.
17-
//
18-
// TRY THIS: Try running your application with "flutter run". You'll see
19-
// the application has a purple toolbar. Then, without quitting the app,
20-
// try changing the seedColor in the colorScheme below to Colors.green
21-
// and then invoke "hot reload" (save your changes or press the "hot
22-
// reload" button in a Flutter-supported IDE, or press "r" if you used
23-
// the command line to start the app).
24-
//
25-
// Notice that the counter didn't reset back to zero; the application
26-
// state is not lost during the reload. To reset the state, use hot
27-
// restart instead.
28-
//
29-
// This works for code too, not just values: Most code changes can be
30-
// tested with just a hot reload.
31-
colorScheme: .fromSeed(seedColor: Colors.deepPurple),
20+
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
3221
),
33-
home: const MyHomePage(title: 'Flutter Demo Home Page'),
22+
home: ViewCollection(views: [PrimaryWindow(), SecondaryWindow()]),
3423
);
3524
}
3625
}
3726

38-
class MyHomePage extends StatefulWidget {
39-
const MyHomePage({super.key, required this.title});
40-
41-
// This widget is the home page of your application. It is stateful, meaning
42-
// that it has a State object (defined below) that contains fields that affect
43-
// how it looks.
27+
class PrimaryWindow extends StatefulWidget {
28+
const PrimaryWindow({super.key});
4429

45-
// This class is the configuration for the state. It holds the values (in this
46-
// case the title) provided by the parent (in this case the App widget) and
47-
// used by the build method of the State. Fields in a Widget subclass are
48-
// always marked "final".
30+
@override
31+
State<PrimaryWindow> createState() => _PrimaryWindowState();
32+
}
4933

50-
final String title;
34+
class _PrimaryWindowState extends State<PrimaryWindow> {
35+
final _windowController = RegularWindowController(
36+
preferredSize: const Size(800, 600),
37+
title: 'Primary Window',
38+
);
5139

5240
@override
53-
State<MyHomePage> createState() => _MyHomePageState();
41+
Widget build(BuildContext context) {
42+
return RegularWindow(
43+
controller: _windowController,
44+
child: Scaffold(
45+
appBar: AppBar(title: const Text('Primary Window')),
46+
body: Center(
47+
child: Column(
48+
children: [
49+
FilledButton(
50+
onPressed: () {
51+
Window? primaryWindow;
52+
final windows = WindowManager.instance.getAll();
53+
for (var window in windows) {
54+
if (window.title == 'Primary Window') {
55+
primaryWindow = window;
56+
break;
57+
}
58+
}
59+
if (primaryWindow != null) {
60+
primaryWindow.setSize(1000, 1000);
61+
primaryWindow.show();
62+
}
63+
},
64+
child: const Text('A Window'),
65+
),
66+
],
67+
),
68+
),
69+
),
70+
);
71+
}
5472
}
5573

56-
class _MyHomePageState extends State<MyHomePage> {
57-
int _counter = 0;
74+
class SecondaryWindow extends StatefulWidget {
75+
const SecondaryWindow({super.key});
5876

59-
void _incrementCounter() {
60-
setState(() {
61-
// This call to setState tells the Flutter framework that something has
62-
// changed in this State, which causes it to rerun the build method below
63-
// so that the display can reflect the updated values. If we changed
64-
// _counter without calling setState(), then the build method would not be
65-
// called again, and so nothing would appear to happen.
66-
_counter++;
67-
});
68-
}
77+
@override
78+
State<SecondaryWindow> createState() => _SecondaryWindowState();
79+
}
80+
81+
class _SecondaryWindowState extends State<SecondaryWindow> {
82+
final _windowController = RegularWindowController(
83+
preferredSize: const Size(800, 600),
84+
title: 'Secondary Window',
85+
);
6986

7087
@override
7188
Widget build(BuildContext context) {
72-
// This method is rerun every time setState is called, for instance as done
73-
// by the _incrementCounter method above.
74-
//
75-
// The Flutter framework has been optimized to make rerunning build methods
76-
// fast, so that you can just rebuild anything that needs updating rather
77-
// than having to individually change instances of widgets.
78-
return Scaffold(
79-
appBar: AppBar(
80-
// TRY THIS: Try changing the color here to a specific color (to
81-
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
82-
// change color while the other colors stay the same.
83-
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
84-
// Here we take the value from the MyHomePage object that was created by
85-
// the App.build method, and use it to set our appbar title.
86-
title: Text(widget.title),
87-
),
88-
body: Center(
89-
// Center is a layout widget. It takes a single child and positions it
90-
// in the middle of the parent.
91-
child: Column(
92-
// Column is also a layout widget. It takes a list of children and
93-
// arranges them vertically. By default, it sizes itself to fit its
94-
// children horizontally, and tries to be as tall as its parent.
95-
//
96-
// Column has various properties to control how it sizes itself and
97-
// how it positions its children. Here we use mainAxisAlignment to
98-
// center the children vertically; the main axis here is the vertical
99-
// axis because Columns are vertical (the cross axis would be
100-
// horizontal).
101-
//
102-
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
103-
// action in the IDE, or press "p" in the console), to see the
104-
// wireframe for each widget.
105-
mainAxisAlignment: .center,
106-
children: [
107-
const Text('You have pushed the button this many times:'),
108-
Text(
109-
'$_counter',
110-
style: Theme.of(context).textTheme.headlineMedium,
111-
),
112-
],
113-
),
114-
),
115-
floatingActionButton: FloatingActionButton(
116-
onPressed: _incrementCounter,
117-
tooltip: 'Increment',
118-
child: const Icon(Icons.add),
89+
return RegularWindow(
90+
controller: _windowController,
91+
child: Scaffold(
92+
appBar: AppBar(title: const Text('Secondary Window')),
93+
body: const Center(child: Text('Secondary Window')),
11994
),
12095
);
12196
}

examples/multiple_window_example/linux/flutter/generated_plugins.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
66
)
77

88
list(APPEND FLUTTER_FFI_PLUGIN_LIST
9+
cnativeapi
910
)
1011

1112
set(PLUGIN_BUNDLED_LIBRARIES)

examples/multiple_window_example/macos/Runner.xcodeproj/project.pbxproj

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
2727
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
2828
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
29-
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
29+
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
30+
F52B082E2EBF40BA00DF8AA8 /* NSWindow+Swizzle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F52B082D2EBF40B500DF8AA8 /* NSWindow+Swizzle.swift */; };
3031
/* End PBXBuildFile section */
3132

3233
/* Begin PBXContainerItemProxy section */
@@ -64,20 +65,21 @@
6465
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
6566
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
6667
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
67-
33CC10ED2044A3C60003C045 /* multiple_window_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "multiple_window_example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
68+
33CC10ED2044A3C60003C045 /* multiple_window_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = multiple_window_example.app; sourceTree = BUILT_PRODUCTS_DIR; };
6869
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
6970
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
7071
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
7172
33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
72-
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
7373
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
7474
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
7575
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
7676
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
7777
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
7878
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
79+
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
7980
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
8081
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
82+
F52B082D2EBF40B500DF8AA8 /* NSWindow+Swizzle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWindow+Swizzle.swift"; sourceTree = "<group>"; };
8183
/* End PBXFileReference section */
8284

8385
/* Begin PBXFrameworksBuildPhase section */
@@ -92,6 +94,7 @@
9294
isa = PBXFrameworksBuildPhase;
9395
buildActionMask = 2147483647;
9496
files = (
97+
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
9598
);
9699
runOnlyForDeploymentPostprocessing = 0;
97100
};
@@ -124,7 +127,6 @@
124127
33CEB47122A05771004F2AC0 /* Flutter */,
125128
331C80D6294CF71000263BE5 /* RunnerTests */,
126129
33CC10EE2044A3C60003C045 /* Products */,
127-
D73912EC22F37F3D000D13A0 /* Frameworks */,
128130
);
129131
sourceTree = "<group>";
130132
};
@@ -151,6 +153,7 @@
151153
33CEB47122A05771004F2AC0 /* Flutter */ = {
152154
isa = PBXGroup;
153155
children = (
156+
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */,
154157
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
155158
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
156159
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
@@ -162,8 +165,8 @@
162165
33FAB671232836740065AC1E /* Runner */ = {
163166
isa = PBXGroup;
164167
children = (
168+
F52B082D2EBF40B500DF8AA8 /* NSWindow+Swizzle.swift */,
165169
33CC10F02044A3C60003C045 /* AppDelegate.swift */,
166-
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
167170
33E51913231747F40026EE4D /* DebugProfile.entitlements */,
168171
33E51914231749380026EE4D /* Release.entitlements */,
169172
33CC11242044D66E0003C045 /* Resources */,
@@ -172,13 +175,6 @@
172175
path = Runner;
173176
sourceTree = "<group>";
174177
};
175-
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
176-
isa = PBXGroup;
177-
children = (
178-
);
179-
name = Frameworks;
180-
sourceTree = "<group>";
181-
};
182178
/* End PBXGroup section */
183179

184180
/* Begin PBXNativeTarget section */
@@ -216,6 +212,9 @@
216212
33CC11202044C79F0003C045 /* PBXTargetDependency */,
217213
);
218214
name = Runner;
215+
packageProductDependencies = (
216+
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
217+
);
219218
productName = Runner;
220219
productReference = 33CC10ED2044A3C60003C045 /* multiple_window_example.app */;
221220
productType = "com.apple.product-type.application";
@@ -260,6 +259,9 @@
260259
Base,
261260
);
262261
mainGroup = 33CC10E42044A3C60003C045;
262+
packageReferences = (
263+
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
264+
);
263265
productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
264266
projectDirPath = "";
265267
projectRoot = "";
@@ -344,7 +346,7 @@
344346
isa = PBXSourcesBuildPhase;
345347
buildActionMask = 2147483647;
346348
files = (
347-
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
349+
F52B082E2EBF40BA00DF8AA8 /* NSWindow+Swizzle.swift in Sources */,
348350
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
349351
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
350352
);
@@ -700,6 +702,20 @@
700702
defaultConfigurationName = Release;
701703
};
702704
/* End XCConfigurationList section */
705+
706+
/* Begin XCLocalSwiftPackageReference section */
707+
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
708+
isa = XCLocalSwiftPackageReference;
709+
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
710+
};
711+
/* End XCLocalSwiftPackageReference section */
712+
713+
/* Begin XCSwiftPackageProductDependency section */
714+
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
715+
isa = XCSwiftPackageProductDependency;
716+
productName = FlutterGeneratedPluginSwiftPackage;
717+
};
718+
/* End XCSwiftPackageProductDependency section */
703719
};
704720
rootObject = 33CC10E52044A3C60003C045 /* Project object */;
705721
}

examples/multiple_window_example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,24 @@
55
<BuildAction
66
parallelizeBuildables = "YES"
77
buildImplicitDependencies = "YES">
8+
<PreActions>
9+
<ExecutionAction
10+
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
11+
<ActionContent
12+
title = "Run Prepare Flutter Framework Script"
13+
scriptText = "&quot;$FLUTTER_ROOT&quot;/packages/flutter_tools/bin/macos_assemble.sh prepare&#10;">
14+
<EnvironmentBuildable>
15+
<BuildableReference
16+
BuildableIdentifier = "primary"
17+
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
18+
BuildableName = "multiple_window_example.app"
19+
BlueprintName = "Runner"
20+
ReferencedContainer = "container:Runner.xcodeproj">
21+
</BuildableReference>
22+
</EnvironmentBuildable>
23+
</ActionContent>
24+
</ExecutionAction>
25+
</PreActions>
826
<BuildActionEntries>
927
<BuildActionEntry
1028
buildForTesting = "YES"

examples/multiple_window_example/macos/Runner.xcworkspace/contents.xcworkspacedata

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
15
import Cocoa
26
import FlutterMacOS
37

48
@main
59
class AppDelegate: FlutterAppDelegate {
6-
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
10+
override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
711
return true
812
}
913

10-
override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
11-
return true
14+
var engine: FlutterEngine?
15+
16+
override func applicationWillFinishLaunching(_ notification: Notification) {
17+
NSWindow.enableSwizzling()
18+
}
19+
20+
override func applicationDidFinishLaunching(_ notification: Notification) {
21+
engine = FlutterEngine(name: "project", project: nil)
22+
engine?.run(withEntrypoint:nil)
1223
}
1324
}

0 commit comments

Comments
 (0)