Created
May 3, 2026 20:33
-
-
Save rodydavis/76646f9f18e7543d9ff02860657f097a to your computer and use it in GitHub Desktop.
Flutter Multi Window with Shared State
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ignore_for_file: invalid_use_of_internal_member | |
| import 'package:flutter/material.dart'; | |
| // ignore: implementation_imports | |
| import 'package:flutter/src/widgets/_window.dart'; | |
| // Global shared state demonstrating the power of the single-isolate multi-view model. | |
| // All windows instantly reflect this state without needing platform channels or IPC. | |
| final ValueNotifier<int> sharedCounter = ValueNotifier<int>(0); | |
| void main() { | |
| WidgetsFlutterBinding.ensureInitialized(); | |
| runWidget(const MultiWindowOrchestrator()); | |
| } | |
| class MyWindowDelegate extends RegularWindowControllerDelegate { | |
| MyWindowDelegate({required this.onDestroy}); | |
| final VoidCallback onDestroy; | |
| @override | |
| void onWindowDestroyed() { | |
| super.onWindowDestroyed(); | |
| onDestroy(); | |
| } | |
| } | |
| class MultiWindowOrchestrator extends StatefulWidget { | |
| const MultiWindowOrchestrator({super.key}); | |
| @override | |
| State<MultiWindowOrchestrator> createState() => | |
| _MultiWindowOrchestratorState(); | |
| } | |
| class _MultiWindowOrchestratorState extends State<MultiWindowOrchestrator> { | |
| final Map<int, RegularWindowController> _activeWindows = {}; | |
| int _windowIdCounter = 0; | |
| @override | |
| void initState() { | |
| super.initState(); | |
| _spawnNewWindow(title: "Primary Application Workspace"); | |
| } | |
| void _spawnNewWindow({required String title}) { | |
| final int currentId = _windowIdCounter++; | |
| final controller = RegularWindowController( | |
| title: title, | |
| preferredSize: const Size(1024, 768), | |
| preferredConstraints: const BoxConstraints(minWidth: 800, minHeight: 600), | |
| delegate: MyWindowDelegate( | |
| onDestroy: () { | |
| setState(() { | |
| _activeWindows.remove(currentId); | |
| }); | |
| }, | |
| ), | |
| ); | |
| setState(() { | |
| _activeWindows[currentId] = controller; | |
| }); | |
| } | |
| @override | |
| Widget build(BuildContext context) { | |
| if (_activeWindows.isEmpty) { | |
| return const SizedBox.shrink(); | |
| } | |
| return ViewCollection( | |
| views: _activeWindows.entries.map((entry) { | |
| return RegularWindow( | |
| key: ValueKey('window_${entry.key}'), | |
| controller: entry.value, | |
| child: AppView( | |
| windowId: entry.key, | |
| onSpawnRequested: () => | |
| _spawnNewWindow(title: "Secondary Workspace ${entry.key}"), | |
| ), | |
| ); | |
| }).toList(), | |
| ); | |
| } | |
| } | |
| class AppView extends StatefulWidget { | |
| final int windowId; | |
| final VoidCallback onSpawnRequested; | |
| const AppView({ | |
| super.key, | |
| required this.windowId, | |
| required this.onSpawnRequested, | |
| }); | |
| @override | |
| State<AppView> createState() => _AppViewState(); | |
| } | |
| class _AppViewState extends State<AppView> { | |
| // Local state specific to this single window | |
| int _localCounter = 0; | |
| @override | |
| Widget build(BuildContext context) { | |
| return MaterialApp( | |
| debugShowCheckedModeBanner: false, | |
| home: Scaffold( | |
| appBar: AppBar(title: Text("Active View ID: ${widget.windowId}")), | |
| body: Center( | |
| child: Column( | |
| mainAxisAlignment: MainAxisAlignment.center, | |
| children: [ | |
| Text( | |
| "Welcome to Workspace ${widget.windowId}", | |
| style: Theme.of(context).textTheme.headlineMedium, | |
| ), | |
| const SizedBox(height: 32), | |
| // Demonstrating shared memory state across windows | |
| ValueListenableBuilder<int>( | |
| valueListenable: sharedCounter, | |
| builder: (context, value, child) { | |
| return Card( | |
| elevation: 4, | |
| color: Colors.blue.shade50, | |
| child: Padding( | |
| padding: const EdgeInsets.all(16.0), | |
| child: Column( | |
| children: [ | |
| Text( | |
| "Shared Global State", | |
| style: Theme.of(context).textTheme.titleLarge, | |
| ), | |
| Text( | |
| "$value", | |
| style: Theme.of(context).textTheme.displayMedium | |
| ?.copyWith(color: Colors.blue.shade800), | |
| ), | |
| ElevatedButton.icon( | |
| icon: const Icon(Icons.add), | |
| label: const Text("Increment Shared"), | |
| onPressed: () => sharedCounter.value++, | |
| ), | |
| ], | |
| ), | |
| ), | |
| ); | |
| }, | |
| ), | |
| const SizedBox(height: 24), | |
| // Demonstrating isolated local state for this specific window | |
| Card( | |
| elevation: 4, | |
| color: Colors.green.shade50, | |
| child: Padding( | |
| padding: const EdgeInsets.all(16.0), | |
| child: Column( | |
| children: [ | |
| Text( | |
| "Local Window State", | |
| style: Theme.of(context).textTheme.titleLarge, | |
| ), | |
| Text( | |
| "$_localCounter", | |
| style: Theme.of(context).textTheme.displayMedium | |
| ?.copyWith(color: Colors.green.shade800), | |
| ), | |
| ElevatedButton.icon( | |
| icon: const Icon(Icons.add), | |
| label: const Text("Increment Local"), | |
| onPressed: () { | |
| setState(() { | |
| _localCounter++; | |
| }); | |
| }, | |
| ), | |
| ], | |
| ), | |
| ), | |
| ), | |
| const SizedBox(height: 32), | |
| ElevatedButton.icon( | |
| icon: const Icon(Icons.open_in_new), | |
| label: const Text("Spawn Secondary Window"), | |
| onPressed: widget.onSpawnRequested, | |
| style: ElevatedButton.styleFrom( | |
| padding: const EdgeInsets.symmetric( | |
| horizontal: 24, | |
| vertical: 16, | |
| ), | |
| textStyle: const TextStyle(fontSize: 16), | |
| ), | |
| ), | |
| ], | |
| ), | |
| ), | |
| ), | |
| ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment