diff --git a/flutter/assets/checkbox.ttf b/flutter/assets/checkbox.ttf
new file mode 100644
index 000000000..70ddde698
Binary files /dev/null and b/flutter/assets/checkbox.ttf differ
diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart
index e518a56e0..799894f7e 100644
--- a/flutter/lib/common.dart
+++ b/flutter/lib/common.dart
@@ -90,6 +90,7 @@ class IconFont {
   static const IconData roundClose = IconData(0xe6ed, fontFamily: _family2);
   static const IconData addressBook =
       IconData(0xe602, fontFamily: "AddressBook");
+  static const IconData checkbox = IconData(0xe7d6, fontFamily: "CheckBox");
 }
 
 class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart
index a88bb4930..f12c15160 100644
--- a/flutter/lib/common/widgets/peer_card.dart
+++ b/flutter/lib/common/widgets/peer_card.dart
@@ -61,20 +61,19 @@ class _PeerCardState extends State<_PeerCard>
     final name =
         '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
     final PeerTabModel peerTabModel = Provider.of(context);
-    final selected = peerTabModel.isPeerSelected(peer.id);
     return Card(
         margin: EdgeInsets.symmetric(horizontal: 2),
         child: GestureDetector(
             onTap: () {
               if (peerTabModel.multiSelectionMode) {
-                peerTabModel.togglePeerSelect(peer);
+                peerTabModel.select(peer);
               } else {
                 if (!isWebDesktop) connect(context, peer.id);
               }
             },
             onDoubleTap: isWebDesktop ? () => connect(context, peer.id) : null,
             onLongPress: () {
-              peerTabModel.togglePeerSelect(peer);
+              peerTabModel.select(peer);
             },
             child: Container(
               padding: EdgeInsets.only(left: 12, top: 8, bottom: 8),
@@ -103,23 +102,7 @@ class _PeerCardState extends State<_PeerCard>
                       ],
                     ).paddingOnly(left: 8.0),
                   ),
-                  selected
-                      ? Padding(
-                          padding: const EdgeInsets.all(12),
-                          child: checkBox(),
-                        )
-                      : InkWell(
-                          child: const Padding(
-                              padding: EdgeInsets.all(12),
-                              child: Icon(Icons.more_vert)),
-                          onTapDown: (e) {
-                            final x = e.globalPosition.dx;
-                            final y = e.globalPosition.dy;
-                            _menuPos = RelativeRect.fromLTRB(x, y, x, y);
-                          },
-                          onTap: () {
-                            _showPeerMenu(peer.id);
-                          }),
+                  checkBoxOrActionMoreMobile(peer),
                 ],
               ),
             )));
@@ -159,15 +142,8 @@ class _PeerCardState extends State<_PeerCard>
           onDoubleTap: peerTabModel.multiSelectionMode
               ? null
               : () => widget.connect(context, peer.id),
-          onLongPress: () {
-            peerTabModel.togglePeerSelect(peer);
-          },
-          onSecondaryTapDown: (_) {
-            peerTabModel.togglePeerSelect(peer);
-          },
-          onTap: peerTabModel.multiSelectionMode
-              ? () => peerTabModel.togglePeerSelect(peer)
-              : null,
+          onTap: () => peerTabModel.select(peer),
+          onLongPress: () => peerTabModel.select(peer),
           child: Obx(() => peerCardUiType.value == PeerUiType.grid
               ? _buildPeerCard(context, peer, deco)
               : _buildPeerTile(context, peer, deco))),
@@ -176,8 +152,6 @@ class _PeerCardState extends State<_PeerCard>
 
   Widget _buildPeerTile(
       BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
-    final PeerTabModel peerTabModel = Provider.of(context);
-    final selected = peerTabModel.isPeerSelected(peer.id);
     final name =
         '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
     final greyStyle = TextStyle(
@@ -237,7 +211,7 @@ class _PeerCardState extends State<_PeerCard>
                         ],
                       ).marginOnly(top: 2),
                     ),
-                    selected ? checkBox() : _actionMore(peer),
+                    checkBoxOrActionMoreDesktop(peer),
                   ],
                 ).paddingOnly(left: 10.0, top: 3.0),
               ),
