flutter_alone 2.3.1
flutter_alone: ^2.3.1 copied to clipboard
A Flutter plugin for preventing duplicate execution of desktop applications with customizable message support and cross-user detection
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_alone/flutter_alone.dart';
import 'package:system_tray/system_tray.dart';
import 'package:window_manager/window_manager.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await windowManager.ensureInitialized();
WindowOptions windowOptions = WindowOptions(
size: Size(500, 800),
center: true,
title: 'Tray App Example',
);
windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.show();
await windowManager.focus();
});
if (Platform.isWindows) {
if (!await FlutterAlone.instance.checkAndRun(
messageConfig: CustomMessageConfig(
customTitle: 'Example App',
customMessage: 'Application is already running in another account',
enableInDebugMode: true, // Enable duplicate check even in debug mode
windowTitle: 'Tray App Example',
packageId: 'com.example.myapp',
appName: 'MyFlutterApp',
mutexSuffix: 'production',
),
)) {
exit(0);
}
}
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final SystemTray _systemTray = SystemTray();
@override
void initState() {
super.initState();
_initSystemTray();
}
@override
void dispose() {
FlutterAlone.instance.dispose();
super.dispose();
}
Future<void> _initSystemTray() async {
String path =
Platform.isWindows ? 'assets/app_icon.ico' : 'assets/app_icon_64.png';
if (!await File(path).exists()) {
debugPrint("icon file not found: $path");
}
await _systemTray.initSystemTray(iconPath: path);
_systemTray.setTitle('Flutter alone example');
_systemTray.setToolTip('Flutter alone example');
final Menu menu = Menu();
await menu.buildFrom([
MenuItemLabel(
label: 'Open',
onClicked: (_) async {
await windowManager.show();
await windowManager.focus();
},
),
MenuItemLabel(
label: 'Exit',
onClicked: (_) async {
await _systemTray.destroy();
exit(0);
},
),
]);
await _systemTray.setContextMenu(menu);
_systemTray.registerSystemTrayEventHandler(
(eventName) async {
if (eventName == kSystemTrayEventClick) {
if (Platform.isWindows) {
await windowManager.show();
await windowManager.focus();
} else {
await _systemTray.popUpContextMenu();
}
} else if (eventName == kSystemTrayEventRightClick) {
if (Platform.isWindows) {
await _systemTray.popUpContextMenu();
} else {
await windowManager.show();
await windowManager.focus();
}
}
},
);
}
Future<void> hideWindow() async {
await windowManager.hide();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Alone Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'The app ran normally.',
style: TextStyle(fontSize: 18),
),
SizedBox(height: 20),
Text(
'Prevent duplicate execution with custom mutex name:',
style: TextStyle(fontSize: 14),
),
Text(
'packageId: com.example.myapp',
style: TextStyle(fontSize: 14),
),
Text(
'appName: MyFlutterApp',
style: TextStyle(fontSize: 14),
),
Text(
'suffix: production',
style: TextStyle(fontSize: 14),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: hideWindow,
child: Text('hide window'),
),
],
),
),
),
);
}
}