diff --git a/lib/common.dart b/lib/common.dart index 6d9b69b76..359844f24 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'dart:async'; import 'package:flutter_easyloading/flutter_easyloading.dart'; -import 'package:tuple/tuple.dart'; final globalKey = GlobalKey(); @@ -40,61 +39,73 @@ void showLoading(String text) { EasyLoading.show(status: text, maskType: EasyLoadingMaskType.black); } -class DialogManager{ +typedef DialogBuilder = CustomAlertDialog Function( + BuildContext context, StateSetter setState); + +class DialogManager { static BuildContext? _dialogContext; - static void reset(){ - if(_dialogContext!=null){ + static void reset() { + if (_dialogContext != null) { Navigator.pop(_dialogContext!); } _dialogContext = null; } - static void register(BuildContext dialogContext){ + + static void register(BuildContext dialogContext) { _dialogContext = dialogContext; } - static void drop(){ + static void drop() { _dialogContext = null; } + + static Future show(DialogBuilder builder, + {bool barrierDismissible = false}) async { + if (globalKey.currentContext == null) return null; + EasyLoading.dismiss(); + DialogManager.reset(); + final res = await showDialog( + context: globalKey.currentContext!, + barrierDismissible: barrierDismissible, + builder: (context) { + DialogManager.register(context); + return StatefulBuilder(builder: builder); + }); + DialogManager.drop(); + return res; + } } -typedef BuildAlertDialog = Tuple3> Function( - void Function(void Function())); +class CustomAlertDialog extends StatelessWidget { + CustomAlertDialog( + {required this.title, + required this.content, + required this.actions, + this.onWillPop, + this.contentPadding}); -// flutter Dialog -Future showAlertDialog(BuildAlertDialog build, - [WillPopCallback? onWillPop, - bool barrierDismissible = false, - double contentPadding = 20]) async { - EasyLoading.dismiss(); - DialogManager.reset(); - var dialog = StatefulBuilder(builder: (context, setState) { - var widgets = build(setState); - if (onWillPop == null) onWillPop = () async => false; + final Widget title; + final Widget content; + final List actions; + final WillPopCallback? onWillPop; + final double? contentPadding; + + @override + Widget build(BuildContext context) { return WillPopScope( - onWillPop: onWillPop, + onWillPop: onWillPop ?? () async => false, child: AlertDialog( - title: widgets.item1, - contentPadding: EdgeInsets.all(contentPadding), - content: widgets.item2, - actions: widgets.item3, + title: title, + contentPadding: EdgeInsets.all(contentPadding ?? 20), + content: content, + actions: actions, )); - }); - if(globalKey.currentContext == null) return null; - var res = await showDialog( - context: globalKey.currentContext!, - barrierDismissible: barrierDismissible, - builder: (context) { - DialogManager.register(context); - return dialog; - }); - DialogManager.drop(); - return res; + } } // EasyLoading -void msgBox(String type, String title, String text, - {bool? hasCancel}) { +void msgBox(String type, String title, String text, {bool? hasCancel}) { var wrap = (String text, void Function() onPressed) => ButtonTheme( padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, @@ -110,7 +121,7 @@ void msgBox(String type, String title, String text, EasyLoading.dismiss(); DialogManager.reset(); - if(globalKey.currentContext == null) return; + if (globalKey.currentContext == null) return; final buttons = [ Expanded(child: Container()), wrap(Translator.call('OK'), () { @@ -129,65 +140,22 @@ void msgBox(String type, String title, String text, })); } EasyLoading.show( - status: "", - maskType: EasyLoadingMaskType.black, - indicator: Container( - constraints: BoxConstraints(maxWidth: 300), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(Translator.call(title), style: TextStyle(fontSize: 21)), - SizedBox(height: 20), - Text(Translator.call(text), style: TextStyle(fontSize: 15)), - SizedBox(height: 20), - Row( - children: buttons, - ) - ], - )) - ); -} - -class PasswordWidget extends StatefulWidget { - PasswordWidget({Key? key, required this.controller}) : super(key: key); - - final TextEditingController controller; - - @override - _PasswordWidgetState createState() => _PasswordWidgetState(); -} - -class _PasswordWidgetState extends State { - bool _passwordVisible = false; - - @override - Widget build(BuildContext context) { - return TextField( - autofocus: true, - controller: widget.controller, - obscureText: !_passwordVisible, - //This will obscure text dynamically - keyboardType: TextInputType.visiblePassword, - decoration: InputDecoration( - labelText: Translator.call('Password'), - hintText: Translator.call('Enter your password'), - // Here is key idea - suffixIcon: IconButton( - icon: Icon( - // Based on passwordVisible state choose the icon - _passwordVisible ? Icons.visibility : Icons.visibility_off, - color: Theme.of(context).primaryColorDark, - ), - onPressed: () { - // Update the state i.e. toogle the state of passwordVisible variable - setState(() { - _passwordVisible = !_passwordVisible; - }); - }, - ), - ), - ); - } + status: "", + maskType: EasyLoadingMaskType.black, + indicator: Container( + constraints: BoxConstraints(maxWidth: 300), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(Translator.call(title), style: TextStyle(fontSize: 21)), + SizedBox(height: 20), + Text(Translator.call(text), style: TextStyle(fontSize: 15)), + SizedBox(height: 20), + Row( + children: buttons, + ) + ], + ))); } Color str2color(String str, [alpha = 0xFF]) { diff --git a/lib/pages/connection_page.dart b/lib/pages/connection_page.dart index abea7db7d..12a166538 100644 --- a/lib/pages/connection_page.dart +++ b/lib/pages/connection_page.dart @@ -54,12 +54,6 @@ class _ConnectionPageState extends State { getSearchBarUI(), Container(height: 12), getPeers(), - ElevatedButton( - onPressed: () { - final res = FFI.getByName("read_dir"); - debugPrint("read_dir : $res"); - }, - child: Text("Local File Debug")) ]), ); } @@ -69,7 +63,7 @@ class _ConnectionPageState extends State { connect(id); } - void connect(String id,{bool isFileTransfer = false}) { + void connect(String id, {bool isFileTransfer = false}) { if (id == '') return; id = id.replaceAll(' ', ''); if (isFileTransfer) { @@ -79,7 +73,7 @@ class _ConnectionPageState extends State { builder: (BuildContext context) => FileManagerPage(id: id), ), ); - }else{ + } else { Navigator.push( context, MaterialPageRoute( @@ -271,8 +265,8 @@ class _ConnectionPageState extends State { () async { removePreference(id); }(); - }else if (value == 'file') { - connect(id,isFileTransfer: true); + } else if (value == 'file') { + connect(id, isFileTransfer: true); } } } diff --git a/lib/pages/remote_page.dart b/lib/pages/remote_page.dart index 726b79268..6da22b0ec 100644 --- a/lib/pages/remote_page.dart +++ b/lib/pages/remote_page.dart @@ -5,10 +5,9 @@ import 'package:provider/provider.dart'; import 'package:flutter/services.dart'; import 'dart:ui' as ui; import 'dart:async'; -import 'package:tuple/tuple.dart'; import 'package:wakelock/wakelock.dart'; import '../common.dart'; -import '../gestures.dart'; +import '../widgets/gestures.dart'; import '../models/model.dart'; import '../widgets/dialog.dart'; import 'chat_page.dart'; @@ -289,7 +288,7 @@ class _RemotePageState extends State { icon: Icon(Icons.tv), onPressed: () { setState(() => _showEdit = false); - showOptions(context); + showOptions(); }, ) ] + @@ -318,7 +317,7 @@ class _RemotePageState extends State { icon: Icon(Icons.more_vert), onPressed: () { setState(() => _showEdit = false); - showActions(context); + showActions(); }, ), ]), @@ -475,7 +474,7 @@ class _RemotePageState extends State { color: MyTheme.canvasColor, child: Stack(children: paints))); } - void showActions(BuildContext context) { + void showActions() { final size = MediaQuery.of(context).size; final x = 120.0; final y = size.height; @@ -494,7 +493,7 @@ class _RemotePageState extends State { style: flatButtonStyle, onPressed: () { Navigator.pop(context); - showSetOSPassword(context, false); + showSetOSPassword(false); }, child: Icon(Icons.edit, color: MyTheme.accent), ) @@ -553,7 +552,7 @@ class _RemotePageState extends State { if (password != "") { FFI.setByName('input_os_password', password); } else { - showSetOSPassword(context, true); + showSetOSPassword(true); } } else if (value == 'reset_canvas') { FFI.cursorModel.reset(); @@ -806,7 +805,7 @@ RadioListTile getRadio(String name, String toValue, String curValue, ); } -void showOptions(BuildContext context) { +void showOptions() { String quality = FFI.getByName('image_quality'); if (quality == '') quality = 'balanced'; String viewStyle = FFI.getByName('peer_option', 'view-style'); @@ -824,7 +823,7 @@ void showOptions(BuildContext context) { onTap: () { if (i == cur) return; FFI.setByName('switch_display', i.toString()); - Navigator.pop(context); + DialogManager.reset(); }, child: Ink( width: 40, @@ -848,7 +847,8 @@ void showOptions(BuildContext context) { displays.add(Divider(color: MyTheme.border)); } final perms = FFI.ffiModel.permissions; - showAlertDialog((setState) { + + DialogManager.show((context, setState) { final more = []; if (perms['audio'] != false) { more.add(getToggle(setState, 'disable-audio', 'Mute')); @@ -878,19 +878,19 @@ void showOptions(BuildContext context) { FFI.canvasModel.updateViewStyle(); }); }; - return Tuple3( - SizedBox.shrink(), - Column( + return CustomAlertDialog( + title: SizedBox.shrink(), + content: Column( mainAxisSize: MainAxisSize.min, children: displays + (isDesktop ? [ - getRadio( - 'Original', 'original', viewStyle, setViewStyle), - getRadio('Shrink', 'shrink', viewStyle, setViewStyle), - getRadio('Stretch', 'stretch', viewStyle, setViewStyle), - Divider(color: MyTheme.border), - ] + getRadio( + 'Original', 'original', viewStyle, setViewStyle), + getRadio('Shrink', 'shrink', viewStyle, setViewStyle), + getRadio('Stretch', 'stretch', viewStyle, setViewStyle), + Divider(color: MyTheme.border), + ] : []) + [ getRadio('Good image quality', 'best', quality, setQuality), @@ -902,18 +902,22 @@ void showOptions(BuildContext context) { setState, 'show-remote-cursor', 'Show remote cursor'), ] + more), - []); - }, () async => true, true, 0); + actions: [], + onWillPop: () async => true, + contentPadding: 0, + ); + },barrierDismissible: true); } -void showSetOSPassword(BuildContext context, bool login) { +void showSetOSPassword(bool login) { final controller = TextEditingController(); var password = FFI.getByName('peer_option', "os-password"); var autoLogin = FFI.getByName('peer_option', "auto-login") != ""; controller.text = password; - showAlertDialog((setState) => Tuple3( - Text(translate('OS Password')), - Column(mainAxisSize: MainAxisSize.min, children: [ + DialogManager.show((context, setState) { + return CustomAlertDialog( + title: Text(translate('OS Password')), + content: Column(mainAxisSize: MainAxisSize.min, children: [ PasswordWidget(controller: controller), CheckboxListTile( contentPadding: const EdgeInsets.all(0), @@ -929,7 +933,7 @@ void showSetOSPassword(BuildContext context, bool login) { }, ), ]), - [ + actions: [ TextButton( style: flatButtonStyle, onPressed: () { @@ -952,8 +956,8 @@ void showSetOSPassword(BuildContext context, bool login) { }, child: Text(translate('OK')), ), - ], - )); + ]); + }); } void sendPrompt(bool isMac, String key) { diff --git a/lib/pages/settings_page.dart b/lib/pages/settings_page.dart index 2fb38b604..327e0b1c1 100644 --- a/lib/pages/settings_page.dart +++ b/lib/pages/settings_page.dart @@ -1,6 +1,5 @@ import 'package:settings_ui/settings_ui.dart'; import 'package:flutter/material.dart'; -import 'package:tuple/tuple.dart'; import 'package:url_launcher/url_launcher.dart'; import '../common.dart'; import '../models/model.dart'; @@ -38,7 +37,7 @@ class SettingsPage extends StatelessWidget implements PageShape { title: Text("About"), tiles: [ SettingsTile.navigation( - title: Text("Version: "+version), + title: Text("Version: " + version), value: InkWell( onTap: () async { const url = 'https://rustdesk.com/'; @@ -70,9 +69,10 @@ void showServer() { var id = ''; var relay = ''; var key = ''; - showAlertDialog((setState) => Tuple3( - Text(translate('ID Server')), - Form( + DialogManager.show((context, setState) { + return CustomAlertDialog( + title: Text(translate('ID Server')), + content: Form( key: formKey, child: Column(mainAxisSize: MainAxisSize.min, children: [ TextFormField( @@ -108,7 +108,7 @@ void showServer() { }, ), ])), - [ + actions: [ TextButton( style: flatButtonStyle, onPressed: () { @@ -135,8 +135,8 @@ void showServer() { }, child: Text(translate('OK')), ), - ], - )); + ]); + }); } String? validate(value) { diff --git a/lib/widgets/dialog.dart b/lib/widgets/dialog.dart index f9aaceb22..352dd33a4 100644 --- a/lib/widgets/dialog.dart +++ b/lib/widgets/dialog.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:tuple/tuple.dart'; import '../common.dart'; import '../models/model.dart'; @@ -10,54 +9,28 @@ void clientClose() { void enterPasswordDialog(String id) { final controller = TextEditingController(); var remember = FFI.getByName('remember', id) == 'true'; - if (globalKey.currentContext == null) return; - showAlertDialog((setState) => Tuple3( - Text(translate('Password Required')), - Column(mainAxisSize: MainAxisSize.min, children: [ - PasswordWidget(controller: controller), - CheckboxListTile( - contentPadding: const EdgeInsets.all(0), - dense: true, - controlAffinity: ListTileControlAffinity.leading, - title: Text( - translate('Remember password'), + DialogManager.show((context, setState) { + return CustomAlertDialog( + title: Text(translate('Password Required')), + content: Column(mainAxisSize: MainAxisSize.min, children: [ + PasswordWidget(controller: controller), + CheckboxListTile( + contentPadding: const EdgeInsets.all(0), + dense: true, + controlAffinity: ListTileControlAffinity.leading, + title: Text( + translate('Remember password'), + ), + value: remember, + onChanged: (v) { + debugPrint("onChanged"); + if (v != null) { + setState(() => remember = v); + } + }, ), - value: remember, - onChanged: (v) { - if (v != null) { - setState(() => remember = v); - } - }, - ), - ]), - [ - TextButton( - style: flatButtonStyle, - onPressed: () { - DialogManager.reset(); - Navigator.pop(globalKey.currentContext!); - }, - child: Text(translate('Cancel')), - ), - TextButton( - style: flatButtonStyle, - onPressed: () { - var text = controller.text.trim(); - if (text == '') return; - FFI.login(text, remember); - DialogManager.reset(); - showLoading(translate('Logging in...')); - }, - child: Text(translate('OK')), - ), - ], - )); -} - -void wrongPasswordDialog(String id) { - if (globalKey.currentContext == null) return; - showAlertDialog((_) => Tuple3(Text(translate('Wrong Password')), - Text(translate('Do you want to enter again?')), [ + ]), + actions: [ TextButton( style: flatButtonStyle, onPressed: () { @@ -69,9 +42,80 @@ void wrongPasswordDialog(String id) { TextButton( style: flatButtonStyle, onPressed: () { - enterPasswordDialog(id); + var text = controller.text.trim(); + if (text == '') return; + FFI.login(text, remember); + DialogManager.reset(); + showLoading(translate('Logging in...')); }, - child: Text(translate('Retry')), + child: Text(translate('OK')), ), - ])); -} \ No newline at end of file + ], + ); + }); +} + +void wrongPasswordDialog(String id) { + DialogManager.show((context, setState) => CustomAlertDialog( + title: Text(translate('Wrong Password')), + content: Text(translate('Do you want to enter again?')), + actions: [ + TextButton( + style: flatButtonStyle, + onPressed: () { + DialogManager.reset(); + Navigator.pop(globalKey.currentContext!); + }, + child: Text(translate('Cancel')), + ), + TextButton( + style: flatButtonStyle, + onPressed: () { + enterPasswordDialog(id); + }, + child: Text(translate('Retry')), + ), + ])); +} + +class PasswordWidget extends StatefulWidget { + PasswordWidget({Key? key, required this.controller}) : super(key: key); + + final TextEditingController controller; + + @override + _PasswordWidgetState createState() => _PasswordWidgetState(); +} + +class _PasswordWidgetState extends State { + bool _passwordVisible = false; + + @override + Widget build(BuildContext context) { + return TextField( + autofocus: true, + controller: widget.controller, + obscureText: !_passwordVisible, + //This will obscure text dynamically + keyboardType: TextInputType.visiblePassword, + decoration: InputDecoration( + labelText: Translator.call('Password'), + hintText: Translator.call('Enter your password'), + // Here is key idea + suffixIcon: IconButton( + icon: Icon( + // Based on passwordVisible state choose the icon + _passwordVisible ? Icons.visibility : Icons.visibility_off, + color: Theme.of(context).primaryColorDark, + ), + onPressed: () { + // Update the state i.e. toogle the state of passwordVisible variable + setState(() { + _passwordVisible = !_passwordVisible; + }); + }, + ), + ), + ); + } +} diff --git a/lib/gestures.dart b/lib/widgets/gestures.dart similarity index 100% rename from lib/gestures.dart rename to lib/widgets/gestures.dart