Skip to content

Commit b8a6c78

Browse files
Migrate to macos_window_utils (#377)
* chore: bump Dart SDK version * add `.vscode/settings.json` to .gitignore * feat: add macos_window_utils * feat: wrap side in example with `TransparentMacOSSidebar` * feat: enable wallpaper tinting on content area * feat: adjust macOS window brightness depending on theme * feat: add a way to disable wallpaper tinting Wallpaper tinting is now also disabled whenever an overlay filter is used. * refactor: refactor wallpaper tinted area * fix: fix wallpaper tinting override being applied on overlay filter rebuild * fix: do not override wallpaper tinting if no `WallpaperTintingSettingsCubit` is found * refactor: stop relying on exceptions to check if `WallpaperTintingSettingsCubit` exists in widget tree * change: switch from BLoC to global wallpaper tinting settings * chore: remove unused imports * change: make canvas color mimic `NSWindow.windowBackgroundColor` * change: make wallpaper tinted area rebuild on layout change * feat: make toolbar wallpaper-tinted * doc: document `disableWallpaperTinting` * doc: improve documentation of `disableWallpaperTinting` Add link to #16296 and clarify that disabling wallpaper tinting is meant to be a temporary solution. * doc: document wallpaper tinted area * doc: document wallpaper tinting override * doc: document wallpaper tinting settings builder * doc: document wallpaper tinting settings data * change: make `numberOfWallpaperTintingOverrides` private * doc: document global wallpaper tinting settings * doc: fix typo * refactor: refactor wallpaper tinted area * feat: add `insertRepaintBoundary` property * change: enable `insertRepaintBoundary` in toolbar * change: enable `insertRepaintBoundary` in scaffold * change: export wallpaper tinted area * feat: enable wallpaper tinting on end sidebar * feat: add `sidebarState` property to window * fix: fix background color of end sidebar not matching canvas color when theme brightness does not match platform brightness * fix: fix wallpaper tinting override using `deactivate` instead of `dispose` * merge * change: comment out swift code that hides the toolbar in fullscreen mode * change: upgrade to macos_window_utils ^1.1.1 * feat: migrate window delegate to macos_window_utils * change: remove commented-out and unused Swift code * fix: fix sliver toolbar not having a backdrop filter * docs: document toolbar's `isVisible` property * docs: document sliver toolbar's `isVisible` property * docs: document sliver toolbar page's `isVisible` property * docs: document `_WallpaperTintedAreaOrBlurFilter` * refactor: remove unused `key` parameter * refactor: remove unused import * readme: update “Modern window look” * change: bump version * changelog: add entry for version 2.0.0 * changelog: add migration hint * merge: toolbar * fix: remove duplicate `debugFillProperties` * refactor: rename `pages` to `pageBuilders` * fix: remove unused import * fix: remove unused imports * change: remove unnecessary `NSWindowDelegate` from `MainFlutterWindow` * change: rename `isVisible` property to `allowWallpaperTintingOverrides` for toolbar * change: rename `isVisible` property to `allowWallpaperTintingOverrides` for sliver toolbar * fix: fix typo in comment * fix: fix `disableWallpaperTinting` having no effect * change: disable `VisualEffectSubviewContainer` when disabling wallpaper tinting in window * fix: fix some unit tests not passing Some unit tests were failing with the following message: “A Timer is still pending even after the widget tree was disposed.” The cause of that was that `VisualEffectSubviewContainer`, or more precisely `VisualEffectSubviewContainerWithGlobalKey` is using a timer to update its visual effect subview outside of the widget's `build` method. The issue is fixed by either disabling wallpaper tinting in the window (and therefore eliminating the use of `VisualEffectSubviewContainer`) or, in cases where that was impossible (such as the sidebar) running `await tester.pump(Duration.zero);` to allow the timer to complete. * remove unused import * change default canvas color `NSColor.windowBackgroundColor` was found to be inaccurate. The color has instead been changed to a color that was captured using the Digital Color Meter. * upgrade to macos_window_utils 1.1.2 * update “Modern window look” in readme for use with macos_window_utils 1.1.2 * change version to 2.0.0-beta.1 Co-authored-by: Reuben Turner <groovinchip@gmail.com> * add code formatting to documentation Highlighted `macos_ui` as a code element. Co-authored-by: Reuben Turner <groovinchip@gmail.com> * add missing comma to documentation Co-authored-by: Reuben Turner <groovinchip@gmail.com> * change version in changelog entry to 2.0.0-beta.1 Co-authored-by: Reuben Turner <groovinchip@gmail.com> * change version in pubspec.lock * format `window.dart` * replace `Colors.transparent` with `MacosColors.transparent` * document `WallpaperTintingSettingsBuilder` * document `WallpaperTintedArea` * implement `MacosWindowUtilsConfig` * export `src/macos_window_utils_config.dart` * use `MacosWindowUtilsConfig` in example * document `MacosWindowUtilsConfig` constructor * rename `_initMacosWindowUtils` to `_configureMacosWindowUtils` in example * document `MacosWindowUtilsConfig` usage in readme * replace opacity widget with repaint boundary * improve documentation for `sidebarState` * improve `MacosWindowUtilsConfig` documentation * fix inconsistencies introduced by merging * Update example/lib/main.dart * remove ”do” prefix from field names in `MacosWindowUtilsConfig` --------- Co-authored-by: Reuben Turner <groovinchip@gmail.com>
1 parent 5fc566c commit b8a6c78

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+992
-353
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## [2.0.0-beta.1]
2+
🚨 Breaking Changes 🚨
3+
* Migrate macos_ui to [macos_window_utils](https://pub.dev/packages/macos_window_utils), which provides the following benefits:
4+
* Window animation smoothness is drastically improved, particularly when miniaturizing and deminiaturizing the application window.
5+
* Some visual artifacts that occurred while the window was being (de)miniaturized (such as the application's shadow going missing) no longer occur.
6+
* The sidebar remains transparent when the app's brightness setting mismatches the OS setting.
7+
* Wallpaper tinting is now supported.
8+
9+
To migrate an existing application, please refer to the “Modern window look” section in the README.
10+
111
## [1.12.5]
212
* Fixed a bug where the `Sidebar.key` parameter wasn't used, which caused certain layouts to be unachievable.
313

README.md

Lines changed: 25 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -226,96 +226,42 @@ See the documentation for customizations and `ToolBar` examples.
226226
## Modern window look
227227

228228
A new look for macOS apps was introduced in Big Sur (macOS 11). To match that look
229-
in your Flutter app, like our screenshots, your `macos/Runner/MainFlutterWindow.swift`
230-
file should look like this:
229+
in your Flutter app, macos_ui relies on [macos_window_utils](https://pub.dev/packages/macos_window_utils), which requires a minimum macOS deployment target of 10.14.6. Therefore, make sure to open the `macos/Runner.xcworkspace` folder of your project using Xcode and search for `Runner.xcodeproj`. Go to `Info` > `Deployment Target` and set the `macOS Deployment Target` to `10.14.6` or above. Then, open your project's `Podfile` (if it doesn't show up in Xcode, you can find it in your project's `macos` directory via VS Code) and set the minimum deployment version in the first line to `10.14.6` or above:
231230

232-
```swift
233-
import Cocoa
234-
import FlutterMacOS
235-
236-
class BlurryContainerViewController: NSViewController {
237-
let flutterViewController = FlutterViewController()
238-
239-
init() {
240-
super.init(nibName: nil, bundle: nil)
241-
}
242-
243-
required init?(coder: NSCoder) {
244-
fatalError()
245-
}
231+
```podspec
232+
platform :osx, '10.14.6'
233+
```
246234

247-
override func loadView() {
248-
let blurView = NSVisualEffectView()
249-
blurView.autoresizingMask = [.width, .height]
250-
blurView.blendingMode = .behindWindow
251-
blurView.state = .active
252-
if #available(macOS 10.14, *) {
253-
blurView.material = .sidebar
254-
}
255-
self.view = blurView
256-
}
235+
Now, configure your window inside your `main()` as follows:
257236

258-
override func viewDidLoad() {
259-
super.viewDidLoad()
237+
```dart
238+
/// This method initializes macos_window_utils and styles the window.
239+
Future<void> _configureMacosWindowUtils() async {
240+
const config = MacosWindowUtilsConfig(
241+
toolbarStyle: NSWindowToolbarStyle.unified,
242+
);
243+
await config.apply();
244+
}
260245
261-
self.addChild(flutterViewController)
246+
void main() async {
247+
await _configureMacosWindowUtils();
262248
263-
flutterViewController.view.frame = self.view.bounds
264-
flutterViewController.backgroundColor = .clear // **Required post-Flutter 3.7.0**
265-
flutterViewController.view.autoresizingMask = [.width, .height]
266-
self.view.addSubview(flutterViewController.view)
267-
}
249+
runApp(const MacosUIGalleryApp());
268250
}
251+
```
269252

270-
class MainFlutterWindow: NSWindow, NSWindowDelegate {
271-
override func awakeFromNib() {
272-
delegate = self
273-
let blurryContainerViewController = BlurryContainerViewController()
274-
let windowFrame = self.frame
275-
self.contentViewController = blurryContainerViewController
276-
self.setFrame(windowFrame, display: true)
277-
278-
if #available(macOS 10.13, *) {
279-
let customToolbar = NSToolbar()
280-
customToolbar.showsBaselineSeparator = false
281-
self.toolbar = customToolbar
282-
}
283-
self.titleVisibility = .hidden
284-
self.titlebarAppearsTransparent = true
285-
if #available(macOS 11.0, *) {
286-
// Use .expanded if the app will have a title bar, else use .unified
287-
self.toolbarStyle = .unified
288-
}
289-
290-
self.isMovableByWindowBackground = true
291-
self.styleMask.insert(NSWindow.StyleMask.fullSizeContentView)
292-
293-
self.isOpaque = false
294-
self.backgroundColor = .clear
295-
296-
RegisterGeneratedPlugins(registry: blurryContainerViewController.flutterViewController)
297-
298-
super.awakeFromNib()
299-
}
253+
Please note that if you are using a title bar (`TitleBar`) in your `MacosWindow`, you should set the `toolbarStyle` of your window to `NSWindowToolbarStyle.expanded`, in order to properly align the close, minimize, zoom window buttons:
300254

301-
func window(_ window: NSWindow, willUseFullScreenPresentationOptions proposedOptions: NSApplication.PresentationOptions = []) -> NSApplication.PresentationOptions {
302-
return [.autoHideToolbar, .autoHideMenuBar, .fullScreen]
303-
}
304-
305-
func windowWillEnterFullScreen(_ notification: Notification) {
306-
self.toolbar?.isVisible = false
307-
}
308-
309-
func windowDidExitFullScreen(_ notification: Notification) {
310-
self.toolbar?.isVisible = true
311-
}
255+
```dart
256+
Future<void> _configureMacosWindowUtils() async {
257+
const config = MacosWindowUtilsConfig(
258+
toolbarStyle: NSWindowToolbarStyle.expanded,
259+
);
260+
await config.apply();
312261
}
313-
314262
```
315263

316-
See [this issue comment](https://github.com/flutter/flutter/issues/59969#issuecomment-916682559) for more details on the new look and explanations for how it works.
317-
318-
Please note that if you are using a title bar (`TitleBar`) in your `MacosWindow`, you should set the `toolbarStyle` of NSWindow to `.expanded`, in order to properly align the close, minimize, zoom window buttons. In any other case, you should keep it as `.unified`. This must be set beforehand, i.e. it cannot be switched in runtime.
264+
In any other case, you should keep it as `NSWindowToolbarStyle.unified`.
319265

320266
## ToolBar
321267

example/lib/main.dart

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,15 @@ import 'package:provider/provider.dart';
1313

1414
import 'theme.dart';
1515

16-
void main() {
16+
/// This method initializes macos_window_utils and styles the window.
17+
Future<void> _configureMacosWindowUtils() async {
18+
const config = MacosWindowUtilsConfig();
19+
await config.apply();
20+
}
21+
22+
Future<void> main() async {
23+
await _configureMacosWindowUtils();
24+
1725
runApp(const MacosUIGalleryApp());
1826
}
1927

@@ -55,23 +63,25 @@ class _WidgetGalleryState extends State<WidgetGallery> {
5563

5664
late final searchFieldController = TextEditingController();
5765

58-
final List<Widget> pages = [
59-
CupertinoTabView(
60-
builder: (_) => const ButtonsPage(),
61-
),
62-
const IndicatorsPage(),
63-
const FieldsPage(),
64-
const ColorsPage(),
65-
const Center(
66-
child: MacosIcon(
67-
CupertinoIcons.add,
68-
),
69-
),
70-
const DialogsPage(),
71-
const ToolbarPage(),
72-
const SliverToolbarPage(),
73-
const TabViewPage(),
74-
const SelectorsPage(),
66+
final List<Widget Function(bool)> pageBuilders = [
67+
(bool isVisible) => CupertinoTabView(
68+
builder: (_) => const ButtonsPage(),
69+
),
70+
(bool isVisible) => const IndicatorsPage(),
71+
(bool isVisible) => const FieldsPage(),
72+
(bool isVisible) => const ColorsPage(),
73+
(bool isVisible) => const Center(
74+
child: MacosIcon(
75+
CupertinoIcons.add,
76+
),
77+
),
78+
(bool isVisible) => const DialogsPage(),
79+
(bool isVisible) => const ToolbarPage(),
80+
(bool isVisible) => SliverToolbarPage(
81+
isVisible: isVisible,
82+
),
83+
(bool isVisible) => const TabViewPage(),
84+
(bool isVisible) => const SelectorsPage(),
7585
];
7686

7787
@override
@@ -291,7 +301,14 @@ class _WidgetGalleryState extends State<WidgetGallery> {
291301
),
292302
child: IndexedStack(
293303
index: pageIndex,
294-
children: pages,
304+
children: pageBuilders
305+
.asMap()
306+
.map((index, builder) {
307+
final widget = builder(index == pageIndex);
308+
return MapEntry(index, widget);
309+
})
310+
.values
311+
.toList(),
295312
),
296313
),
297314
);

example/lib/pages/sliver_toolbar_page.dart

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,27 @@ import 'package:flutter/material.dart';
33
import 'package:macos_ui/macos_ui.dart';
44

55
class SliverToolbarPage extends StatefulWidget {
6-
const SliverToolbarPage({super.key});
6+
const SliverToolbarPage({super.key, required this.isVisible});
7+
8+
/// Whether this [SliverToolbarPage] is currently visible on the screen
9+
/// (that is, not e.g. hidden by an [IndexedStack]).
10+
///
11+
/// By default, macos_ui applies wallpaper tinting to the application's
12+
/// window to match macOS' native appearance:
13+
///
14+
/// <img src="https://user-images.githubusercontent.com/86920182/220182724-d78319d7-5c41-4e8c-b785-a73a6ea24927.jpg" width=640/>
15+
///
16+
/// However, this effect is realized by inserting `NSVisualEffectView`s behind
17+
/// Flutter's canvas and turning the background of areas that are meant to be
18+
/// affected by wallpaper tinting transparent. Since Flutter's
19+
/// [`ImageFilter.blur`](https://api.flutter.dev/flutter/dart-ui/ImageFilter/ImageFilter.blur.html)
20+
/// does not support transparency, wallpaper tinting is disabled automatically
21+
/// when this widget's [isVisible] is true.
22+
///
23+
/// This is meant to be a temporary solution until
24+
/// [#16296](https://github.com/flutter/flutter/issues/16296) is resolved in
25+
/// the Flutter project.
26+
final bool isVisible;
727

828
@override
929
State<SliverToolbarPage> createState() => _SliverToolbarPageState();
@@ -27,6 +47,7 @@ class _SliverToolbarPageState extends State<SliverToolbarPage> {
2747
floating: floating,
2848
pinned: pinned,
2949
toolbarOpacity: opacity,
50+
allowWallpaperTintingOverrides: widget.isVisible,
3051
actions: [
3152
ToolBarIconButton(
3253
label: 'Pinned',

example/macos/Flutter/GeneratedPluginRegistrant.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import FlutterMacOS
66
import Foundation
77

88
import macos_ui
9+
import macos_window_utils
910

1011
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
1112
MacOSUiPlugin.register(with: registry.registrar(forPlugin: "MacOSUiPlugin"))
13+
MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin"))
1214
}

example/macos/Podfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
platform :osx, '10.14'
1+
platform :osx, '10.14.6'
22

33
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
44
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

example/macos/Podfile.lock

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,27 @@ PODS:
22
- FlutterMacOS (1.0.0)
33
- macos_ui (0.1.0):
44
- FlutterMacOS
5+
- macos_window_utils (1.0.0):
6+
- FlutterMacOS
57

68
DEPENDENCIES:
79
- FlutterMacOS (from `Flutter/ephemeral`)
810
- macos_ui (from `Flutter/ephemeral/.symlinks/plugins/macos_ui/macos`)
11+
- macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`)
912

1013
EXTERNAL SOURCES:
1114
FlutterMacOS:
1215
:path: Flutter/ephemeral
1316
macos_ui:
1417
:path: Flutter/ephemeral/.symlinks/plugins/macos_ui/macos
18+
macos_window_utils:
19+
:path: Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos
1520

1621
SPEC CHECKSUMS:
1722
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
1823
macos_ui: 6229a8922cd97bafb7d9636c8eb8dfb0744183ca
24+
macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663
1925

20-
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
26+
PODFILE CHECKSUM: ff0a9a3ce75ee73f200ca7e2f47745698c917ef9
2127

2228
COCOAPODS: 1.11.3

example/macos/Runner.xcodeproj/project.pbxproj

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
6262
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
6363
33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
64-
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
64+
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; tabWidth = 2; };
6565
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
6666
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
6767
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
@@ -94,7 +94,6 @@
9494
94ECD9F878BC8EB5F0E7094E /* Pods-Runner.release.xcconfig */,
9595
AFB798A3289226D0E5AB9985 /* Pods-Runner.profile.xcconfig */,
9696
);
97-
name = Pods;
9897
path = Pods;
9998
sourceTree = "<group>";
10099
};
@@ -405,7 +404,7 @@
405404
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
406405
GCC_WARN_UNUSED_FUNCTION = YES;
407406
GCC_WARN_UNUSED_VARIABLE = YES;
408-
MACOSX_DEPLOYMENT_TARGET = 10.14;
407+
MACOSX_DEPLOYMENT_TARGET = 10.14.6;
409408
MTL_ENABLE_DEBUG_INFO = NO;
410409
SDKROOT = macosx;
411410
SWIFT_COMPILATION_MODE = wholemodule;
@@ -484,7 +483,7 @@
484483
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
485484
GCC_WARN_UNUSED_FUNCTION = YES;
486485
GCC_WARN_UNUSED_VARIABLE = YES;
487-
MACOSX_DEPLOYMENT_TARGET = 10.14;
486+
MACOSX_DEPLOYMENT_TARGET = 10.14.6;
488487
MTL_ENABLE_DEBUG_INFO = YES;
489488
ONLY_ACTIVE_ARCH = YES;
490489
SDKROOT = macosx;
@@ -531,7 +530,7 @@
531530
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
532531
GCC_WARN_UNUSED_FUNCTION = YES;
533532
GCC_WARN_UNUSED_VARIABLE = YES;
534-
MACOSX_DEPLOYMENT_TARGET = 10.14;
533+
MACOSX_DEPLOYMENT_TARGET = 10.14.6;
535534
MTL_ENABLE_DEBUG_INFO = NO;
536535
SDKROOT = macosx;
537536
SWIFT_COMPILATION_MODE = wholemodule;

0 commit comments

Comments
 (0)