Files
vpn/lib/main.dart
2026-04-20 01:34:03 +03:00

160 lines
3.6 KiB
Dart

import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:quasar_jet/models/storage_model.dart';
import 'package:quasar_jet/quick_tile_dispatcher.dart' show quickTileDispatcher;
import 'package:quasar_jet/services/tray_service.dart';
import 'package:quasar_jet/services/vpn_service.dart';
import 'package:quasar_jet/views/main_view.dart';
import 'package:quasar_jet/theme.dart';
import 'package:window_manager/window_manager.dart';
@pragma('vm:entry-point')
void _keepQuickTileDispatcher() {
quickTileDispatcher;
}
@pragma('vm:entry-point')
Future<void> quickTileMain() async {
await quickTileDispatcher();
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
if (_isLinuxDesktop) {
await windowManager.ensureInitialized();
await windowManager.waitUntilReadyToShow(
const WindowOptions(
size: Size(980, 720),
center: true,
title: 'Quasar Jet',
),
() async {
await windowManager.setPreventClose(true);
await windowManager.show();
await windowManager.focus();
},
);
}
final model = StorageModel();
await model.init();
final vpnService = VpnService();
runApp(MyApp(model: model, vpnService: vpnService));
}
// ignore: must_be_immutable
class MyApp extends StatefulWidget {
MyApp({super.key, required this.model, required this.vpnService});
StorageModel model;
VpnService vpnService;
static _MyAppState of(BuildContext context) {
return context.findAncestorStateOfType<_MyAppState>()!;
}
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ThemeMode _themeMode = ThemeMode.system;
late final WindowListener _windowListener = _LinuxWindowListener(this);
TrayService? _trayService;
bool _isQuitting = false;
@override
void initState() {
super.initState();
if (_isLinuxDesktop) {
windowManager.addListener(_windowListener);
unawaited(_initTray());
}
}
void changeTheme(ThemeMode mode) {
setState(() {
_themeMode = mode;
});
}
Future<void> _initTray() async {
_trayService = TrayService(
onShow: _showWindow,
onHide: _hideWindow,
onQuit: _quitFromTray,
);
await _trayService!.init();
}
Future<void> _showWindow() async {
await windowManager.show();
await windowManager.focus();
}
Future<void> _hideWindow() async {
await windowManager.hide();
}
Future<void> _quitFromTray() async {
if (_isQuitting) {
return;
}
_isQuitting = true;
await widget.vpnService.stop();
await windowManager.setPreventClose(false);
await windowManager.close();
}
Future<void> _handleWindowClose() async {
if (_isQuitting) {
await windowManager.setPreventClose(false);
return;
}
await _hideWindow();
}
@override
void dispose() {
if (_isLinuxDesktop) {
windowManager.removeListener(_windowListener);
unawaited(_trayService?.dispose());
}
widget.vpnService.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: AppTheme.light,
darkTheme: AppTheme.dark,
themeMode: _themeMode,
home: MainView(model: widget.model, vpnService: widget.vpnService),
);
}
}
bool get _isLinuxDesktop {
return !kIsWeb && Platform.isLinux;
}
final class _LinuxWindowListener with WindowListener {
_LinuxWindowListener(this._state);
final _MyAppState _state;
@override
void onWindowClose() {
unawaited(_state._handleWindowClose());
}
}