flutter_second_screen 1.0.1
flutter_second_screen: ^1.0.1 copied to clipboard
A clean architecture Flutter package for managing secondary screens and cross-screen communication on Windows and macOS desktop, with upcoming Android and iOS support.
Flutter Second Screen #
A production-grade, Clean Architecture Flutter package for managing secondary screens, dual displays, and cross-screen communication. Currently available for Windows Desktop, with architectural blueprints and upcoming native support for Android and iOS.
๐ Key Features #
- Multi-Display Detection: Query physical monitors, detect primary vs. secondary displays, resolutions, and position offsets.
- Secondary Window Lifecycle: Initialize, open, maximize, focus, and close secondary windows seamlessly.
- Bi-Directional Messaging: Typed or event-based cross-screen event bus to transfer cart data, signatures, advertisements, or status updates between windows.
- Clean Architecture & SOLID: Strict separation of concerns (Domain, Data, Presentation) with dependency inversion, interface segregation, and platform interface extensibility.
- Developer-Friendly Mixin & Widgets: Use
SecondScreenListenerMixin,SecondScreenEventListener, orSecondScreenEventBuilderfor reactive UI updates without boilerplate. - Multi-Platform Blueprint: Built on Flutter's
PlatformInterfacestandard with pluggable platform classes and documented TODO blueprints for Android Presentation API and iOS external UIScreen.
๐ฅ Platform Support #
| Platform | Support Status | Underlying Mechanism |
|---|---|---|
| Windows Desktop / macOS | โ Available | window_manager_plus + screen_retriever multi-window |
| Android | โณ In Development | android.app.Presentation + DisplayManager |
| iOS | โณ In Development | UIScreen external displays + UIWindow |
| Linux | ๐ Planned | Desktop multi-window & display retriever |
๐ Architecture Overview #
flutter_second_screen strictly adheres to Clean Architecture and SOLID principles, ensuring decoupling between UI, business rules, and platform-specific window managers.
๐ณ Architectural Component Tree #
flutter_second_screen
โโโ ๐ฑ Presentation Layer (Flutter Widgets & Mixins)
โ โโโ Main Screen View (Cashier / POS UI)
โ โโโ Secondary Screen View (Customer Display UI)
โ โโโ SecondScreenListenerMixin (State mixin for reactive events)
โ โโโ SecondScreenEventListener (Callback-based event listener widget)
โ โโโ SecondScreenEventBuilder (Builder widget reacting to stream events)
โ
โโโ ๐ฏ Unified Facade (Public API Entry Point)
โ โโโ FlutterSecondScreen (Singleton & factory for all operations)
โ
โโโ ๐ง Domain Layer (Pure Dart Business Logic - Zero External Dependencies)
โ โโโ ๐ฆ Entities
โ โ โโโ ScreenDisplay (Display ID, resolution, position offset, isPrimary)
โ โ โโโ SecondScreenEvent<T> (Cross-screen typed messaging model)
โ โ โโโ SecondScreenConfig (Secondary window title, size, position, flags)
โ โ โโโ SecondScreenStatus (Window lifecycle state)
โ โ
โ โโโ โ๏ธ Use Cases (Single Responsibility)
โ โ โโโ Display Management
โ โ โ โโโ GetDisplaysUseCase
โ โ โ โโโ IsSecondaryDisplayAvailableUseCase
โ โ โโโ Window Lifecycle
โ โ โ โโโ InitSecondaryDisplayUseCase
โ โ โ โโโ OpenSecondaryScreenUseCase
โ โ โ โโโ CloseSecondaryScreenUseCase
โ โ โ โโโ IsSecondaryScreenOpenUseCase
โ โ โโโ Cross-Screen Messaging
โ โ โโโ SendEventToSecondaryUseCase
โ โ โโโ SendEventToMainUseCase
โ โ โโโ ListenSecondaryScreenEventsUseCase
โ โ
โ โโโ ๐ Repository Contracts (Interface Segregation)
โ โโโ SecondScreenDisplayManager (Display detection contract)
โ โโโ SecondScreenWindowManager (Window lifecycle contract)
โ โโโ SecondScreenMessenger (Cross-screen messaging contract)
โ โโโ SecondScreenRepository (Aggregated domain interface)
โ
โโโ ๐พ Data Layer (Infrastructure & Platform Implementations)
โโโ ๐ Repository Implementation
โ โโโ SecondScreenRepositoryImpl (Implements Domain Repository via DIP)
โ
โโโ ๐ฆ Data Models (Adapters & Serialization)
โ โโโ ScreenDisplayModel (Adapts platform displays to domain entities)
โ โโโ SecondScreenEventModel (JSON serialization for cross-screen events)
โ
โโโ ๐ Platform Data Sources (Platform Interface Pattern)
โโโ SecondScreenPlatform (Federated platform interface)
โโโ DesktopSecondScreenPlatform (Windows & macOS multi-window core)
โ โโโ WindowsSecondScreenPlatform (Windows desktop plugin class)
โ โโโ MacosSecondScreenPlatform (macOS desktop plugin class)
โโโ AndroidSecondScreenPlatform (Android Presentation API blueprint)
โโโ IosSecondScreenPlatform (iOS UIScreen & UIWindow blueprint)
๐ Codebase File & Folder Structure #
lib/
โโโ flutter_second_screen.dart # Public export library entrypoint
โโโ src/
โโโ flutter_second_screen.dart # FlutterSecondScreen facade class
โโโ presentation/ # Presentation Layer
โ โโโ presentation.dart
โ โโโ mixins/
โ โ โโโ second_screen_listener_mixin.dart
โ โโโ widgets/
โ โโโ second_screen_event_listener.dart
โโโ domain/ # Domain Layer (Pure Dart)
โ โโโ domain.dart
โ โโโ entities/
โ โ โโโ entities.dart
โ โ โโโ screen_display.dart
โ โ โโโ second_screen_config.dart
โ โ โโโ second_screen_event.dart
โ โ โโโ second_screen_status.dart
โ โโโ repositories/
โ โ โโโ repositories.dart
โ โ โโโ second_screen_display_manager.dart
โ โ โโโ second_screen_window_manager.dart
โ โ โโโ second_screen_messenger.dart
โ โ โโโ second_screen_repository.dart
โ โโโ usecases/
โ โโโ usecases.dart
โ โโโ get_displays_usecase.dart
โ โโโ is_secondary_display_available_usecase.dart
โ โโโ init_secondary_display_usecase.dart
โ โโโ open_secondary_screen_usecase.dart
โ โโโ close_secondary_screen_usecase.dart
โ โโโ is_secondary_screen_open_usecase.dart
โ โโโ send_event_to_secondary_usecase.dart
โ โโโ send_event_to_main_usecase.dart
โ โโโ listen_secondary_screen_events_usecase.dart
โโโ data/ # Data Layer
โโโ data.dart
โโโ datasources/
โ โโโ datasources.dart
โ โโโ second_screen_platform.dart
โ โโโ desktop_second_screen_platform.dart
โ โโโ windows_second_screen_platform.dart
โ โโโ macos_second_screen_platform.dart
โ โโโ android_second_screen_platform.dart
โ โโโ ios_second_screen_platform.dart
โโโ models/
โ โโโ models.dart
โ โโโ screen_display_model.dart
โ โโโ second_screen_event_model.dart
โโโ repositories/
โโโ repositories.dart
โโโ second_screen_repository_impl.dart
๐ Data & Control Flow Diagram #
flowchart TD
subgraph UI ["Presentation Layer (Flutter UI)"]
ScreenA["Main Screen View"]
ScreenB["Secondary Screen View"]
Mixin["SecondScreenListenerMixin"]
Widget["SecondScreenEventListener"]
end
subgraph Facade ["Unified Facade"]
FSS["FlutterSecondScreen"]
end
subgraph Domain ["Domain Layer (Pure Dart)"]
direction TB
UseCases["Use Cases (Open, Close, Init, SendEvent, etc.)"]
Contracts["Repository Contracts (DisplayManager, WindowManager, Messenger)"]
Entities["Entities (ScreenDisplay, SecondScreenEvent, SecondScreenConfig)"]
UseCases --> Contracts
UseCases -.-> Entities
end
subgraph Data ["Data Layer"]
RepoImpl["SecondScreenRepositoryImpl"]
PI["SecondScreenPlatform (Interface)"]
subgraph Implementations ["Platform Drivers"]
DeskPlat["DesktopSecondScreenPlatform"]
WinPlat["WindowsSecondScreenPlatform"]
MacPlat["MacosSecondScreenPlatform"]
AndPlat["AndroidSecondScreenPlatform (Blueprint)"]
IosPlat["IosSecondScreenPlatform (Blueprint)"]
end
end
UI --> Facade
Facade --> UseCases
RepoImpl -.->|implements (DIP)| Contracts
RepoImpl --> PI
PI --> DeskPlat
DeskPlat --> WinPlat
DeskPlat --> MacPlat
PI --> AndPlat
PI --> IosPlat
SOLID Principles Applied: #
- Single Responsibility Principle (SRP): Display queries (
SecondScreenDisplayManager), window lifecycle management (SecondScreenWindowManager), and inter-screen messaging (SecondScreenMessenger) are decoupled into focused, dedicated interfaces. - Open/Closed Principle (OCP): Adding support for a new operating system (e.g., Android, iOS, or macOS) only requires implementing
SecondScreenPlatformwithout modifying the domain use cases or repository contracts. - Liskov Substitution Principle (LSP): Any platform implementation (
WindowsSecondScreenPlatform,AndroidSecondScreenPlatform, or test mocks) can be seamlessly substituted without breaking consumers. - Interface Segregation Principle (ISP): Clients can depend solely on
SecondScreenDisplayManager,SecondScreenWindowManager, orSecondScreenMessengerinstead of one monolithic interface. - Dependency Inversion Principle (DIP): High-level application modules depend on domain abstractions rather than concrete platform libraries.
๐ Getting Started #
1. Add Dependency #
In your pubspec.yaml:
dependencies:
flutter_second_screen: ^1.0.0
2. Main Entry Point Setup #
In your main.dart, handle the secondary screen entry point when the process is launched:
import 'package:flutter/material.dart';
import 'package:flutter_second_screen/flutter_second_screen.dart';
void main(List<String> args) async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterSecondScreen.ensureInitialized(args);
if (FlutterSecondScreen.isSecondaryScreen(args)) {
// Secondary screen process
final initialized = await FlutterSecondScreen.instance.initSecondaryDisplay(
const SecondScreenConfig(title: 'Customer Facing Display'),
);
if (initialized) {
runApp(const CustomerDisplayApp());
}
} else {
// Main cashier / POS application
runApp(const CashierMainApp());
}
}
๐ก Usage Examples #
Opening the Secondary Screen #
final secondScreen = FlutterSecondScreen.instance;
// Check if a second physical monitor is connected
if (await secondScreen.isSecondaryDisplayAvailable()) {
await secondScreen.openSecondaryScreen(arguments: ['second-screen']);
}
Sending Events Between Screens #
// Send from Main Screen to Secondary Screen
await secondScreen.sendEventToSecondary('cartUpdate', {
'items': [
{'name': 'Sneakers', 'price': 89.99},
],
'total': 89.99,
});
// Send from Secondary Screen back to Main Screen (e.g. Signature or Confirmation)
await secondScreen.sendEventToMain('signatureApproved', signatureData);
Listening to Events with SecondScreenListenerMixin #
class CustomerDisplayScreen extends StatefulWidget {
const CustomerDisplayScreen({super.key});
@override
State<CustomerDisplayScreen> createState() => _CustomerDisplayScreenState();
}
class _CustomerDisplayScreenState extends State<CustomerDisplayScreen>
with SecondScreenListenerMixin {
Map<String, dynamic>? cartData;
@override
Map<String, void Function(dynamic)> getSecondScreenEventListeners() {
return {
'cartUpdate': (data) {
setState(() {
cartData = data as Map<String, dynamic>;
});
},
'reset': (_) {
setState(() {
cartData = null;
});
},
};
}
@override
Widget build(BuildContext context) {
if (cartData == null) {
return const Center(child: Text('Welcome!'));
}
return Text('Total: \$${cartData!['total']}');
}
}
Listening to Events with SecondScreenEventListener Widget #
SecondScreenEventListener(
eventName: 'cartUpdate',
onEvent: (data) {
debugPrint('Received cart update: $data');
},
child: const MyCartWidget(),
);
๐ฑ Upcoming Android & iOS Implementation #
flutter_second_screen defines clean platform contracts and architectural blueprints for Android and iOS in:
AndroidSecondScreenPlatform(lib/src/data/datasources/android_second_screen_platform.dart)IosSecondScreenPlatform(lib/src/data/datasources/ios_second_screen_platform.dart)
Android Roadmap #
- Integrates with Android's
DisplayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION). - Spawns a secondary
FlutterEngineinside anandroid.app.Presentationdialog window. - Communicates across engines via native MethodChannel and EventChannel.
iOS Roadmap #
- Detects external displays via
UIScreen.screensandUIScreenDidConnectNotification. - Attaches a secondary
UIWindowwith aFlutterViewControllerbound to the externalUIScreen. - Communicates across engines via native MethodChannel and EventChannel.
๐งช Testing #
The package includes comprehensive unit and widget tests covering all entities, use cases, repository implementations, mixins, and platform interfaces:
flutter test
๐ License #
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Support #
If you find this package helpful, please consider giving it a โญ on GitHub and a ๐ on pub.dev!