11import 'package:flutter/material.dart' ;
2+ import 'package:flutter/services.dart' ;
3+
24import 'package:go_router/go_router.dart' ;
5+ import 'package:netshare/ui/common_view/confirm_dialog.dart' ;
6+ import 'package:provider/provider.dart' ;
7+
38import 'package:netshare/config/styles.dart' ;
49import 'package:netshare/di/di.dart' ;
510import 'package:netshare/plugin_management/plugins.dart' ;
@@ -13,34 +18,40 @@ import 'package:netshare/ui/receive/receive_widget.dart';
1318import 'package:netshare/ui/send/send_widget.dart' ;
1419import 'package:netshare/ui/server/server_widget.dart' ;
1520import 'package:netshare/util/utility_functions.dart' ;
16- import 'package:provider/provider.dart' ;
17-
1821import 'package:netshare/config/constants.dart' ;
1922import 'package:netshare/ui/send/uploading_widget.dart' ;
2023
2124void main () async {
2225 WidgetsFlutterBinding .ensureInitialized ();
2326 await initPlugins ();
2427 setupDI ();
25- runApp (MyApp ());
28+ runApp (const MyApp ());
2629}
2730
28- class MyApp extends StatelessWidget {
31+ final GlobalKey <NavigatorState > _navigatorKey = GlobalKey <NavigatorState >();
32+
33+ class MyApp extends StatefulWidget {
34+ const MyApp ({super .key});
35+
36+ @override
37+ State <MyApp > createState () => _MyAppState ();
38+ }
2939
30- MyApp ({ super .key});
40+ class _MyAppState extends State < MyApp > {
3141
3242 final GoRouter _router = GoRouter (
43+ navigatorKey: _navigatorKey,
3344 errorBuilder: (BuildContext context, GoRouterState state) => ErrorWidget (state.error! ),
3445 routes: < GoRoute > [
3546 GoRoute (
3647 path: mRootPath,
37- redirect: (context, state) {
38- if (UtilityFunctions .isMobile) {
39- return '/$mClientPath ' ;
40- } else {
41- return '/$mServerPath ' ;
42- }
43- },
48+ redirect: (context, state) {
49+ if (UtilityFunctions .isMobile) {
50+ return '/$mClientPath ' ;
51+ } else {
52+ return '/$mServerPath ' ;
53+ }
54+ },
4455 ),
4556 GoRoute (
4657 name: mNavigationPath,
@@ -56,33 +67,34 @@ class MyApp extends StatelessWidget {
5667 name: mClientPath,
5768 path: '/$mClientPath ' ,
5869 builder: (context, state) => const ClientWidget (),
59- routes: [
60- GoRoute (
61- name: mSendPath,
62- path: mSendPath,
63- builder: (BuildContext context, GoRouterState state) => const SendWidget (),
64- routes: [
65- GoRoute (
66- name: mUploadingPath,
67- path: mUploadingPath,
68- builder: (context, state) => const UploadingWidget (),
69- )
70- ],
71- ),
72- GoRoute (
73- name: mReceivePath,
74- path: mReceivePath,
75- builder: (BuildContext context, GoRouterState state) => const ReceiveWidget (),
76- ),
77- GoRoute (
78- name: mScanningPath,
79- path: mScanningPath,
80- builder: (BuildContext context, GoRouterState state) => const ScanQRWidget (),
81- ),
82- ],
70+ routes: [
71+ GoRoute (
72+ name: mSendPath,
73+ path: mSendPath,
74+ builder: (BuildContext context, GoRouterState state) => const SendWidget (),
75+ routes: [
76+ GoRoute (
77+ name: mUploadingPath,
78+ path: mUploadingPath,
79+ builder: (context, state) => const UploadingWidget (),
80+ )
81+ ],
82+ ),
83+ GoRoute (
84+ name: mReceivePath,
85+ path: mReceivePath,
86+ builder: (BuildContext context, GoRouterState state) => const ReceiveWidget (),
87+ ),
88+ GoRoute (
89+ name: mScanningPath,
90+ path: mScanningPath,
91+ builder: (BuildContext context, GoRouterState state) => const ScanQRWidget (),
92+ ),
93+ ],
8394 ),
8495 ],
8596 );
97+ bool _isKeyboardListenerEnabled = true ;
8698
8799 @override
88100 Widget build (BuildContext context) {
@@ -101,7 +113,63 @@ class MyApp extends StatelessWidget {
101113 colorScheme: ColorScheme .fromSeed (seedColor: seedColor, background: backgroundColor),
102114 ),
103115 routerConfig: _router,
116+ builder: (context, child) {
117+ // Handle keyboard listener here
118+ RawKeyboard .instance.addListener ((RawKeyEvent value) => _handleKeyEvent (value));
119+ return child ?? const SizedBox .shrink ();
120+ },
104121 ),
105122 );
106123 }
124+
125+ void _handleKeyEvent (RawKeyEvent value) async {
126+ if (! _isKeyboardListenerEnabled) return ;
127+
128+ // If user pressed Command/Control + W keys, quit the app
129+ if (value.isMetaPressed && value.logicalKey == LogicalKeyboardKey .keyW ||
130+ value.isControlPressed && value.logicalKey == LogicalKeyboardKey .keyW) {
131+
132+ if (_navigatorKey.currentContext == null ) return ;
133+
134+ // show confirm dialog
135+ _showQuitAppConfirmationDialog (_navigatorKey.currentContext! , (confirmCallback) {
136+ if (confirmCallback) {
137+ SystemNavigator .pop (); // Quit the app
138+ }
139+ // listen keyboard again
140+ _isKeyboardListenerEnabled = true ;
141+ });
142+ }
143+ }
144+
145+ void _showQuitAppConfirmationDialog (BuildContext context, Function (bool )? confirmCallback) {
146+ // Disable the keyboard listener.
147+ _isKeyboardListenerEnabled = false ;
148+
149+ showDialog (
150+ barrierDismissible: false ,
151+ context: context,
152+ builder: (dialogContext) {
153+ return ConfirmDialog (
154+ dialogWidth: MediaQuery .of (dialogContext).size.width / 2 ,
155+ header: Text (
156+ 'Quit App' ,
157+ style: Theme .of (dialogContext).textTheme.headlineMedium? .copyWith (
158+ fontSize: 18.0 ,
159+ fontWeight: FontWeight .bold,
160+ ),
161+ textAlign: TextAlign .center,
162+ ),
163+ body: const Text (
164+ 'Are you sure you want to quit the app?' ,
165+ textAlign: TextAlign .center,
166+ ),
167+ cancelButtonTitle: 'No' ,
168+ okButtonTitle: 'Yes, I\' m sure' ,
169+ onCancel: () => confirmCallback? .call (false ),
170+ onConfirm: () => confirmCallback? .call (true ),
171+ );
172+ },
173+ );
174+ }
107175}
0 commit comments