From 85c3dbdf7f7094e8139a00a03f3d876c217f2022 Mon Sep 17 00:00:00 2001
From: csf <csf@breakbeat.cn>
Date: Tue, 19 Apr 2022 13:07:45 +0800
Subject: [PATCH] update dialog,use flutter_smart_dialog

---
 .../com/carriez/flutter_hbb/MainService.kt    |   1 -
 lib/common.dart                               | 149 ++++++--------
 lib/main.dart                                 |   9 +-
 lib/models/file_model.dart                    |  94 ++++-----
 lib/models/model.dart                         |  22 +-
 lib/models/server_model.dart                  | 190 +++++++++---------
 lib/pages/file_manager_page.dart              |  12 +-
 lib/pages/remote_page.dart                    |   6 +-
 lib/pages/server_page.dart                    |  28 ++-
 lib/pages/settings_page.dart                  |   4 +-
 lib/widgets/dialog.dart                       |  18 +-
 pubspec.lock                                  |  21 +-
 pubspec.yaml                                  |   4 +-
 13 files changed, 278 insertions(+), 280 deletions(-)

diff --git a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt
index a85c62bd6..47244de1a 100644
--- a/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt
+++ b/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt
@@ -582,7 +582,6 @@ class MainService : Service() {
         username: String,
         peerId: String
     ) {
-        cancelNotification(clientID)
         val notification = notificationBuilder
             .setOngoing(false)
             .setPriority(NotificationCompat.PRIORITY_HIGH)
diff --git a/lib/common.dart b/lib/common.dart
index 84fef86ef..585d53569 100644
--- a/lib/common.dart
+++ b/lib/common.dart
@@ -1,7 +1,9 @@
 import 'package:flutter/gestures.dart';
 import 'package:flutter/material.dart';
 import 'dart:async';
-import 'package:flutter_easyloading/flutter_easyloading.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
+
+import 'models/model.dart';
 
 final globalKey = GlobalKey<NavigatorState>();
 final navigationBarKey = GlobalKey();
@@ -42,37 +44,40 @@ final ButtonStyle flatButtonStyle = TextButton.styleFrom(
   ),
 );
 
-void showToast(String text) {
-  EasyLoading.showToast(Translator.call(text),
-      maskType: EasyLoadingMaskType.black);
+void showToast(String text,{Duration? duration}) {
+  SmartDialog.showToast(text,displayTime: duration);
 }
 
-void showLoading(String text) {
-  DialogManager.reset();
-  EasyLoading.dismiss();
-  EasyLoading.show(
-      indicator: Container(
-          constraints: BoxConstraints(maxWidth: 240),
-          child:
-              Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
-            SizedBox(height: 30),
-            Center(child: CircularProgressIndicator()),
-            SizedBox(height: 20),
-            Center(
-                child: Text(Translator.call(text),
-                    style: TextStyle(fontSize: 15))),
-            SizedBox(height: 20),
-            Center(
-                child: TextButton(
-                    style: flatButtonStyle,
-                    onPressed: () {
-                      EasyLoading.dismiss();
-                      backToHome();
-                    },
-                    child: Text(Translator.call('Cancel'),
-                        style: TextStyle(color: MyTheme.accent))))
-          ])),
-      maskType: EasyLoadingMaskType.black);
+void showLoading(String text, {bool clickMaskDismiss = false}) {
+  SmartDialog.dismiss();
+  SmartDialog.showLoading(
+      clickMaskDismiss: false,
+      builder: (context) {
+        return Container(
+            color: MyTheme.white,
+            constraints: BoxConstraints(maxWidth: 240),
+            child: Column(
+                mainAxisSize: MainAxisSize.min,
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  SizedBox(height: 30),
+                  Center(child: CircularProgressIndicator()),
+                  SizedBox(height: 20),
+                  Center(
+                      child: Text(Translator.call(text),
+                          style: TextStyle(fontSize: 15))),
+                  SizedBox(height: 20),
+                  Center(
+                      child: TextButton(
+                          style: flatButtonStyle,
+                          onPressed: () {
+                            SmartDialog.dismiss();
+                            backToHome();
+                          },
+                          child: Text(Translator.call('Cancel'),
+                              style: TextStyle(color: MyTheme.accent))))
+                ]));
+      });
 }
 
 backToHome() {
@@ -80,40 +85,36 @@ backToHome() {
 }
 
 typedef DialogBuilder = CustomAlertDialog Function(
-    StateSetter setState, Function([dynamic]) close);
+    StateSetter setState, void Function([dynamic]) close);
 
 class DialogManager {
-  static BuildContext? _dialogContext;
+  static int _tag = 0;
 
-  static void reset([result]) {
-    if (_dialogContext != null) {
-      Navigator.pop(_dialogContext!, result);
-    }
-    _dialogContext = null;
-  }
-
-  static void register(BuildContext dialogContext) {
-    _dialogContext = dialogContext;
-  }
-
-  static void drop() {
-    _dialogContext = null;
+  static dismissByTag(String tag, [result]) {
+    SmartDialog.dismiss(tag: tag, result: result);
   }
 
   static Future<T?> show<T>(DialogBuilder builder,
-      {bool barrierDismissible = false}) async {
-    if (globalKey.currentContext == null) return null;
-    EasyLoading.dismiss();
-    DialogManager.reset();
-    final res = await showDialog<T>(
-        context: globalKey.currentContext!,
-        barrierDismissible: barrierDismissible,
-        builder: (context) {
-          DialogManager.register(context);
-          return StatefulBuilder(
-              builder: (_, setState) => builder(setState, DialogManager.reset));
-        });
-    DialogManager.drop();
+      {bool barrierDismissible = false,
+      String? tag,
+      bool useAnimation = true}) async {
+    final t;
+    if (tag != null) {
+      t = tag;
+    } else {
+      _tag += 1;
+      t = _tag.toString();
+    }
+    SmartDialog.dismiss(status: SmartStatus.allToast);
+    SmartDialog.dismiss(status: SmartStatus.loading);
+    final close = ([res]) {
+      SmartDialog.dismiss(tag: t, result: res);
+    };
+    final res = await SmartDialog.show<T>(
+        tag: t,
+        useAnimation: useAnimation,
+        builder: (_) => StatefulBuilder(
+            builder: (_, setState) => builder(setState, close)));
     return res;
   }
 }
@@ -147,7 +148,6 @@ class CustomAlertDialog extends StatelessWidget {
   }
 }
 
-// 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),
@@ -162,12 +162,10 @@ void msgBox(String type, String title, String text, {bool? hasCancel}) {
           child: Text(Translator.call(text),
               style: TextStyle(color: MyTheme.accent))));
 
-  EasyLoading.dismiss();
-  DialogManager.reset();
+  SmartDialog.dismiss();
   final buttons = [
-    Expanded(child: Container()),
     wrap(Translator.call('OK'), () {
-      EasyLoading.dismiss();
+      SmartDialog.dismiss();
       backToHome();
     })
   ];
@@ -176,28 +174,15 @@ void msgBox(String type, String title, String text, {bool? hasCancel}) {
   }
   if (hasCancel) {
     buttons.insert(
-        1,
+        0,
         wrap(Translator.call('Cancel'), () {
-          EasyLoading.dismiss();
+          SmartDialog.dismiss();
         }));
   }