@@ -250,8 +224,6 @@ class _PeerCardState extends State<_PeerCard>
 
   Widget _buildPeerCard(
       BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
-    final PeerTabModel peerTabModel = Provider.of(context);
-    final selected = peerTabModel.isPeerSelected(peer.id);
     final name =
         '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
     return Card(
@@ -321,7 +293,7 @@ class _PeerCardState extends State<_PeerCard>
                           style: Theme.of(context).textTheme.titleSmall,
                         )),
                       ]).paddingSymmetric(vertical: 8)),
-                      selected ? checkBox() : _actionMore(peer),
+                      checkBoxOrActionMoreDesktop(peer),
                     ],
                   ).paddingSymmetric(horizontal: 12.0),
                 )
@@ -333,11 +305,57 @@ class _PeerCardState extends State<_PeerCard>
     );
   }
 
-  Widget checkBox() {
-    return Icon(
-      Icons.check_box,
-      color: MyTheme.accent,
-    );
+  Widget checkBoxOrActionMoreMobile(Peer peer) {
+    final PeerTabModel peerTabModel = Provider.of(context);
+    final selected = peerTabModel.isPeerSelected(peer.id);
+    if (peerTabModel.multiSelectionMode) {
+      return Padding(
+        padding: const EdgeInsets.all(12),
+        child: selected
+            ? Icon(
+                Icons.check_box,
+                color: MyTheme.accent,
+              )
+            : Icon(Icons.check_box_outline_blank),
+      );
+    } else {
+      return InkWell(
+          child: const Padding(
+              padding: EdgeInsets.all(12), child: Icon(Icons.more_vert)),
+          onTapDown: (e) {
+            final x = e.globalPosition.dx;
+            final y = e.globalPosition.dy;
+            _menuPos = RelativeRect.fromLTRB(x, y, x, y);
+          },
+          onTap: () {
+            _showPeerMenu(peer.id);
+          });
+    }
+  }
+
+  Widget checkBoxOrActionMoreDesktop(Peer peer) {
+    final PeerTabModel peerTabModel = Provider.of(context);
+    final selected = peerTabModel.isPeerSelected(peer.id);
+    if (peerTabModel.multiSelectionMode) {
+      final icon = selected
+          ? Icon(
+              Icons.check_box,
+              color: MyTheme.accent,
+            )
+          : Icon(Icons.check_box_outline_blank);
+      bool last = peerTabModel.isShiftDown && peer.id == peerTabModel.lastId;
+      if (last) {
+        return Container(
+          decoration: BoxDecoration(
+              border: Border.all(color: MyTheme.accent, width: 1)),
+          child: icon,
+        );
+      } else {
+        return icon;
+      }
+    } else {
+      return _actionMore(peer);
+    }
   }
 
   Widget _actionMore(Peer peer) => Listener(
diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart
index e8cbd2619..a034ec13d 100644
--- a/flutter/lib/common/widgets/peer_tab_page.dart
+++ b/flutter/lib/common/widgets/peer_tab_page.dart
@@ -103,6 +103,7 @@ class _PeerTabPageState extends State<PeerTabPage>
                 Expanded(child: _createSwitchBar(context)),
                 const PeerSearchBar().marginOnly(right: isMobile ? 0 : 13),
                 _createRefresh(),
+                _createMultiSelection(),
                 Offstage(
                     offstage: !isDesktop,
                     child: _createPeerViewTypeSwitch(context)),
@@ -258,6 +259,24 @@ class _PeerTabPageState extends State<PeerTabPage>
     );
   }
 
+  Widget _createMultiSelection() {
+    final textColor = Theme.of(context).textTheme.titleLarge?.color;
+    final model = Provider.of<PeerTabModel>(context);
+    return Container(
+      padding: EdgeInsets.all(4.0),
+      child: InkWell(
+        onTap: () {
+          model.setMultiSelectionMode(true);
+        },
+        child: Icon(
+          IconFont.checkbox,
+          size: 18,
+          color: textColor,
+        ),
+      ),
+    );
+  }
+
   Widget createMultiSelectionBar() {
     final model = Provider.of<PeerTabModel>(context);
     return Row(
@@ -308,7 +327,7 @@ class _PeerTabPageState extends State<PeerTabPage>
               default:
                 break;
             }
-            gFFI.peerTabModel.closeSelection();
+            gFFI.peerTabModel.setMultiSelectionMode(false);
             showToast(translate('Successful'));
           }
 
@@ -334,7 +353,7 @@ class _PeerTabPageState extends State<PeerTabPage>
             }
           }
           await bind.mainStoreFav(favs: favs);
