diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart
index 8a8992a1e..702b4eb79 100644
--- a/flutter/lib/common/widgets/address_book.dart
+++ b/flutter/lib/common/widgets/address_book.dart
@@ -87,7 +87,10 @@ class _AddressBookState extends State<AddressBook> {
                 child: Column(
                   children: [
                     _buildAbDropdown(),
-                    _buildTagHeader().marginOnly(left: 8.0, right: 0),
+                    _buildTagHeader().marginOnly(
+                        left: 8.0,
+                        right: gFFI.abModel.legacyMode.value ? 8.0 : 0,
+                        top: gFFI.abModel.legacyMode.value ? 8.0 : 0),
                     Expanded(
                       child: Container(
                         width: double.infinity,
@@ -415,6 +418,7 @@ class _AddressBookState extends State<AddressBook> {
       return;
     }
     var isInProgress = false;
+    var passwordVisible = false;
     IDTextEditingController idController = IDTextEditingController(text: '');
     TextEditingController aliasController = TextEditingController(text: '');
     TextEditingController passwordController = TextEditingController(text: '');
@@ -460,6 +464,24 @@ class _AddressBookState extends State<AddressBook> {
       }
 
       double marginBottom = 4;
+
+      row({required Widget lable, required Widget input}) {
+        return Row(
+          children: [
+            !isMobile
+                ? ConstrainedBox(
+                    constraints: const BoxConstraints(minWidth: 100),
+                    child: lable.marginOnly(right: 10))
+                : SizedBox.shrink(),
+            Expanded(
+              child: ConstrainedBox(
+                  constraints: const BoxConstraints(minWidth: 200),
+                  child: input),
+            ),
+          ],
+        ).marginOnly(bottom: !isMobile ? 8 : 0);
+      }
+
       return CustomAlertDialog(
         title: Text(translate("Add ID")),
         content: Column(
@@ -467,75 +489,90 @@ class _AddressBookState extends State<AddressBook> {
           children: [
             Column(
               children: [
-                Align(
-                  alignment: Alignment.centerLeft,
-                  child: Row(
-                    children: [
-                      Text(
-                        '*',
-                        style: TextStyle(color: Colors.red, fontSize: 14),
-                      ),
-                      Text(
-                        'ID',
-                        style: style,
-                      ),
-                    ],
-                  ),
-                ).marginOnly(bottom: marginBottom),
-                TextField(
-                  controller: idController,
-                  inputFormatters: [IDTextInputFormatter()],
-                  decoration:
-                      InputDecoration(errorText: errorMsg, errorMaxLines: 5),
-                ),
-                Align(
-                  alignment: Alignment.centerLeft,
-                  child: Text(
+                row(
+                    lable: Row(
+                      children: [
+                        Text(
+                          '*',
+                          style: TextStyle(color: Colors.red, fontSize: 14),
+                        ),
+                        Text(
+                          'ID',
+                          style: style,
+                        ),
+                      ],
+                    ),
+                    input: TextField(
+                      controller: idController,
+                      inputFormatters: [IDTextInputFormatter()],
+                      decoration: InputDecoration(
+                          labelText: !isMobile ? null : translate('ID'),
+                          errorText: errorMsg,
+                          errorMaxLines: 5),
+                    )),
+                row(
+                  lable: Text(
                     translate('Alias'),
                     style: style,
                   ),
-                ).marginOnly(top: 8, bottom: marginBottom),
-                TextField(
-                  controller: aliasController,
+                  input: TextField(
+                      controller: aliasController,
+                      decoration: InputDecoration(
+                        labelText: !isMobile ? null : translate('Alias'),
+                      )),
                 ),
                 if (isCurrentAbShared)
+                  row(
+                      lable: Text(
+                        translate('Password'),
+                        style: style,
+                      ),
+                      input: TextField(
+                        controller: passwordController,
+                        obscureText: !passwordVisible,
+                        decoration: InputDecoration(
+                          labelText: !isMobile ? null : translate('Password'),
+                          suffixIcon: IconButton(
+                            icon: Icon(
+                                passwordVisible
+                                    ? Icons.visibility
+                                    : Icons.visibility_off,
+                                color: MyTheme.lightTheme.primaryColor),
+                            onPressed: () {
+                              setState(() {
+                                passwordVisible = !passwordVisible;
+                              });
+                            },
+                          ),
+                        ),
+                      )),
+                if (gFFI.abModel.currentAbTags.isNotEmpty)
                   Align(
                     alignment: Alignment.centerLeft,
                     child: Text(
-                      translate('Password'),
+                      translate('Tags'),
                       style: style,
                     ),
                   ).marginOnly(top: 8, bottom: marginBottom),
-                if (isCurrentAbShared)
-                  TextField(
-                    controller: passwordController,
-                    obscureText: true,
+                if (gFFI.abModel.currentAbTags.isNotEmpty)
+                  Align(
+                    alignment: Alignment.centerLeft,
+                    child: Wrap(
+                      children: tags
+                          .map((e) => AddressBookTag(
+                              name: e,
+                              tags: selectedTag,
+                              onTap: () {
+                                if (selectedTag.contains(e)) {
+                                  selectedTag.remove(e);
+                                } else {
+                                  selectedTag.add(e);
+                                }
+                              },
+                              showActionMenu: false))
+                          .toList(growable: false),
+                    ),
                   ),
-                Align(
-                  alignment: Alignment.centerLeft,
-                  child: Text(
-                    translate('Tags'),
-                    style: style,
-                  ),
-                ).marginOnly(top: 8, bottom: marginBottom),
-                Align(
-                  alignment: Alignment.centerLeft,
-                  child: Wrap(
-                    children: tags
-                        .map((e) => AddressBookTag(
-                            name: e,
-                            tags: selectedTag,
-                            onTap: () {
-                              if (selectedTag.contains(e)) {
-                                selectedTag.remove(e);
-                              } else {
-                                selectedTag.add(e);
-                              }
-                            },
-                            showActionMenu: false))
-                        .toList(growable: false),
-                  ),
-                ),
               ],
             ),
             const SizedBox(
diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart
index be1df6539..b98b2b248 100644
--- a/flutter/lib/common/widgets/dialog.dart
+++ b/flutter/lib/common/widgets/dialog.dart
@@ -1918,11 +1918,9 @@ void addPeersToAbDialog(
   Future<bool> addTo(String abname) async {
     final mapList = peers.map((e) {
       var json = e.toJson();
-      // remove shared password when add to other address book
+      // remove password when add to another address book to avoid re-share
       json.remove('password');
-      if (gFFI.abModel.addressbooks[abname]?.isPersonal() != true) {
-        json.remove('hash');
-      }
+      json.remove('hash');
       return json;
     }).toList();
     final errMsg = await gFFI.abModel.addPeersTo(mapList, abname);
@@ -1986,6 +1984,7 @@ void addPeersToAbDialog(
       content: Obx(() => Column(
             crossAxisAlignment: CrossAxisAlignment.center,
             children: [
+              // https://github.com/flutter/flutter/issues/145081
               DropdownMenu(
                 initialSelection: currentName.value,
                 onSelected: (value) {
@@ -2026,18 +2025,23 @@ void addPeersToAbDialog(
 }
 
 void setSharedAbPasswordDialog(String abName, Peer peer) {
-  TextEditingController controller = TextEditingController(text: peer.password);
+  TextEditingController controller = TextEditingController(text: '');
   RxBool isInProgress = false.obs;
+  RxBool isInputEmpty = true.obs;
+  bool passwordVisible = false;
+  controller.addListener(() {
+    isInputEmpty.value = controller.text.isEmpty;
+  });
   gFFI.dialogManager.show((setState, close, context) {
-    submit() async {
+    change(String password) async {
       isInProgress.value = true;
-      bool res = await gFFI.abModel
-          .changeSharedPassword(abName, peer.id, controller.text);
-      close();
+      bool res =
+          await gFFI.abModel.changeSharedPassword(abName, peer.id, password);
       isInProgress.value = false;
       if (res) {
         showToast(translate('Successful'));
       }
+      close();
     }
 
     cancel() {
@@ -2049,22 +2053,38 @@ void setSharedAbPasswordDialog(String abName, Peer peer) {
         mainAxisAlignment: MainAxisAlignment.center,
         children: [
           Icon(Icons.key, color: MyTheme.accent),
-          Text(translate('Set shared password')).paddingOnly(left: 10),
+          Text(translate(peer.password.isEmpty
+                  ? 'Set shared password'
+                  : 'Change Password'))
+              .paddingOnly(left: 10),
         ],
       ),
       content: Obx(() => Column(children: [
             TextField(
               controller: controller,
-              obscureText: true,
               autofocus: true,
+              obscureText: !passwordVisible,
+              decoration: InputDecoration(
+                suffixIcon: IconButton(
+                  icon: Icon(
+                      passwordVisible ? Icons.visibility : Icons.visibility_off,
+                      color: MyTheme.lightTheme.primaryColor),
+                  onPressed: () {
+                    setState(() {
+                      passwordVisible = !passwordVisible;
+                    });
+                  },
+                ),
+              ),
             ),
-            Row(children: [
-              Icon(Icons.info, color: Colors.amber).marginOnly(right: 4),
-              Text(
-                translate('share_warning_tip'),
-                style: TextStyle(fontSize: 12),
-              )
-            ]).marginSymmetric(vertical: 10),
+            if (!gFFI.abModel.current.isPersonal())
+              Row(children: [
+                Icon(Icons.info, color: Colors.amber).marginOnly(right: 4),
+                Text(
+                  translate('share_warning_tip'),
+                  style: TextStyle(fontSize: 12),
+                )
+              ]).marginSymmetric(vertical: 10),
             // NOT use Offstage to wrap LinearProgressIndicator
             isInProgress.value ? const LinearProgressIndicator() : Offstage()
           ])),
@@ -2075,13 +2095,22 @@ void setSharedAbPasswordDialog(String abName, Peer peer) {
           onPressed: cancel,
           isOutline: true,
         ),
-        dialogButton(
-          "OK",
-          icon: Icon(Icons.done_rounded),
-          onPressed: submit,
-        ),
+        if (peer.password.isNotEmpty)
+          dialogButton(
+            "Remove",
+            icon: Icon(Icons.delete_outline_rounded),
+            onPressed: () => change(''),
+            buttonStyle: ButtonStyle(
+                backgroundColor: MaterialStatePropertyAll(Colors.red)),
+          ),
+        Obx(() => dialogButton(
+              "OK",
+              icon: Icon(Icons.done_rounded),
+              onPressed:
+                  isInputEmpty.value ? null : () => change(controller.text),
+            )),
       ],
-      onSubmit: submit,
+      onSubmit: isInputEmpty.value ? null : () => change(controller.text),
       onCancel: cancel,
     );
   });
diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart
index 3efebf4c7..d1aede0b6 100644
--- a/flutter/lib/common/widgets/peer_card.dart
+++ b/flutter/lib/common/widgets/peer_card.dart
@@ -139,21 +139,30 @@ class _PeerCardState extends State<_PeerCard>
       mainAxisSize: MainAxisSize.max,
       children: [
         Container(
-          decoration: BoxDecoration(
-            color: str2color('${peer.id}${peer.platform}', 0x7f),
-            borderRadius: isMobile
-                ? BorderRadius.circular(_tileRadius)
-                : BorderRadius.only(
-                    topLeft: Radius.circular(_tileRadius),
-                    bottomLeft: Radius.circular(_tileRadius),
+            decoration: BoxDecoration(
+              color: str2color('${peer.id}${peer.platform}', 0x7f),
+              borderRadius: isMobile
+                  ? BorderRadius.circular(_tileRadius)
+                  : BorderRadius.only(
+                      topLeft: Radius.circular(_tileRadius),
+                      bottomLeft: Radius.circular(_tileRadius),
+                    ),
+            ),
+            alignment: Alignment.center,
+            width: isMobile ? 50 : 42,
+            height: isMobile ? 50 : null,
+            child: Stack(
+              children: [
+                getPlatformImage(peer.platform, size: isMobile ? 38 : 30)
+                    .paddingAll(6),
+                if (_shouldBuildPasswordIcon(peer))
+                  Positioned(
+                    top: 1,
+                    left: 1,
+                    child: Icon(Icons.key, size: 6, color: Colors.white),
                   ),
-          ),
-          alignment: Alignment.center,
-          width: isMobile ? 50 : 42,
-          height: isMobile ? 50 : null,
-          child: getPlatformImage(peer.platform, size: isMobile ? 38 : 30)
-              .paddingAll(6),
-        ),
+              ],
+            )),
         Expanded(
           child: Container(
             decoration: BoxDecoration(
@@ -216,12 +225,6 @@ class _PeerCardState extends State<_PeerCard>
                   child: child,
                 ),
               ),
-        if (_shouldBuildPasswordIcon(peer))
-          Positioned(
-            top: 2,
-            left: isMobile ? 60 : 50,
-            child: Icon(Icons.key, size: 12),
-          ),
         if (colors.isNotEmpty)
           Positioned(
             top: 2,
@@ -329,7 +332,7 @@ class _PeerCardState extends State<_PeerCard>
           Positioned(
             top: 4,
             left: 12,
-            child: Icon(Icons.key, size: 12),
+            child: Icon(Icons.key, size: 12, color: Colors.white),
           ),
         if (colors.isNotEmpty)
           Positioned(
@@ -1102,7 +1105,8 @@ class AddressBookPeerCard extends BasePeerCard {
   MenuEntryBase<String> _changeSharedAbPassword() {
     return MenuEntryButton<String>(
       childBuilder: (TextStyle? style) => Text(
-        translate('Set shared password'),
+        translate(
+            peer.password.isEmpty ? 'Set shared password' : 'Change Password'),
         style: style,
       ),
       proc: () {
diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart
index ba13c5143..4f3a04a14 100644
--- a/flutter/lib/models/ab_model.dart
+++ b/flutter/lib/models/ab_model.dart
@@ -320,7 +320,7 @@ class AbModel {
       peer['password'] = password;
     }
     final ret = await addPeersTo([peer], _currentName.value);
-    _timerCounter = 0;
+    _syncAllFromRecent = true;
     return ret;
   }
 
@@ -364,7 +364,7 @@ class AbModel {
     final personalAb = addressbooks[_personalAddressBookName];
     if (personalAb != null) {
       ret = await personalAb.changePersonalHashPassword(id, hash);
-      await pullNonLegacyAfterChange();
+      await personalAb.pullAb(quiet: true);
     } else {
       final legacyAb = addressbooks[_legacyAddressBookName];
       if (legacyAb != null) {
@@ -377,9 +377,10 @@ class AbModel {
 
   Future<bool> changeSharedPassword(
       String abName, String id, String password) async {
-    final ret =
-        await addressbooks[abName]?.changeSharedPassword(id, password) ?? false;
-    await pullNonLegacyAfterChange();
+    final ab = addressbooks[abName];
+    if (ab == null) return false;
+    final ret = await ab.changeSharedPassword(id, password);
+    await ab.pullAb(quiet: true);
     return ret;
   }
 
@@ -538,9 +539,7 @@ class AbModel {
         "name": key,
         "tags": value.tags,
         "peers": value.peers
-            .map((e) => value.isPersonal()
-                ? e.toPersonalAbUploadJson(true)
-                : e.toSharedAbCacheJson())
+            .map((e) => e.toCustomJson(includingHash: value.isPersonal()))
             .toList(),
         "tag_colors": jsonEncode(value.tagColors)
       });
@@ -745,6 +744,10 @@ abstract class BaseAb {
         name() == _legacyAddressBookName;
   }
 
+  bool isLegacy() {
+    return name() == _legacyAddressBookName;
+  }
+
   Future<void> pullAb({quiet = false}) async {
     debugPrint("pull ab \"${name()}\"");
     if (abLoading.value) return;
@@ -1049,9 +1052,6 @@ class LegacyAb extends BaseAb {
     p.hostname = r.hostname.isEmpty ? p.hostname : r.hostname;
     p.platform = r.platform.isEmpty ? p.platform : r.platform;
     p.alias = p.alias.isEmpty ? r.alias : p.alias;
-    p.forceAlwaysRelay = r.forceAlwaysRelay;
-    p.rdpPort = r.rdpPort;
-    p.rdpUsername = r.rdpUsername;
   }
 
   @override
@@ -1151,7 +1151,7 @@ class LegacyAb extends BaseAb {
 
   Map<String, dynamic> _serialize() {
     final peersJsonData =
-        peers.map((e) => e.toPersonalAbUploadJson(true)).toList();
+        peers.map((e) => e.toCustomJson(includingHash: true)).toList();
     for (var e in tags) {
       if (tagColors[e] == null) {
         tagColors[e] = str2color2(e, existing: tagColors.values.toList()).value;
@@ -1491,38 +1491,55 @@ class Ab extends BaseAb {
   Future<bool> changePersonalHashPassword(String id, String hash) async {
     if (!personal) return false;
     if (!peers.any((e) => e.id == id)) return false;
-    return _setPassword({"id": id, "hash": hash});
+    return await _setPassword({"id": id, "hash": hash});
   }
 
   @override
   Future<bool> changeSharedPassword(String id, String password) async {
     if (personal) return false;
-    return _setPassword({"id": id, "password": password});
+    return await _setPassword({"id": id, "password": password});
   }
 
   @override
   Future<void> syncFromRecent(List<Peer> recents) async {
     bool uiUpdate = false;
-    bool peerSyncEqual(Peer a, Peer b) {
-      return a.username == b.username &&
-          a.platform == b.platform &&
-          a.hostname == b.hostname;
-    }
+    bool saveCache = false;
+    final api =
+        "${await bind.mainGetApiServer()}/api/ab/peer/update/${profile.guid}";
+    var headers = getHttpHeaders();
+    headers['Content-Type'] = "application/json";
 
-    Future<bool> syncOnePeer(Peer p, Peer r) async {
-      p.username = r.username;
-      p.hostname = r.hostname;
-      p.platform = r.platform;
-      final api =
-          "${await bind.mainGetApiServer()}/api/ab/peer/update/${profile.guid}";
-      var headers = getHttpHeaders();
-      headers['Content-Type'] = "application/json";
-      final body = jsonEncode({
-        "id": p.id,
-        "username": r.username,
-        "hostname": r.hostname,
-        "platform": r.platform
-      });
+    Future<bool> trySyncOnePeer(Peer p, Peer r) async {
+      var map = Map<String, String>.fromEntries([]);
+      if (p.sameServer != true &&
+          r.username.isNotEmpty &&
+          p.username != r.username) {
+        p.username = r.username;
+        map['username'] = r.username;
+      }
+      if (p.sameServer != true &&
+          r.hostname.isNotEmpty &&
+          p.hostname != r.hostname) {
+        p.hostname = r.hostname;
+        map['hostname'] = r.hostname;
+      }
+      if (p.sameServer != true &&
+          r.platform.isNotEmpty &&
+          p.platform != r.platform) {
+        p.platform = r.platform;
+        map['platform'] = r.platform;
+      }
+      if (personal && r.hash.isNotEmpty && p.hash != r.hash) {
+        p.hash = r.hash;
+        map['hash'] = r.hash;
+        saveCache = true;
+      }
+      if (map.isEmpty) {
+        // no need to sync
+        return false;
+      }
+      map['id'] = p.id;
+      final body = jsonEncode(map);
       final resp = await http.put(Uri.parse(api), headers: headers, body: body);
       final errMsg = _jsonDecodeActionResp(resp);
       if (errMsg.isNotEmpty) {
@@ -1534,35 +1551,20 @@ class Ab extends BaseAb {
     }
 
     try {
-      /* Remove this because IDs that are not on the server can't be synced, then sync will happen every startup.
-      // Try add new peers to personal ab
-      if (personal) {
-        for (var r in recents) {
-          if (peers.length < gFFI.abModel._maxPeerOneAb) {
-            if (!peers.any((e) => e.id == r.id)) {
-              var err = await addPeers([r.toPersonalAbUploadJson(true)]);
-              if (err == null) {
-                peers.add(r);
-                uiUpdate = true;
-              }
-            }
-          }
-        }
-      }
-      */
-      final syncPeers = peers.where((p0) => p0.sameServer != true);
-      for (var p in syncPeers) {
+      // Not add new peers because IDs that are not on the server can't be synced, then sync will happen every startup.
+      for (var p in peers) {
         Peer? r = recents.firstWhereOrNull((e) => e.id == p.id);
         if (r != null) {
-          if (!peerSyncEqual(p, r)) {
-            await syncOnePeer(p, r);
-          }
+          await trySyncOnePeer(p, r);
         }
       }
       // Pull cannot be used for sync to avoid cyclic sync.
       if (uiUpdate && gFFI.abModel.currentName.value == profile.name) {
         peers.refresh();
       }
+      if (saveCache) {
+        gFFI.abModel._saveCache();
+      }
     } catch (err) {
       debugPrint('syncFromRecent err: ${err.toString()}');
     }
diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart
index 3f316ef46..0a58aa023 100644
--- a/flutter/lib/models/model.dart
+++ b/flutter/lib/models/model.dart
@@ -352,13 +352,13 @@ class FfiModel with ChangeNotifier {
         handleReloading(evt);
       } else if (name == 'plugin_option') {
         handleOption(evt);
-      } else if (name == "sync_peer_password_to_ab") {
+      } else if (name == "sync_peer_hash_password_to_personal_ab") {
         if (desktopType == DesktopType.main) {
           final id = evt['id'];
-          final password = evt['password'];
-          if (id != null && password != null) {
+          final hash = evt['hash'];
+          if (id != null && hash != null) {
             gFFI.abModel
-                .changePersonalHashPassword(id.toString(), password.toString());
+                .changePersonalHashPassword(id.toString(), hash.toString());
           }
         }
       } else if (name == "cm_file_transfer_log") {
diff --git a/flutter/lib/models/peer_model.dart b/flutter/lib/models/peer_model.dart
index 8b853b3fe..188dd4e0b 100644
--- a/flutter/lib/models/peer_model.dart
+++ b/flutter/lib/models/peer_model.dart
@@ -61,7 +61,7 @@ class Peer {
     };
   }
 
-  Map<String, dynamic> toPersonalAbUploadJson(bool includingHash) {
+  Map<String, dynamic> toCustomJson({required bool includingHash}) {
     var res = <String, dynamic>{
       "id": id,
       "username": username,
@@ -76,32 +76,6 @@ class Peer {
     return res;
   }
 
-  Map<String, dynamic> toSharedAbUploadJson(bool includingPassword) {
-    var res = <String, dynamic>{
-      "id": id,
-      "username": username,
-      "hostname": hostname,
-      "platform": platform,
-      "alias": alias,
-      "tags": tags,
-    };
-    if (includingPassword) {
-      res['password'] = password;
-    }
-    return res;
-  }
-
-  Map<String, dynamic> toSharedAbCacheJson() {
-    return <String, dynamic>{
-      "id": id,
-      "username": username,
-      "hostname": hostname,
-      "platform": platform,
-      "alias": alias,
-      "tags": tags,
-    };
-  }
-
   Map<String, dynamic> toGroupCacheJson() {
     return <String, dynamic>{
       "id": id,
diff --git a/src/client.rs b/src/client.rs
index b32331072..0ff275bc4 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1129,6 +1129,53 @@ impl VideoHandler {
     }
 }
 
+// The source of sent password
+#[derive(Debug, Clone, PartialEq, Eq)]
+enum PasswordSource {
+    PersonalAb(Vec<u8>),
+    SharedAb(String),
+    Undefined,
+}
+
+impl Default for PasswordSource {
+    fn default() -> Self {
+        PasswordSource::Undefined
+    }
+}
+
+impl PasswordSource {
+    // Whether the password is personal ab password
+    pub fn is_personal_ab(&self, password: &[u8]) -> bool {
+        if password.is_empty() {
+            return false;
+        }
+        match self {
+            PasswordSource::PersonalAb(p) => p == password,
+            _ => false,
+        }
+    }
+
+    // Whether the password is shared ab password
+    pub fn is_shared_ab(&self, password: &[u8], hash: &Hash) -> bool {
+        if password.is_empty() {
+            return false;
+        }
+        match self {
+            PasswordSource::SharedAb(p) => Self::equal(p, password, hash),
+            _ => false,
+        }
+    }
+
+    //  Whether the password equals to the connected password
+    fn equal(password: &str, connected_password: &[u8], hash: &Hash) -> bool {
+        let mut hasher = Sha256::new();
+        hasher.update(password);
+        hasher.update(&hash.salt);
+        let res = hasher.finalize();
+        connected_password[..] == res[..]
+    }
+}
+
 /// Login config handler for [`Client`].
 #[derive(Default)]
 pub struct LoginConfigHandler {
@@ -1155,7 +1202,8 @@ pub struct LoginConfigHandler {
     pub mark_unsupported: Vec<CodecFormat>,
     pub selected_windows_session_id: Option<u32>,
     pub peer_info: Option<PeerInfo>,
-    shared_password: Option<String>, // used to distinguish whether it is connected with a shared password
+    password_source: PasswordSource, // where the sent password comes from
+    shared_password: Option<String>, // Store the shared password
 }
 
 impl Deref for LoginConfigHandler {
@@ -1829,20 +1877,25 @@ impl LoginConfigHandler {
             platform: pi.platform.clone(),
         };
         let mut config = self.load_config();
-        let connected_with_shared_password = self.is_connected_with_shared_password();
-        let old_config_password = config.password.clone();
         config.info = serde;
         let password = self.password.clone();
         let password0 = config.password.clone();
         let remember = self.remember;
+        let hash = self.hash.clone();
         if remember {
-            if !password.is_empty() && password != password0 {
-                config.password = password;
+            // remember is true: use PeerConfig password or ui login
+            // not sync shared password to recent
+            if !password.is_empty()
+                && password != password0
+                && !self.password_source.is_shared_ab(&password, &hash)
+            {
+                config.password = password.clone();
                 log::debug!("remember password of {}", self.id);
             }
         } else {
-            if self.save_ab_password_to_recent {
-                config.password = password;
+            if self.password_source.is_personal_ab(&password) {
+                // sync personal ab password to recent automatically
+                config.password = password.clone();
                 log::debug!("save ab password of {} to recent", self.id);
             } else if !password0.is_empty() {
                 config.password = Default::default();
@@ -1863,13 +1916,16 @@ impl LoginConfigHandler {
         }
         #[cfg(feature = "flutter")]
         {
-            if !connected_with_shared_password && remember && !config.password.is_empty() {
-                // sync ab password with PeerConfig password
-                let password = base64::encode(config.password.clone(), base64::Variant::Original);
+            // sync connected password to personal ab automatically if it is not shared password
+            if !config.password.is_empty()
+                && !self.password_source.is_shared_ab(&password, &hash)
+                && !self.password_source.is_personal_ab(&password)
+            {
+                let hash = base64::encode(config.password.clone(), base64::Variant::Original);
                 let evt: HashMap<&str, String> = HashMap::from([
-                    ("name", "sync_peer_password_to_ab".to_string()),
+                    ("name", "sync_peer_hash_password_to_personal_ab".to_string()),
                     ("id", self.id.clone()),
-                    ("password", password),
+                    ("hash", hash),
                 ]);
                 let evt = serde_json::ser::to_string(&evt).unwrap_or("".to_owned());
                 crate::flutter::push_global_event(crate::flutter::APP_TYPE_MAIN, evt);
@@ -1893,27 +1949,12 @@ impl LoginConfigHandler {
                 config.keyboard_mode = KeyboardMode::Legacy.to_string();
             }
         }
-        // keep hash password unchanged if connected with shared password
-        if connected_with_shared_password {
-            config.password = old_config_password;
-        }
         // no matter if change, for update file time
         self.save_config(config);
         self.supported_encoding = pi.encoding.clone().unwrap_or_default();
         log::info!("peer info supported_encoding:{:?}", self.supported_encoding);
     }
 
-    fn is_connected_with_shared_password(&self) -> bool {
-        if let Some(shared_password) = self.shared_password.as_ref() {
-            let mut hasher = Sha256::new();
-            hasher.update(shared_password);
-            hasher.update(&self.hash.salt);
-            let res = hasher.finalize();
-            return self.password.clone()[..] == res[..];
-        }
-        false
-    }
-
     pub fn get_remote_dir(&self) -> String {
         serde_json::from_str::<HashMap<String, String>>(&self.get_option("remote_dir"))
             .unwrap_or_default()
@@ -2565,7 +2606,6 @@ pub fn handle_login_error(
     err: &str,
     interface: &impl Interface,
 ) -> bool {
-    lc.write().unwrap().save_ab_password_to_recent = false;
     if err == LOGIN_MSG_PASSWORD_EMPTY {
         lc.write().unwrap().password = Default::default();
         interface.msgbox("input-password", "Password Required", "", "");
@@ -2617,14 +2657,20 @@ pub async fn handle_hash(
     peer: &mut Stream,
 ) {
     lc.write().unwrap().hash = hash.clone();
+    // Take care of password application order
+
+    // switch_uuid
     let uuid = lc.write().unwrap().switch_uuid.take();
     if let Some(uuid) = uuid {
         if let Ok(uuid) = uuid::Uuid::from_str(&uuid) {
             send_switch_login_request(lc.clone(), peer, uuid).await;
+            lc.write().unwrap().password_source = Default::default();
             return;
         }
     }
+    // last password
     let mut password = lc.read().unwrap().password.clone();
+    // preset password
     if password.is_empty() {
         if !password_preset.is_empty() {
             let mut hasher = Sha256::new();
@@ -2632,32 +2678,33 @@ pub async fn handle_hash(
             hasher.update(&hash.salt);
             let res = hasher.finalize();
             password = res[..].into();
+            lc.write().unwrap().password_source = Default::default();
         }
     }
+    // shared password
+    // Currently it's used only when click shared ab peer card
+    let shared_password = lc.write().unwrap().shared_password.take();
+    if let Some(shared_password) = shared_password {
+        if !shared_password.is_empty() {
+            let mut hasher = Sha256::new();
+            hasher.update(shared_password.clone());
+            hasher.update(&hash.salt);
+            let res = hasher.finalize();
+            password = res[..].into();
+            lc.write().unwrap().password_source = PasswordSource::SharedAb(shared_password);
+        }
+    }
+    // peer config password
     if password.is_empty() {
         password = lc.read().unwrap().config.password.clone();
-    }
-    if password.is_empty() {
-        let access_token = LocalConfig::get_option("access_token");
-        let ab = hbb_common::config::Ab::load();
-        if !access_token.is_empty() && access_token == ab.access_token {
-            let id = lc.read().unwrap().id.clone();
-            if let Some(ab) = ab.ab_entries.iter().find(|a| a.personal()) {
-                if let Some(p) = ab
-                    .peers
-                    .iter()
-                    .find_map(|p| if p.id == id { Some(p) } else { None })
-                {
-                    if let Ok(hash) = base64::decode(p.hash.clone(), base64::Variant::Original) {
-                        if !hash.is_empty() {
-                            password = hash;
-                            lc.write().unwrap().save_ab_password_to_recent = true;
-                        }
-                    }
-                }
-            }
+        if !password.is_empty() {
+            lc.write().unwrap().password_source = Default::default();
         }
     }
+    // personal ab password
+    if password.is_empty() {
+        try_get_password_from_personal_ab(lc.clone(), &mut password);
+    }
     lc.write().unwrap().password = password.clone();
     let password = if password.is_empty() {
         // login without password, the remote side can click accept
@@ -2677,6 +2724,31 @@ pub async fn handle_hash(
     lc.write().unwrap().hash = hash;
 }
 
+#[inline]
+fn try_get_password_from_personal_ab(lc: Arc<RwLock<LoginConfigHandler>>, password: &mut Vec<u8>) {
+    let access_token = LocalConfig::get_option("access_token");
+    let ab = hbb_common::config::Ab::load();
+    if !access_token.is_empty() && access_token == ab.access_token {
+        let id = lc.read().unwrap().id.clone();
+        if let Some(ab) = ab.ab_entries.iter().find(|a| a.personal()) {
+            if let Some(p) = ab
+                .peers
+                .iter()
+                .find_map(|p| if p.id == id { Some(p) } else { None })
+            {
+                if let Ok(hash_password) = base64::decode(p.hash.clone(), base64::Variant::Original)
+                {
+                    if !hash_password.is_empty() {
+                        *password = hash_password.clone();
+                        lc.write().unwrap().password_source =
+                            PasswordSource::PersonalAb(hash_password);
+                    }
+                }
+            }
+        }
+    }
+}
+
 /// Send login message to peer.
 ///
 /// # Arguments
@@ -2722,9 +2794,13 @@ pub async fn handle_login_from_ui(
         let mut password2 = lc.read().unwrap().password.clone();
         if password2.is_empty() {
             password2 = lc.read().unwrap().config.password.clone();
+            if !password2.is_empty() {
+                lc.write().unwrap().password_source = Default::default();
+            }
         }
         password2
     } else {
+        lc.write().unwrap().password_source = Default::default();
         let mut hasher = Sha256::new();
         hasher.update(password);
         hasher.update(&lc.read().unwrap().hash.salt);
diff --git a/src/flutter.rs b/src/flutter.rs
index af150c3f5..ca4057d36 100644
--- a/src/flutter.rs
+++ b/src/flutter.rs
@@ -1050,8 +1050,17 @@ pub fn session_add(
 
     LocalConfig::set_remote_id(&id);
 
+    let mut preset_password = password.clone();
+    let shared_password = if is_shared_password {
+        // To achieve a flexible password application order, we dont' treat shared password as a preset password.
+        preset_password = Default::default();
+        Some(password)
+    } else {
+        None
+    };
+
     let session: Session<FlutterHandler> = Session {
-        password: password.clone(),
+        password: preset_password,
         server_keyboard_enabled: Arc::new(RwLock::new(true)),
         server_file_transfer_enabled: Arc::new(RwLock::new(true)),
         server_clipboard_enabled: Arc::new(RwLock::new(true)),
@@ -1069,11 +1078,6 @@ pub fn session_add(
     #[cfg(not(feature = "gpucodec"))]
     let adapter_luid = None;
 
-    let shared_password = if is_shared_password {
-        Some(password)
-    } else {
-        None
-    };
     session.lc.write().unwrap().initialize(
         id.to_owned(),
         conn_type,