Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
6b60503
Add the useWindowing flag to the MaterialApp
mattkae Feb 2, 2026
569237b
Provide the windowing configuraton to MaterialApp descendents internally
mattkae Feb 2, 2026
65606bc
Getting somewhere
mattkae Feb 2, 2026
a3b5ae4
Cleaning up the implementation
mattkae Feb 3, 2026
42c4f88
Reset
mattkae Feb 3, 2026
551b874
Fix error
mattkae Feb 3, 2026
c3ab6d8
Falling back to the non-windowed implementation when dialogs are not …
mattkae Feb 3, 2026
030576a
Implement proper documentation
mattkae Feb 3, 2026
bb6c715
Fullscreen example
mattkae Feb 3, 2026
a577cee
Adding tests
mattkae Feb 3, 2026
133dbaa
Add checksum
mattkae Feb 3, 2026
8c2e1ca
Add checksum again
mattkae Feb 3, 2026
c3fb870
Remove useWindowing flag from MaterialApp in favor of using the windo…
mattkae Feb 4, 2026
d914b44
Marking certain APIs as experimental, throwing where necessary
mattkae Feb 4, 2026
21a0e53
Genericizing the WindowRegistry
mattkae Feb 6, 2026
731445f
PR feedback
mattkae Feb 6, 2026
dd3dee8
Fix missing doc directive
mattkae Feb 9, 2026
dbccf37
Significantly improving the WindowManager API
mattkae Feb 11, 2026
35a1d33
PR feedback
mattkae Feb 11, 2026
4cf5571
Fixing some formatting issues
mattkae Feb 17, 2026
44e490d
Fix import issues
mattkae Feb 17, 2026
308c8f0
Conditionally providing the WindowManager
mattkae Feb 17, 2026
3066811
More linux analyze shenanigans
mattkae Feb 17, 2026
2584c84
Skipping api_multiwindow build explicitly
mattkae Feb 19, 2026
ff3aec7
Making WindowRegistry.maybeOf NOT throw when windowing is unavailable…
mattkae Feb 19, 2026
44109dd
Minor pull request feedback
mattkae Feb 20, 2026
4a40467
Merge branch 'master' into feature/material_dialogs_multi_window
mattkae Feb 24, 2026
9302ca5
Moving usafe of windowing internals to the widgets API
mattkae Feb 27, 2026
c9dc047
Revert "Moving usafe of windowing internals to the widgets API"
mattkae Feb 27, 2026
513fba5
Re-add moving to the WidgetApp and fixing the Hero issue
mattkae Feb 27, 2026
07b4992
showRawDialog
justinmc Feb 28, 2026
e3bbe32
Cleanup
justinmc Feb 28, 2026
fee18f5
Material dialogs multi window widgets (#66)
mattkae Mar 2, 2026
3ac0c26
Reset app.dart
mattkae Mar 2, 2026
184301a
Re-add newline
mattkae Mar 2, 2026
03c25de
feedback
mattkae Mar 3, 2026
c5d95a5
Breaking out the WindowManager exampleinto its own file
mattkae Mar 3, 2026
937f67d
More feedback + fixing linux analyze
mattkae Mar 3, 2026
785f0cc
Remove api_multiwindow
mattkae Mar 3, 2026
a850d58
Pull request feedback
mattkae Mar 3, 2026
684ea47
Add missing imports
mattkae Mar 3, 2026
dddd1bf
Simplifying the showRawDialog method
mattkae Mar 3, 2026
280e795
Remove unused import
mattkae Mar 3, 2026
8e3ebd8
Merge branch 'master' into feature/material_dialogs_multi_window
mattkae Mar 4, 2026
ee80c0d
PR feedback
mattkae Mar 4, 2026
76159c2
Fixes
mattkae Mar 4, 2026
47667fe
Linux analyze
mattkae Mar 4, 2026
8054c49
Fixing context issues
mattkae Mar 4, 2026
b8cb64a
Whoops, forgot this
mattkae Mar 4, 2026
6b652eb
Fix for context
mattkae Mar 4, 2026
c823f80
PR feedback
mattkae Mar 5, 2026
eb2e05b
Making less of a diff
mattkae Mar 5, 2026
d38fa48
Fix dart doc
mattkae Mar 5, 2026
6eb1053
Rename RouteBuilder to DialogRouteBuilder to avoid collisions
mattkae Mar 5, 2026
39d60ec
Merge branch 'master' into feature/material_dialogs_multi_window
mattkae Mar 5, 2026
6538a43
Rename DialogRouteBuilder to RawDialogRouteBuilder
mattkae Mar 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions examples/api/lib/widgets/windows/window_manager.0.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// TODO(mattkae): remove invalid_use_of_internal_member ignore comment when this API is stable.
// See: https://github.com/flutter/flutter/issues/177586
// ignore_for_file: invalid_use_of_internal_member
// ignore_for_file: implementation_imports
import 'package:flutter/widgets.dart';
import 'package:flutter/src/widgets/_window.dart';

void main() {
try {
WidgetsFlutterBinding.ensureInitialized();
final RegularWindowController controller = RegularWindowController(
preferredSize: const Size(800, 600),
);
runWidget(
WindowManager(
child: RegularWindow(controller: controller, child: const MainWindow()),
),
);
} on UnsupportedError catch (e) {
// TODO(mattkae): Remove this catch block when windowing is supported in tests.
// For now, we need to catch the error so that the API smoke tests pass.
runApp(
WidgetsApp(
color: const Color(0xFFFFFFFF),
builder: (BuildContext context, Widget? child) {
return Center(
child: Text(
e.message ?? 'Unsupported',
textDirection: TextDirection.ltr,
),
);
},
),
);
}
}

class MainWindow extends StatefulWidget {
const MainWindow({super.key});

@override
State<StatefulWidget> createState() => MainWindowState();
}

class MainWindowState extends State<MainWindow> {
WindowEntry? entry;

void _openDialog(BuildContext context) {
final WindowRegistry? registry = WindowRegistry.maybeOf(context);
assert(registry != null);
entry = WindowEntry(
controller: DialogWindowController(
parent: WindowScope.of(context),
preferredSize: const Size(400, 300),
delegate: _DialogWindowControllerDelegate(
mainWindow: this,
registry: registry!,
),
),
builder: (BuildContext context) {
return const SizedBox.shrink();
},
);
registry.register(entry!);
}

void closeDialog(WindowRegistry registry) {
if (entry != null) {
registry.unregister(entry!);
entry = null;
}
}

@override
Widget build(BuildContext context) {
return WidgetsApp(
color: const Color(0xFFFFFFFF),
builder: (BuildContext context, Widget? child) {
return Center(
child: GestureDetector(
onTap: () => _openDialog(context),
child: const Text(
'Open a dialog',
textDirection: TextDirection.ltr,
),
),
);
},
);
}
}

class _DialogWindowControllerDelegate extends DialogWindowControllerDelegate {
_DialogWindowControllerDelegate({
required this.mainWindow,
required this.registry,
});

final MainWindowState mainWindow;
final WindowRegistry registry;

@override
void onWindowDestroyed() {
super.onWindowDestroyed();
mainWindow.closeDialog(registry);
}
}
15 changes: 15 additions & 0 deletions examples/api/test/widgets/windows/window_manager.0_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter_api_samples/widgets/windows/window_manager.0.dart'
as example;
import 'package:flutter_test/flutter_test.dart';

void main() {
testWidgets('Calling window_manager main returns normally', (
WidgetTester tester,
) async {
expect(() => example.main(), returnsNormally);
});
Comment on lines +10 to +14

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to pump the example and expect that "Unsupported" is shown?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left it as "returns normally" so that when we "turn on" multi-window, it will just work

}
4 changes: 3 additions & 1 deletion examples/multiple_windows/lib/app/dialog_window_content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ class DialogWindowContent extends StatelessWidget {

@override
Widget build(BuildContext context) {
final WindowManager windowManager = WindowManagerAccessor.of(context);
final KeyedWindowManager windowManager = KeyedWindowManagerAccessor.of(
context,
);
final WindowSettings windowSettings = WindowSettingsAccessor.of(context);

final child = FocusScope(
Expand Down
17 changes: 13 additions & 4 deletions examples/multiple_windows/lib/app/main_window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ class MainWindow extends StatelessWidget {

@override
Widget build(BuildContext context) {
final WindowManager windowManager = WindowManagerAccessor.of(context);
final KeyedWindowManager windowManager = KeyedWindowManagerAccessor.of(
context,
);

return ViewAnchor(
view: ListenableBuilder(
Expand Down Expand Up @@ -81,7 +83,10 @@ class MainWindow extends StatelessWidget {
}

class _WindowsTable extends StatelessWidget {
List<DataRow> _buildRows(WindowManager windowManager, BuildContext context) {
List<DataRow> _buildRows(
KeyedWindowManager windowManager,
BuildContext context,
) {
List<DataRow> rows = [];
for (KeyedWindow controller in windowManager.windows) {
rows.add(
Expand Down Expand Up @@ -150,7 +155,9 @@ class _WindowsTable extends StatelessWidget {

@override
Widget build(BuildContext context) {
final WindowManager windowManager = WindowManagerAccessor.of(context);
final KeyedWindowManager windowManager = KeyedWindowManagerAccessor.of(
context,
);
return DataTable(
showBottomBorder: true,
columns: const [
Expand All @@ -176,7 +183,9 @@ class _WindowsTable extends StatelessWidget {
class _WindowCreatorCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final WindowManager windowManager = WindowManagerAccessor.of(context);
final KeyedWindowManager windowManager = KeyedWindowManagerAccessor.of(
context,
);
final WindowSettings windowSettings = WindowSettingsAccessor.of(context);
final BaseWindowController windowController = WindowScope.of(context);

Expand Down
20 changes: 10 additions & 10 deletions examples/multiple_windows/lib/app/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ class KeyedWindow {
/// The window manager manages a flat list of all of the [BaseWindowController]s
/// that have been created by the application as well as which controller is
/// currently selected by the UI.
class WindowManager extends ChangeNotifier {
WindowManager({required List<KeyedWindow> initialWindows})
class KeyedWindowManager extends ChangeNotifier {
KeyedWindowManager({required List<KeyedWindow> initialWindows})
: _windows = initialWindows;

final List<KeyedWindow> _windows;
Expand All @@ -61,18 +61,18 @@ class WindowManager extends ChangeNotifier {
}
}

/// Provides access to the [WindowManager] from the widget tree.
class WindowManagerAccessor extends InheritedNotifier<WindowManager> {
const WindowManagerAccessor({
/// Provides access to the [KeyedWindowManager] from the widget tree.
class KeyedWindowManagerAccessor extends InheritedNotifier<KeyedWindowManager> {
const KeyedWindowManagerAccessor({
super.key,
required super.child,
required WindowManager windowManager,
required KeyedWindowManager windowManager,
}) : super(notifier: windowManager);

static WindowManager of(BuildContext context) {
final WindowManagerAccessor? result = context
.dependOnInheritedWidgetOfExactType<WindowManagerAccessor>();
assert(result != null, 'No WindowManager found in context');
static KeyedWindowManager of(BuildContext context) {
final KeyedWindowManagerAccessor? result = context
.dependOnInheritedWidgetOfExactType<KeyedWindowManagerAccessor>();
assert(result != null, 'No KeyedWindowManager found in context');
return result!.notifier!;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ class RegularWindowContent extends StatelessWidget {
Widget build(BuildContext context) {
final dpr = MediaQuery.of(context).devicePixelRatio;
final windowSize = WindowScope.contentSizeOf(context);
final WindowManager windowManager = WindowManagerAccessor.of(context);
final KeyedWindowManager windowManager = KeyedWindowManagerAccessor.of(
context,
);
final WindowSettings windowSettings = WindowSettingsAccessor.of(context);

final child = Scaffold(
Expand Down
6 changes: 4 additions & 2 deletions examples/multiple_windows/lib/app/tooltip_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ class _TooltipButtonState extends State<TooltipButton> {
}

void _onPressed(
final WindowManager windowManager,
final KeyedWindowManager windowManager,
final WindowSettings windowSettings,
) {
// Toggle tooltip visibility.
Expand Down Expand Up @@ -159,7 +159,9 @@ class _TooltipButtonState extends State<TooltipButton> {

@override
Widget build(BuildContext context) {
final WindowManager windowManager = WindowManagerAccessor.of(context);
final KeyedWindowManager windowManager = KeyedWindowManagerAccessor.of(
context,
);
final WindowSettings windowSettings = WindowSettingsAccessor.of(context);

return OutlinedButton(
Expand Down
6 changes: 3 additions & 3 deletions examples/multiple_windows/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ class _MultiWindowAppState extends State<MultiWindowApp> {
delegate: MainControllerWindowDelegate(),
);
final WindowSettings settings = WindowSettings();
late final WindowManager windowManager;
late final KeyedWindowManager windowManager;

@override
void initState() {
super.initState();
windowManager = WindowManager(
windowManager = KeyedWindowManager(
initialWindows: <KeyedWindow>[
KeyedWindow(
isMainWindow: true,
Expand All @@ -68,7 +68,7 @@ class _MultiWindowAppState extends State<MultiWindowApp> {
controller: controller,
child: MaterialApp(home: MainWindow(controller: controller)),
);
return WindowManagerAccessor(
return KeyedWindowManagerAccessor(
windowManager: windowManager,
child: WindowSettingsAccessor(
windowSettings: settings,
Expand Down
Loading