add DialogManager

This commit is contained in:
csf 2022-02-28 16:11:21 +08:00
parent 31ff6923d2
commit 034d825b74
5 changed files with 226 additions and 235 deletions

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_hbb/main.dart';
import 'package:tuple/tuple.dart';
typedef F = String Function(String);
@ -32,34 +33,36 @@ final ButtonStyle flatButtonStyle = TextButton.styleFrom(
),
);
void showLoading(String text, BuildContext? context) {
if (_hasDialog && context != null) {
Navigator.pop(context);
_hasDialog = false;
}
dismissLoading();
void showLoading(String text) {
DialogManager.reset();
EasyLoading.dismiss();
EasyLoading.show(status: text, maskType: EasyLoadingMaskType.black);
}
void dismissLoading() {
EasyLoading.dismiss();
class DialogManager{
static BuildContext? _dialogContext;
static void reset(){
if(_dialogContext!=null){
Navigator.pop(_dialogContext!);
}
_dialogContext = null;
}
static void register(BuildContext dialogContext){
_dialogContext = dialogContext;
}
}
bool _hasDialog = false;
typedef BuildAlertDailog = Tuple3<Widget, Widget, List<Widget>> Function(
typedef BuildAlertDialog = Tuple3<Widget, Widget, List<Widget>> Function(
void Function(void Function()));
// ??
Future<T?> showAlertDialog<T>(BuildContext context, BuildAlertDailog build,
// flutter Dialog
Future<T?> showAlertDialog<T>(BuildAlertDialog build,
[WillPopCallback? onWillPop,
bool barrierDismissible = false,
double contentPadding = 20]) async {
dismissLoading();
if (_hasDialog) {
Navigator.pop(context);
}
_hasDialog = true;
EasyLoading.dismiss();
DialogManager.reset();
var dialog = StatefulBuilder(builder: (context, setState) {
var widgets = build(setState);
if (onWillPop == null) onWillPop = () async => false;
@ -72,15 +75,20 @@ Future<T?> showAlertDialog<T>(BuildContext context, BuildAlertDailog build,
actions: widgets.item3,
));
});
if(globalKey.currentContext == null) return null;
var res = await showDialog<T>(
context: context,
context: globalKey.currentContext!,
barrierDismissible: barrierDismissible,
builder: (context) => dialog);
_hasDialog = false;
builder: (context) {
DialogManager.register(context);
return dialog;
});
DialogManager.reset();
return res;
}
void msgBox(String type, String title, String text, BuildContext context,
// EasyLoading
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),
@ -95,16 +103,14 @@ void msgBox(String type, String title, String text, BuildContext context,
child: Text(Translator.call(text),
style: TextStyle(color: MyTheme.accent))));
dismissLoading();
if (_hasDialog) {
Navigator.pop(context);
_hasDialog = false;
}
EasyLoading.dismiss();
DialogManager.reset();
if(globalKey.currentContext == null) return;
final buttons = [
Expanded(child: Container()),
wrap(Translator.call('OK'), () {
dismissLoading();
Navigator.pop(context);
EasyLoading.dismiss();
Navigator.pop(globalKey.currentContext!); // TODO
})
];
if (hasCancel == null) {
@ -114,7 +120,7 @@ void msgBox(String type, String title, String text, BuildContext context,
buttons.insert(
1,
wrap(Translator.call('Cancel'), () {
dismissLoading();
EasyLoading.dismiss();
}));
}
EasyLoading.show(

View File

@ -57,12 +57,10 @@ class _HomePageState extends State<HomePage> {
items.add(PopupMenuItem<String>(
child: Text(translate('ID Server')),
value: 'id_server'));
if (isAndroid){
items.add(
PopupMenuItem<String>(
child: Text(translate('Share My Screen')),
value: 'server')
);
if (isAndroid) {
items.add(PopupMenuItem<String>(
child: Text(translate('Share My Screen')),
value: 'server'));
}
items.add(PopupMenuItem<String>(
child: Text(translate('About') + ' RustDesk'),
@ -315,25 +313,22 @@ void showServer(BuildContext context) {
var id = '';
var relay = '';
var key = '';
showAlertDialog(
context,
(setState) => Tuple3(
Text(translate('ID Server')),
Form(
key: formKey,
child:
Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
TextFormField(
initialValue: id0,
decoration: InputDecoration(
labelText: translate('ID Server'),
),
validator: validate,
onSaved: (String? value) {
if (value != null) id = value.trim();
},
),
/*
showAlertDialog((setState) => Tuple3(
Text(translate('ID Server')),
Form(
key: formKey,
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
TextFormField(
initialValue: id0,
decoration: InputDecoration(
labelText: translate('ID Server'),
),
validator: validate,
onSaved: (String? value) {
if (value != null) id = value.trim();
},
),
/*
TextFormField(
initialValue: relay0,
decoration: InputDecoration(
@ -345,52 +340,51 @@ void showServer(BuildContext context) {
},
),
*/
TextFormField(
initialValue: key0,
decoration: InputDecoration(
labelText: 'Key',
),
validator: null,
onSaved: (String? value) {
if (value != null) key = value.trim();
},
),
])),
[
TextButton(
style: flatButtonStyle,
onPressed: () {
Navigator.pop(context);
TextFormField(
initialValue: key0,
decoration: InputDecoration(
labelText: 'Key',
),
validator: null,
onSaved: (String? value) {
if (value != null) key = value.trim();
},
child: Text(translate('Cancel')),
),
TextButton(
style: flatButtonStyle,
onPressed: () {
if (formKey.currentState != null && formKey.currentState!.validate()) {
formKey.currentState!.save();
if (id != id0)
FFI.setByName('option',
'{"name": "custom-rendezvous-server", "value": "$id"}');
if (relay != relay0)
FFI.setByName('option',
'{"name": "relay-server", "value": "$relay"}');
if (key != key0)
FFI.setByName(
'option', '{"name": "key", "value": "$key"}');
Navigator.pop(context);
}
},
child: Text(translate('OK')),
),
],
));
])),
[
TextButton(
style: flatButtonStyle,
onPressed: () {
Navigator.pop(context);
},
child: Text(translate('Cancel')),
),
TextButton(
style: flatButtonStyle,
onPressed: () {
if (formKey.currentState != null &&
formKey.currentState!.validate()) {
formKey.currentState!.save();
if (id != id0)
FFI.setByName('option',
'{"name": "custom-rendezvous-server", "value": "$id"}');
if (relay != relay0)
FFI.setByName(
'option', '{"name": "relay-server", "value": "$relay"}');
if (key != key0)
FFI.setByName('option', '{"name": "key", "value": "$key"}');
Navigator.pop(context);
}
},
child: Text(translate('OK')),
),
],
));
}
Future<Null> showAbout(BuildContext context) async {
var version = await FFI.getVersion();
showAlertDialog(
context,
(setState) => Tuple3(
SizedBox.shrink(), // TODO test old:null
Wrap(direction: Axis.vertical, spacing: 12, children: [

View File

@ -1,4 +1,5 @@
import 'package:flutter/services.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:math';
import 'dart:convert';
@ -96,7 +97,6 @@ class FfiModel with ChangeNotifier {
_inputBlocked = false;
_permissions.clear();
}
void update(String id,
BuildContext context,
void Function(
@ -136,7 +136,7 @@ class FfiModel with ChangeNotifier {
if (rgba != null) {
if (_waitForImage) {
_waitForImage = false;
dismissLoading();
EasyLoading.dismiss();
}
_decoding = true;
final pid = FFI.id;
@ -170,7 +170,7 @@ class FfiModel with ChangeNotifier {
}
void handlePeerInfo(Map<String, dynamic> evt, BuildContext context) {
dismissLoading();
EasyLoading.dismiss();
_pi.version = evt['version'];
_pi.username = evt['username'];
_pi.hostname = evt['hostname'];
@ -192,7 +192,7 @@ class FfiModel with ChangeNotifier {
_display = _pi.displays[_pi.currentDisplay];
}
if (displays.length > 0) {
showLoading(translate('Connected, waiting for image...'), context);
showLoading(translate('Connected, waiting for image...'));
_waitForImage = true;
}
notifyListeners();

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_hbb/main.dart';
import 'package:flutter_hbb/widgets/gesture_help.dart';
import 'package:provider/provider.dart';
import 'package:flutter/services.dart';
@ -43,7 +44,7 @@ class _RemotePageState extends State<RemotePage> {
FFI.connect(widget.id);
WidgetsBinding.instance!.addPostFrameCallback((_) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
showLoading(translate('Connecting...'), context);
showLoading(translate('Connecting...'));
_interval =
Timer.periodic(Duration(milliseconds: 30), (timer) => interval());
});
@ -57,7 +58,7 @@ class _RemotePageState extends State<RemotePage> {
FFI.close();
_interval?.cancel();
_timer?.cancel();
dismissLoading();
EasyLoading.dismiss();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values);
Wakelock.disable();
@ -99,9 +100,9 @@ class _RemotePageState extends State<RemotePage> {
var title = evt['title'];
var text = evt['text'];
if (type == 're-input-password') {
wrongPasswordDialog(id, context);
wrongPasswordDialog(id);
} else if (type == 'input-password') {
enterPasswordDialog(id, context);
enterPasswordDialog(id);
} else {
var hasRetry = evt['hasRetry'] == 'true';
print(evt);
@ -110,12 +111,12 @@ class _RemotePageState extends State<RemotePage> {
}
void showMsgBox(String type, String title, String text, bool hasRetry) {
msgBox(type, title, text, context);
msgBox(type, title, text);
if (hasRetry) {
_timer?.cancel();
_timer = Timer(Duration(seconds: _reconnects), () {
FFI.reconnect();
showLoading(translate('Connecting...'), context);
showLoading(translate('Connecting...'));
});
_reconnects *= 2;
} else {
@ -530,7 +531,7 @@ class _RemotePageState extends State<RemotePage> {
FFI.setByName('lock_screen');
} else if (value == 'block-input') {
FFI.setByName('toggle_option',
(FFI.ffiModel.inputBlocked ? 'un' : '') + 'block-inpu');
(FFI.ffiModel.inputBlocked ? 'un' : '') + 'block-input');
FFI.ffiModel.inputBlocked = !FFI.ffiModel.inputBlocked;
} else if (value == 'refresh') {
FFI.setByName('refresh');
@ -579,7 +580,7 @@ class _RemotePageState extends State<RemotePage> {
}
void close() {
msgBox('', 'Close', 'Are you sure to close the connection?', context);
msgBox('', 'Close', 'Are you sure to close the connection?');
}
Widget getHelpTools() {
@ -776,75 +777,73 @@ class ImagePainter extends CustomPainter {
}
}
void enterPasswordDialog(String id, BuildContext context) {
void enterPasswordDialog(String id) {
final controller = TextEditingController();
var remember = FFI.getByName('remember', id) == 'true';
showAlertDialog(
context,
(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'),
),
value: remember,
onChanged: (v) {
if (v != null) {
setState(() => remember = v);
}
},
),
]),
[
TextButton(
style: flatButtonStyle,
onPressed: () {
Navigator.pop(context);
Navigator.pop(context);
},
child: Text(translate('Cancel')),
),
TextButton(
style: flatButtonStyle,
onPressed: () {
var text = controller.text.trim();
if (text == '') return;
FFI.login(text, remember);
showLoading(translate('Logging in...'), null);
Navigator.pop(context);
},
child: Text(translate('OK')),
),
],
));
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'),
),
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, BuildContext context) {
showAlertDialog(
context,
(_) => Tuple3(Text(translate('Wrong Password')),
Text(translate('Do you want to enter again?')), [
TextButton(
style: flatButtonStyle,
onPressed: () {
Navigator.pop(context);
Navigator.pop(context);
},
child: Text(translate('Cancel')),
),
TextButton(
style: flatButtonStyle,
onPressed: () {
enterPasswordDialog(id, context);
},
child: Text(translate('Retry')),
),
]));
void wrongPasswordDialog(String id) {
if (globalKey.currentContext == null) return;
showAlertDialog((_) => Tuple3(Text(translate('Wrong Password')),
Text(translate('Do you want to enter again?')), [
TextButton(
style: flatButtonStyle,
onPressed: () {
DialogManager.reset();
Navigator.pop(globalKey.currentContext!);
},
child: Text(translate('Cancel')),
),
TextButton(
style: flatButtonStyle,
onPressed: () {
enterPasswordDialog(id);
},
child: Text(translate('Retry')),
),
]));
}
CheckboxListTile getToggle(
@ -914,7 +913,7 @@ void showOptions(BuildContext context) {
displays.add(Divider(color: MyTheme.border));
}
final perms = FFI.ffiModel.permissions;
showAlertDialog(context, (setState) {
showAlertDialog((setState) {
final more = <Widget>[];
if (perms['audio'] != false) {
more.add(getToggle(setState, 'disable-audio', 'Mute'));
@ -977,51 +976,49 @@ void showSetOSPassword(BuildContext context, bool login) {
var password = FFI.getByName('peer_option', "os-password");
var autoLogin = FFI.getByName('peer_option', "auto-login") != "";
controller.text = password;
showAlertDialog(
context,
(setState) => Tuple3(
Text(translate('OS Password')),
Column(mainAxisSize: MainAxisSize.min, children: [
PasswordWidget(controller: controller),
CheckboxListTile(
contentPadding: const EdgeInsets.all(0),
dense: true,
controlAffinity: ListTileControlAffinity.leading,
title: Text(
translate('Auto Login'),
),
value: autoLogin,
onChanged: (v) {
if (v == null) return;
setState(() => autoLogin = v);
},
),
]),
[
TextButton(
style: flatButtonStyle,
onPressed: () {
Navigator.pop(context);
},
child: Text(translate('Cancel')),
),
TextButton(
style: flatButtonStyle,
onPressed: () {
var text = controller.text.trim();
FFI.setByName('peer_option',
'{"name": "os-password", "value": "$text"}');
FFI.setByName('peer_option',
'{"name": "auto-login", "value": "${autoLogin ? 'Y' : ''}"}');
if (text != "" && login) {
FFI.setByName('input_os_password', text);
}
Navigator.pop(context);
},
child: Text(translate('OK')),
),
],
));
showAlertDialog((setState) => Tuple3(
Text(translate('OS Password')),
Column(mainAxisSize: MainAxisSize.min, children: [
PasswordWidget(controller: controller),
CheckboxListTile(
contentPadding: const EdgeInsets.all(0),
dense: true,
controlAffinity: ListTileControlAffinity.leading,
title: Text(
translate('Auto Login'),
),
value: autoLogin,
onChanged: (v) {
if (v == null) return;
setState(() => autoLogin = v);
},
),
]),
[
TextButton(
style: flatButtonStyle,
onPressed: () {
Navigator.pop(context);
},
child: Text(translate('Cancel')),
),
TextButton(
style: flatButtonStyle,
onPressed: () {
var text = controller.text.trim();
FFI.setByName(
'peer_option', '{"name": "os-password", "value": "$text"}');
FFI.setByName('peer_option',
'{"name": "auto-login", "value": "${autoLogin ? 'Y' : ''}"}');
if (text != "" && login) {
FFI.setByName('input_os_password', text);
}
Navigator.pop(context);
},
child: Text(translate('OK')),
),
],
));
}
void sendPrompt(bool isMac, String key) {

View File

@ -198,15 +198,14 @@ Widget getConnInfo(String name, String peerID) {
);
}
BuildContext? loginReqAlertCtx;
void showLoginReqAlert(String peerID, String name) async {
if (globalKey.currentContext == null) return;
await showDialog(
barrierDismissible: false,
context: globalKey.currentContext!,
builder: (alertContext) {
loginReqAlertCtx = alertContext;
DialogManager.reset();
DialogManager.register(alertContext);
return AlertDialog(
title: Text("Control Request"),
content: Container(
@ -225,7 +224,7 @@ void showLoginReqAlert(String peerID, String name) async {
child: Text(translate("Dismiss")),
onPressed: () {
FFI.setByName("login_res", "false");
Navigator.of(alertContext).pop();
DialogManager.reset();
}),
ElevatedButton(
child: Text(translate("Accept")),
@ -235,20 +234,12 @@ void showLoginReqAlert(String peerID, String name) async {
_toAndroidStartCapture();
}
FFI.serverModel.setPeer(true);
Navigator.of(alertContext).pop();
DialogManager.reset();
}),
],
);
});
loginReqAlertCtx = null;
}
clearLoginReqAlert() {
if (loginReqAlertCtx != null) {
Navigator.of(loginReqAlertCtx!).pop();
FFI.serverModel.updateClientState();
loginReqAlertCtx = null;
}
DialogManager.reset();
}
class PermissionRow extends StatelessWidget {
@ -376,10 +367,12 @@ Future<Null> _toAndroidInitInput() async {
showInputWarnAlert() async {
if (globalKey.currentContext == null) return;
DialogManager.reset();
await showDialog<bool>(
context: globalKey.currentContext!,
builder: (alertContext) {
// TODO t
DialogManager.register(alertContext);
return AlertDialog(
title: Text("获取输入权限引导"),
// content: Text("请在接下来的系统设置页面 \n进入 [服务] 配置页面\n将[RustDesk Input]服务开启"),
@ -396,17 +389,18 @@ showInputWarnAlert() async {
TextButton(
child: Text(translate("Do nothing")),
onPressed: () {
Navigator.of(alertContext).pop();
DialogManager.reset();
}),
ElevatedButton(
child: Text(translate("Go System Setting")),
onPressed: () {
_toAndroidInitInput();
Navigator.of(alertContext).pop();
DialogManager.reset();
}),
],
);
});
DialogManager.reset();
}
void toAndroidChannelInit() {
@ -425,14 +419,14 @@ void toAndroidChannelInit() {
}
case "start_capture":
{
clearLoginReqAlert();
DialogManager.reset();
FFI.serverModel.updateClientState();
break;
}
case "stop_capture":
{
DialogManager.reset();
FFI.serverModel.setPeer(false);
clearLoginReqAlert();
break;
}
case "on_permission_changed":