-          gFFI.peerTabModel.closeSelection();
+          model.setMultiSelectionMode(false);
           showToast(translate('Successful'));
         },
         child: Tooltip(
@@ -355,7 +374,7 @@ class _PeerTabPageState extends State<PeerTabPage>
           final peers = model.selectedPeers;
           gFFI.abModel.addPeers(peers);
           gFFI.abModel.pushAb();
-          gFFI.peerTabModel.closeSelection();
+          model.setMultiSelectionMode(false);
           showToast(translate('Successful'));
         },
         child: Tooltip(
@@ -379,7 +398,7 @@ class _PeerTabPageState extends State<PeerTabPage>
                   gFFI.abModel.changeTagForPeers(
                       peers.map((p) => p.id).toList(), selectedTags);
                   gFFI.abModel.pushAb();
-                  gFFI.peerTabModel.closeSelection();
+                  model.setMultiSelectionMode(false);
                   showToast(translate('Successful'));
                 });
               },
@@ -416,7 +435,7 @@ class _PeerTabPageState extends State<PeerTabPage>
     final model = Provider.of<PeerTabModel>(context);
     return InkWell(
             onTap: () {
-              model.closeSelection();
+              model.setMultiSelectionMode(false);
             },
             child:
                 Tooltip(message: translate('Close'), child: Icon(Icons.clear)))
diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart
index 14db061bd..ef3862aac 100644
--- a/flutter/lib/main.dart
+++ b/flutter/lib/main.dart
@@ -418,7 +418,7 @@ class _AppState extends State<App> {
               : (context, child) {
                   child = _keepScaleBuilder(context, child);
                   child = botToastBuilder(context, child);
-                  if (desktopType == DesktopType.main) {
+                  if (isDesktop && desktopType == DesktopType.main) {
                     child = keyListenerBuilder(context, child);
                   }
                   return child;
@@ -465,9 +465,9 @@ Widget keyListenerBuilder(BuildContext context, Widget? child) {
     onKey: (RawKeyEvent event) {
       if (event.logicalKey == LogicalKeyboardKey.shiftLeft) {
         if (event is RawKeyDownEvent) {
-          gFFI.peerTabModel.isShiftDown = true;
+          gFFI.peerTabModel.setShiftDown(true);
         } else if (event is RawKeyUpEvent) {
-          gFFI.peerTabModel.isShiftDown = false;
+          gFFI.peerTabModel.setShiftDown(false);
         }
       }
     },
diff --git a/flutter/lib/models/peer_tab_model.dart b/flutter/lib/models/peer_tab_model.dart
index 024158ccc..e7f4192cd 100644
--- a/flutter/lib/models/peer_tab_model.dart
+++ b/flutter/lib/models/peer_tab_model.dart
@@ -39,11 +39,14 @@ class PeerTabModel with ChangeNotifier {
   List<int> get indexs => List.generate(tabNames.length, (index) => index);
   List<Peer> _selectedPeers = List.empty(growable: true);
   List<Peer> get selectedPeers => _selectedPeers;
-  bool get multiSelectionMode => _selectedPeers.isNotEmpty;
+  bool _multiSelectionMode = false;
+  bool get multiSelectionMode => _multiSelectionMode;
   List<Peer> _currentTabCachedPeers = List.empty(growable: true);
   List<Peer> get currentTabCachedPeers => _currentTabCachedPeers;
-  bool isShiftDown = false;
-  String? _shiftAnchorId;
+  bool _isShiftDown = false;
+  bool get isShiftDown => _isShiftDown;
+  String _lastId = '';
+  String get lastId => _lastId;
 
   PeerTabModel(this.parent) {
     // init currentTab
@@ -85,38 +88,39 @@ class PeerTabModel with ChangeNotifier {
     return Icons.help;
   }
 
-  togglePeerSelect(Peer peer) {
+  setMultiSelectionMode(bool mode) {
+    _multiSelectionMode = mode;
+    if (!mode) {
+      _selectedPeers.clear();
+      _lastId = '';
+    }
+    notifyListeners();
+  }
+
+  select(Peer peer) {
+    if (!_multiSelectionMode) {
+      // https://github.com/flutter/flutter/issues/101275#issuecomment-1604541700
+      // After onTap, the shift key should be pressed for a while when not in multiselection mode,
+      // because onTap is delayed when onDoubleTap is not null
+      if (isDesktop && !_isShiftDown) return;
+      _multiSelectionMode = true;
+    }
     final cached = _currentTabCachedPeers.map((e) => e.id).toList();
     int thisIndex = cached.indexOf(peer.id);
-    int closestIndex = -1;
-    String? closestId;
-    int smallestDiff = -1;
-    for (var i = 0; i < cached.length; i++) {
-      if (isPeerSelected(cached[i])) {
-        int diff = (i - thisIndex).abs();
-        if (smallestDiff == -1 || diff < smallestDiff) {
-          closestIndex = i;
-          closestId = cached[i];
-          smallestDiff = diff;
-        }
-      }
-    }
-    if (isShiftDown &&
-        thisIndex >= 0 &&
-        closestIndex >= 0 &&
-        closestId != null) {
-      int shiftAnchorIndex = cached.indexOf(_shiftAnchorId ?? '');
-      if (shiftAnchorIndex < 0) {
-        // use closest as shift anchor, rather than focused which we don't have
-        shiftAnchorIndex = closestIndex;
-        _shiftAnchorId = closestId;
-      }
-      int start = min(shiftAnchorIndex, thisIndex);
-      int end = max(shiftAnchorIndex, thisIndex);
-      _selectedPeers.clear();
+    int lastIndex = cached.indexOf(_lastId);
+    if (_isShiftDown && thisIndex >= 0 && lastIndex >= 0) {
+      int start = min(thisIndex, lastIndex);
+      int end = max(thisIndex, lastIndex);
+      bool remove = isPeerSelected(peer.id);
       for (var i = start; i <= end; i++) {
-        if (!isPeerSelected(cached[i])) {
-          _selectedPeers.add(_currentTabCachedPeers[i]);
+        if (remove) {
+          if (isPeerSelected(cached[i])) {
+            _selectedPeers.removeWhere((p) => p.id == cached[i]);
+          }
+        } else {
+          if (!isPeerSelected(cached[i])) {
+            _selectedPeers.add(_currentTabCachedPeers[i]);
+          }
         }
       }
     } else {
@@ -125,14 +129,8 @@ class PeerTabModel with ChangeNotifier {
       } else {
         _selectedPeers.add(peer);
       }
-      _shiftAnchorId = null;
     }
-    notifyListeners();
-  }
-
-  closeSelection() {
-    _selectedPeers.clear();
-    _shiftAnchorId = null;
+    _lastId = peer.id;
     notifyListeners();
   }
 
@@ -151,4 +149,13 @@ class PeerTabModel with ChangeNotifier {
   bool isPeerSelected(String id) {
     return selectedPeers.firstWhereOrNull((p) => p.id == id) != null;
   }
+
+  setShiftDown(bool v) {
+    if (_isShiftDown != v) {
+      _isShiftDown = v;
+      if (_multiSelectionMode) {
+        notifyListeners();
+      }
+    }
+  }
 }
diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml
index e7bc54b4b..9ae0016b6 100644
--- a/flutter/pubspec.yaml
+++ b/flutter/pubspec.yaml
@@ -149,6 +149,9 @@ flutter:
     - family: AddressBook
       fonts:
         - asset: assets/address_book.ttf
+    - family: CheckBox
+      fonts:
+        - asset: assets/checkbox.ttf
 
   # An image asset can refer to one or more resolution-specific "variants", see
   # https://flutter.dev/assets-and-images/#resolution-aware.
diff --git a/src/client.rs b/src/client.rs
index 9c039464c..e9d319685 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1151,6 +1151,10 @@ impl LoginConfigHandler {
     ///
     /// * `config` - [`PeerConfig`] to save.
     pub fn save_config(&mut self, config: PeerConfig) {
+        if self.version == 0 {
+            log::info!("skip saving peer config {}", self.id);
+            return;
+        }
         config.store(&self.id);
         self.config = config;
     }
@@ -1209,10 +1213,6 @@ impl LoginConfigHandler {
     /// * `k` - key of option
     /// * `v` - value of option
     pub fn save_ui_flutter(&mut self, k: String, v: String) {
-        if self.version == 0 && k == "wm_" {
-            log::info!("skip saving {k}");
-            return;
-        }
         let mut config = self.load_config();
         config.ui_flutter.insert(k, v);
         self.save_config(config);