-  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,
-              )
-            ],
-          )));
+  DialogManager.show((setState, close) => CustomAlertDialog(
+      title: Text(translate(title), style: TextStyle(fontSize: 21)),
+      content: Text(Translator.call(text), style: TextStyle(fontSize: 15)),
+      actions: buttons));
 }
 
 Color str2color(String str, [alpha = 0xFF]) {
diff --git a/lib/main.dart b/lib/main.dart
index 7a6205d13..f204a6907 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,5 +1,5 @@
 import 'package:flutter/material.dart';
-import 'package:flutter_easyloading/flutter_easyloading.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import 'package:provider/provider.dart';
 import 'package:firebase_analytics/firebase_analytics.dart';
 import 'package:firebase_core/firebase_core.dart';
@@ -16,7 +16,7 @@ Future<Null> main() async {
   await a;
   await b;
   refreshCurrentUser();
-  EasyLoading.instance.loadingStyle = EasyLoadingStyle.light;
+  // EasyLoading.instance.loadingStyle = EasyLoadingStyle.light;
   toAndroidChannelInit();
   runApp(App());
 }
@@ -43,14 +43,15 @@ class App extends StatelessWidget {
         home: !isAndroid ? WebHomePage() : HomePage(),
         navigatorObservers: [
           FirebaseAnalyticsObserver(analytics: analytics),
+          FlutterSmartDialog.observer
         ],
         builder: isAndroid
             ? (_, child) {
                 return AccessibilityListener(
-                  child: FlutterEasyLoading(child: child),
+                  child: FlutterSmartDialog(child: child),
                 );
               }
-            : EasyLoading.init(),
+            : FlutterSmartDialog.init(),
       ),
     );
   }
diff --git a/lib/models/file_model.dart b/lib/models/file_model.dart
index 1eaf9b4cc..10325f2b0 100644
--- a/lib/models/file_model.dart
+++ b/lib/models/file_model.dart
@@ -1,9 +1,9 @@
 import 'dart:async';
 import 'dart:convert';
-import 'package:flutter_easyloading/flutter_easyloading.dart';
 import 'package:flutter_hbb/common.dart';
 import 'package:flutter_hbb/pages/file_manager_page.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import 'package:path/path.dart' as Path;
 
 import 'model.dart';
@@ -176,8 +176,7 @@ class FileModel extends ChangeNotifier {
   }
 
   onClose() {
-    DialogManager.reset();
-    EasyLoading.dismiss();
+    SmartDialog.dismiss();
 
     // save config
     Map<String, String> msg = Map();
@@ -289,7 +288,7 @@ class FileModel extends ChangeNotifier {
           fd.path = item.path;
         }
         fd.format(isWindows);
-        EasyLoading.dismiss();
+        SmartDialog.dismiss();
         if (fd.entries.isEmpty) {
           final confirm = await showRemoveDialog(
               translate(
@@ -346,49 +345,50 @@ class FileModel extends ChangeNotifier {
 
   Future<bool?> showRemoveDialog(
       String title, String content, bool showCheckbox) async {
-    return await DialogManager.show<bool>((setState, Function(bool v) close) =>
-        CustomAlertDialog(
-            title: Row(
-              children: [
-                Icon(Icons.warning, color: Colors.red),
-                SizedBox(width: 20),
-                Text(title)
-              ],
-            ),
-            content: Column(
-                crossAxisAlignment: CrossAxisAlignment.start,
-                mainAxisSize: MainAxisSize.min,
-                children: [
-                  Text(content),
-                  SizedBox(height: 5),
-                  Text(translate("This is irreversible!"),
-                      style: TextStyle(fontWeight: FontWeight.bold)),
-                  showCheckbox
-                      ? CheckboxListTile(
-                          contentPadding: const EdgeInsets.all(0),
-                          dense: true,
-                          controlAffinity: ListTileControlAffinity.leading,
-                          title: Text(
-                            translate("Do this for all conflicts"),
-                          ),
-                          value: removeCheckboxRemember,
-                          onChanged: (v) {
-                            if (v == null) return;
-                            setState(() => removeCheckboxRemember = v);
-                          },
-                        )
-                      : SizedBox.shrink()
+    return await DialogManager.show<bool>(
+        (setState, Function(bool v) close) => CustomAlertDialog(
+                title: Row(
+                  children: [
+                    Icon(Icons.warning, color: Colors.red),
+                    SizedBox(width: 20),
+                    Text(title)
+                  ],
+                ),
+                content: Column(
+                    crossAxisAlignment: CrossAxisAlignment.start,
+                    mainAxisSize: MainAxisSize.min,
+                    children: [
+                      Text(content),
+                      SizedBox(height: 5),
+                      Text(translate("This is irreversible!"),
+                          style: TextStyle(fontWeight: FontWeight.bold)),
+                      showCheckbox
+                          ? CheckboxListTile(
+                              contentPadding: const EdgeInsets.all(0),
+                              dense: true,
+                              controlAffinity: ListTileControlAffinity.leading,
+                              title: Text(
+                                translate("Do this for all conflicts"),
+                              ),
+                              value: removeCheckboxRemember,
+                              onChanged: (v) {
+                                if (v == null) return;
+                                setState(() => removeCheckboxRemember = v);
+                              },
+                            )
+                          : SizedBox.shrink()
+                    ]),
+                actions: [
+                  TextButton(
+                      style: flatButtonStyle,
+                      onPressed: () => close(false),
+                      child: Text(translate("Cancel"))),
+                  TextButton(
+                      style: flatButtonStyle,
+                      onPressed: () => close(true),
+                      child: Text(translate("OK"))),
                 ]),
-            actions: [
-              TextButton(
-                  style: flatButtonStyle,
-                  onPressed: () => close(false),
-                  child: Text(translate("Cancel"))),
-              TextButton(
-                  style: flatButtonStyle,
-                  onPressed: () => close(true),
-                  child: Text(translate("OK"))),
-            ]));
+        useAnimation: false);
   }
 
   sendRemoveFile(String path, int fileNum, bool isLocal) {
@@ -526,7 +526,7 @@ class FileFetcher {
       final fd = FileDirectory.fromJson(jsonDecode(msg));
       if (fd.id > 0) {
         // fd.id > 0 is result for read recursive
-        // TODO later,will be better if every fetch use ID,so that there will only one task map for read and recursive read
+        // to-do later,will be better if every fetch use ID,so that there will only one task map for read and recursive read
         tasks = readRecursiveTasks;
         final completer = tasks.remove(fd.id);
         completer?.complete(fd);
diff --git a/lib/models/model.dart b/lib/models/model.dart
index 9979532b1..e12ec9c6c 100644
--- a/lib/models/model.dart
+++ b/lib/models/model.dart
@@ -1,8 +1,8 @@
 import 'package:flutter/services.dart';
-import 'package:flutter_easyloading/flutter_easyloading.dart';
 import 'package:flutter_hbb/models/chat_model.dart';
 import 'package:flutter_hbb/models/file_model.dart';
 import 'package:flutter_hbb/models/server_model.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import 'package:shared_preferences/shared_preferences.dart';
 import 'dart:math';
 import 'dart:convert';
@@ -159,7 +159,7 @@ class FfiModel with ChangeNotifier {
       if (rgba != null) {
         if (_waitForImage) {
           _waitForImage = false;
-          EasyLoading.dismiss();
+          SmartDialog.dismiss();
         }
         _decoding = true;
         final pid = FFI.id;
@@ -221,8 +221,7 @@ class FfiModel with ChangeNotifier {
   }
 
   void handlePeerInfo(Map<String, dynamic> evt) {
-    EasyLoading.dismiss();
-    DialogManager.reset();
+    SmartDialog.dismiss();
     _pi.version = evt['version'];
     _pi.username = evt['username'];
     _pi.hostname = evt['hostname'];
@@ -484,16 +483,11 @@ class CursorModel with ChangeNotifier {
   void updatePan(double dx, double dy, bool touchMode) {
     if (FFI.imageModel.image == null) return;
     if (touchMode) {
-      if (true) {
-        final scale = FFI.canvasModel.scale;
-        _x += dx / scale;
-        _y += dy / scale;
-        FFI.moveMouse(_x, _y);
-        notifyListeners();
-      } else {
-        FFI.canvasModel.panX(dx);
-        FFI.canvasModel.panY(dy);
-      }
+      final scale = FFI.canvasModel.scale;
+      _x += dx / scale;
+      _y += dy / scale;
+      FFI.moveMouse(_x, _y);
+      notifyListeners();
       return;
     }
     final scale = FFI.canvasModel.scale;
diff --git a/lib/models/server_model.dart b/lib/models/server_model.dart
index 69d4822f1..37538aaa5 100644
--- a/lib/models/server_model.dart
+++ b/lib/models/server_model.dart
@@ -6,6 +6,7 @@ import '../common.dart';
 import '../pages/server_page.dart';
 import 'model.dart';
 
+const loginDialogTag = "LOGIN";
 final _emptyIdShow = translate("Generating ...");
 
 class ServerModel with ChangeNotifier {
@@ -306,70 +307,89 @@ class ServerModel with ChangeNotifier {
     }
   }
 
-  loginRequest(Map<String, dynamic> evt) {
+  void loginRequest(Map<String, dynamic> evt) {
     try {
       final client = Client.fromJson(jsonDecode(evt["client"]));
-      final Map<String, dynamic> response = Map();
-      response["id"] = client.id;
-      DialogManager.show(
-          (setState, close) => CustomAlertDialog(
-                title: Row(
-                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
-                    children: [
-                      Text(translate(client.isFileTransfer
-                          ? "File Connection"
-                          : "Screen Connection")),
-                      IconButton(onPressed: close, icon: Icon(Icons.close))
-                    ]),
-                content: Column(
-                  mainAxisSize: MainAxisSize.min,
-                  mainAxisAlignment: MainAxisAlignment.center,
-                  crossAxisAlignment: CrossAxisAlignment.start,
-                  children: [
-                    Text(translate("Do you accept?")),
-                    clientInfo(client),
-                    Text(
-                      translate("android_new_connection_tip"),
-                      style: TextStyle(color: Colors.black54),
-                    ),
-                  ],
-                ),
-                actions: [
-                  TextButton(
-                      child: Text(translate("Dismiss")),
-                      onPressed: () {
-                        response["res"] = false;
-                        FFI.setByName("login_res", jsonEncode(response));
-                        FFI.invokeMethod("cancel_notification", client.id);
-                        close();
-                      }),
-                  ElevatedButton(
-                      child: Text(translate("Accept")),
-                      onPressed: () async {
-                        response["res"] = true;
-                        FFI.setByName("login_res", jsonEncode(response));
-                        if (!client.isFileTransfer) {
-                          FFI.invokeMethod("start_capture");
-                        }
-                        FFI.invokeMethod("cancel_notification", client.id);
-                        _clients[client.id] = client;
-                        notifyListeners();
-                        close();
-                      }),
-                ],
-                onWillPop: () async => true,
-              ),
-          barrierDismissible: true);
+      if (_clients.containsKey(client.id)) {
+        return;
+      }
+      _clients[client.id] = client;
+      notifyListeners();
+      showLoginDialog(client);
     } catch (e) {
-      debugPrint("loginRequest failed,error:$e");
+      debugPrint("Failed to call loginRequest,error:$e");
+    }
+  }
+
+  void showLoginDialog(Client client) {
+    DialogManager.show(
+        (setState, close) => CustomAlertDialog(
+              title: Row(
+                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                  children: [
+                    Text(translate(client.isFileTransfer
+                        ? "File Connection"
+                        : "Screen Connection")),
+                    IconButton(
+                        onPressed: () {
+                          close();
+                        },
+                        icon: Icon(Icons.close))
+                  ]),
+              content: Column(
+                mainAxisSize: MainAxisSize.min,
+                mainAxisAlignment: MainAxisAlignment.center,
+                crossAxisAlignment: CrossAxisAlignment.start,
+                children: [
+                  Text(translate("Do you accept?")),
+                  clientInfo(client),
+                  Text(
+                    translate("android_new_connection_tip"),
+                    style: TextStyle(color: Colors.black54),
+                  ),
+                ],
+              ),
+              actions: [
+                TextButton(
+                    child: Text(translate("Dismiss")),
+                    onPressed: () {
+                      sendLoginResponse(client, false);
+                      close();
+                    }),
+                ElevatedButton(
+                    child: Text(translate("Accept")),
+                    onPressed: () {
+                      sendLoginResponse(client, true);
+                      close();
+                    }),
+              ],
+            ),
+        tag: getLoginDialogTag(client.id));
+  }
+
+  void sendLoginResponse(Client client, bool res) {
+    final Map<String, dynamic> response = Map();
+    response["id"] = client.id;
+    response["res"] = res;
+    if (res) {
+      FFI.setByName("login_res", jsonEncode(response));
+      if (!client.isFileTransfer) {
+        FFI.invokeMethod("start_capture");
+      }
+      FFI.invokeMethod("cancel_notification", client.id);
+      _clients[client.id]?.authorized = true;
+      notifyListeners();
+    } else {
+      FFI.setByName("login_res", jsonEncode(response));
+      FFI.invokeMethod("cancel_notification", client.id);
+      _clients.remove(client.id);
     }
   }
 
   void onClientAuthorized(Map<String, dynamic> evt) {
     try {
       final client = Client.fromJson(jsonDecode(evt['client']));
-      // reset the login dialog, to-do,it will close any showing dialog
-      DialogManager.reset();
+      DialogManager.dismissByTag(getLoginDialogTag(client.id));
       _clients[client.id] = client;
       notifyListeners();
     } catch (e) {}
@@ -380,9 +400,7 @@ class ServerModel with ChangeNotifier {
       final id = int.parse(evt['id'] as String);
       if (_clients.containsKey(id)) {
         _clients.remove(id);
-      } else {
-        // reset the login dialog, to-do,it will close any showing dialog
-        DialogManager.reset();
+        DialogManager.dismissByTag(getLoginDialogTag(id));
         FFI.invokeMethod("cancel_notification", id);
       }
       notifyListeners();
@@ -442,39 +460,31 @@ class Client {
   }
 }
 
-showInputWarnAlert() async {
-  if (globalKey.currentContext == null) return;
-  DialogManager.reset();
-  await showDialog<bool>(
-      context: globalKey.currentContext!,
-      builder: (alertContext) {
-        DialogManager.register(alertContext);
-        return AlertDialog(
-          title: Text(translate("How to get Android input permission?")),
-          content: Column(
-            mainAxisSize: MainAxisSize.min,
-            children: [
-              Text(translate(translate("android_input_permission_tip1"))),
-              SizedBox(height: 10),
-              Text(translate(translate("android_input_permission_tip2"))),
-            ],
-          ),
-          actions: [
-            TextButton(
-                child: Text(translate("Cancel")),
-                onPressed: () {
-                  DialogManager.reset();
-                }),
-            ElevatedButton(
-                child: Text(translate("Open System Setting")),
-                onPressed: () {
-                  FFI.serverModel.initInput();
-                  DialogManager.reset();
-                }),
+String getLoginDialogTag(int id) {
+  return loginDialogTag + id.toString();
+}
+
+showInputWarnAlert() {
+  DialogManager.show((setState, close) => CustomAlertDialog(
+        title: Text(translate("How to get Android input permission?")),
+        content: Column(
+          mainAxisSize: MainAxisSize.min,
+          children: [
+            Text(translate(translate("android_input_permission_tip1"))),
+            SizedBox(height: 10),
+            Text(translate(translate("android_input_permission_tip2"))),
           ],
-        );
-      });
-  DialogManager.drop();
+        ),
+        actions: [
+          TextButton(child: Text(translate("Cancel")), onPressed: close),
+          ElevatedButton(
+              child: Text(translate("Open System Setting")),
+              onPressed: () {
+                FFI.serverModel.initInput();
+                close();
+              }),
+        ],
+      ));
 }
 
 class PermissionManager {
diff --git a/lib/pages/file_manager_page.dart b/lib/pages/file_manager_page.dart
index fc628f681..ab18de948 100644
--- a/lib/pages/file_manager_page.dart
+++ b/lib/pages/file_manager_page.dart
@@ -1,7 +1,7 @@
 import 'dart:async';
 import 'package:flutter/material.dart';
-import 'package:flutter_easyloading/flutter_easyloading.dart';
 import 'package:flutter_hbb/models/file_model.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import 'package:provider/provider.dart';
 import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
 import 'package:toggle_switch/toggle_switch.dart';
@@ -27,10 +27,12 @@ class _FileManagerPageState extends State<FileManagerPage> {
   @override
   void initState() {
     super.initState();
-    showLoading(translate('Connecting...'));
     FFI.connect(widget.id, isFileTransfer: true);
-    _interval = Timer.periodic(
-        Duration(milliseconds: 30), (timer) => FFI.ffiModel.update(widget.id));
+    WidgetsBinding.instance!.addPostFrameCallback((_) {
+      showLoading(translate('Connecting...'));
+      _interval = Timer.periodic(
+          Duration(milliseconds: 30), (timer) => FFI.ffiModel.update(widget.id));
+    });
   }
 
   @override
@@ -38,7 +40,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
     model.onClose();
     _interval?.cancel();
     FFI.close();
-    EasyLoading.dismiss();
+    SmartDialog.dismiss();
     super.dispose();
   }
 
diff --git a/lib/pages/remote_page.dart b/lib/pages/remote_page.dart
index 1d473dd11..8c9d145a0 100644
--- a/lib/pages/remote_page.dart
+++ b/lib/pages/remote_page.dart
@@ -1,7 +1,7 @@
 import 'package:flutter/material.dart';
-import 'package:flutter_easyloading/flutter_easyloading.dart';
 import 'package:flutter_hbb/models/chat_model.dart';
 import 'package:flutter_hbb/widgets/gesture_help.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import 'package:provider/provider.dart';
 import 'package:flutter/services.dart';
 import 'dart:ui' as ui;
@@ -58,7 +58,7 @@ class _RemotePageState extends State<RemotePage> {
     FFI.close();
     _interval?.cancel();
     _timer?.cancel();
-    EasyLoading.dismiss();
+    SmartDialog.dismiss();
     SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
         overlays: SystemUiOverlay.values);
     Wakelock.disable();
@@ -800,7 +800,7 @@ void showOptions() {
           onTap: () {
             if (i == cur) return;
             FFI.setByName('switch_display', i.toString());
-            DialogManager.reset();
+            SmartDialog.dismiss();
           },
           child: Ink(
               width: 40,
diff --git a/lib/pages/server_page.dart b/lib/pages/server_page.dart
index 64a15672c..01f0464c4 100644
--- a/lib/pages/server_page.dart
+++ b/lib/pages/server_page.dart
@@ -1,6 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_hbb/models/model.dart';
 import 'package:flutter_hbb/widgets/dialog.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import 'package:provider/provider.dart';
 
 import '../common.dart';
@@ -322,7 +323,7 @@ class ConnectionManager extends StatelessWidget {
                       mainAxisAlignment: MainAxisAlignment.spaceBetween,
                       children: [
                         clientInfo(entry.value),
-                        entry.value.isFileTransfer
+                        entry.value.isFileTransfer || !entry.value.authorized
                             ? SizedBox.shrink()
                             : IconButton(
                                 onPressed: () {
@@ -339,16 +340,33 @@ class ConnectionManager extends StatelessWidget {
                                 ))
                       ],
                     ),
-                    ElevatedButton.icon(
+                    entry.value.authorized?SizedBox.shrink():Text(
+                      translate("android_new_connection_tip"),
+                      style: TextStyle(color: Colors.black54),
+                    ),
+                    entry.value.authorized? ElevatedButton.icon(
                         style: ButtonStyle(
                             backgroundColor:
-                                MaterialStateProperty.all(Colors.red)),
+                            MaterialStateProperty.all(Colors.red)),
                         icon: Icon(Icons.close),
                         onPressed: () {
                           FFI.setByName("close_conn", entry.key.toString());
                           FFI.invokeMethod("cancel_notification", entry.key);
                         },
-                        label: Text(translate("Close")))
+                        label: Text(translate("Close"))):
+                    Row(children: [
+                      TextButton(
+                          child: Text(translate("Dismiss")),
+                          onPressed: () {
+                            serverModel.sendLoginResponse(entry.value,false);
+                          }),
+                      SizedBox(width: 20),
+                      ElevatedButton(
+                          child: Text(translate("Accept")),
+                          onPressed: () {
+                            serverModel.sendLoginResponse(entry.value,true);
+                          }),
+                    ]),
                   ],
                 )))
             .toList());
@@ -436,7 +454,7 @@ void toAndroidChannelInit() {
       switch (method) {
         case "start_capture":
           {
-            DialogManager.reset();
+            SmartDialog.dismiss();
             FFI.serverModel.updateClientState();
             break;
           }
diff --git a/lib/pages/settings_page.dart b/lib/pages/settings_page.dart
index 91dac8d75..9ab4ac491 100644
--- a/lib/pages/settings_page.dart
+++ b/lib/pages/settings_page.dart
@@ -1,4 +1,3 @@
-import 'package:flutter_easyloading/flutter_easyloading.dart';
 import 'package:settings_ui/settings_ui.dart';
 import 'package:flutter/material.dart';
 import 'package:url_launcher/url_launcher.dart';
@@ -226,8 +225,7 @@ void logout() async {
         },
         body: json.encode(body));
   } catch (e) {
-    EasyLoading.showToast('Failed to access $url',
-        maskType: EasyLoadingMaskType.black);
+    showToast('Failed to access $url');
   }
   resetToken();
 }
diff --git a/lib/widgets/dialog.dart b/lib/widgets/dialog.dart
index a5e7d24e3..e37a030b7 100644
--- a/lib/widgets/dialog.dart
+++ b/lib/widgets/dialog.dart
@@ -1,5 +1,5 @@
 import 'package:flutter/material.dart';
-import 'package:flutter_easyloading/flutter_easyloading.dart';
+import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 import '../common.dart';
 import '../models/model.dart';
 
@@ -8,20 +8,14 @@ void clientClose() {
 }
 
 const SEC1 = Duration(seconds: 1);
-void showSuccess({Duration duration = SEC1}){
-  EasyLoading.dismiss();
-  EasyLoading.showSuccess(translate("Successful"),
-      duration: duration,
-      dismissOnTap: true,
-      maskType: EasyLoadingMaskType.black);
+void showSuccess({Duration duration = SEC1}) {
+  SmartDialog.dismiss();
+  showToast(translate("Successful"),duration:SEC1);
 }
 
 void showError({Duration duration = SEC1}){
-  EasyLoading.dismiss();
-  EasyLoading.showError(translate("Error"),
-      duration: duration,
-      dismissOnTap: true,
-      maskType: EasyLoadingMaskType.black);
+  SmartDialog.dismiss();
+  showToast(translate("Error"),duration:SEC1);
 }
 
 void updatePasswordDialog(){
diff --git a/pubspec.lock b/pubspec.lock
index 72376a01c..7dae3bba4 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -202,13 +202,6 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.0.1"
-  flutter_easyloading:
-    dependency: "direct main"
-    description:
-      name: flutter_easyloading
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.3"
   flutter_launcher_icons:
     dependency: "direct dev"
     description:
@@ -230,13 +223,15 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.0.5"
-  flutter_spinkit:
-    dependency: transitive
+  flutter_smart_dialog:
+    dependency: "direct main"
     description:
-      name: flutter_spinkit
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "5.1.0"
+      path: "."
+      ref: HEAD
+      resolved-ref: "594530edbac758cde29c614046dcc107345ba791"
+      url: "https://github.com/Heap-Hop/flutter_smart_dialog.git"
+    source: git
+    version: "4.0.0"
   flutter_test:
     dependency: "direct dev"
     description: flutter
diff --git a/pubspec.yaml b/pubspec.yaml
index 1918813a0..80abb2d6d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -32,7 +32,6 @@ dependencies:
   path_provider: ^2.0.2
   external_path: ^1.0.1
   provider: ^5.0.0
-  flutter_easyloading: ^3.0.3
   tuple: ^2.0.0
   wakelock: ^0.5.2
   device_info: ^2.0.2 
@@ -50,6 +49,9 @@ dependencies:
   zxing2: ^0.1.0
   image_picker: ^0.8.5
   image: ^3.1.3
+  flutter_smart_dialog:
+    git:
+      url: https://github.com/Heap-Hop/flutter_smart_dialog.git
 
 dev_dependencies:
   flutter_launcher_icons: ^0.9.1