diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart
index 21cbe45b9..8d0de3b41 100644
--- a/flutter/lib/desktop/pages/remote_page.dart
+++ b/flutter/lib/desktop/pages/remote_page.dart
@@ -340,10 +340,8 @@ class ImagePaint extends StatelessWidget {
       return FlutterCustomMemoryImageCursor(
         pixbuf: cache.data,
         key: key,
-        // hotx: cache.hotx,
-        // hoty: cache.hoty,
-        hotx: 0,
-        hoty: 0,
+        hotx: cache.hotx,
+        hoty: cache.hoty,
         imageWidth: (cache.width * cache.scale).toInt(),
         imageHeight: (cache.height * cache.scale).toInt(),
       );
@@ -488,11 +486,19 @@ class CursorPaint extends StatelessWidget {
     final m = Provider.of<CursorModel>(context);
     final c = Provider.of<CanvasModel>(context);
     // final adjust = m.adjustForKeyboard();
+    double hotx = m.hotx;
+    double hoty = m.hoty;
+    if (m.image == null) {
+      if (m.defaultCache != null) {
+        hotx = m.defaultCache!.hotx;
+        hoty = m.defaultCache!.hoty;
+      }
+    }
     return CustomPaint(
       painter: ImagePainter(
-          image: m.image,
-          x: m.x - m.hotx + c.x / c.scale,
-          y: m.y - m.hoty + c.y / c.scale,
+          image: m.image ?? m.defaultImage,
+          x: m.x - hotx + c.x / c.scale,
+          y: m.y - hoty + c.y / c.scale,
           scale: c.scale),
     );
   }
diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart
index 0662fce2b..5dd01264b 100644
--- a/flutter/lib/mobile/pages/remote_page.dart
+++ b/flutter/lib/mobile/pages/remote_page.dart
@@ -862,11 +862,19 @@ class CursorPaint extends StatelessWidget {
     final c = Provider.of<CanvasModel>(context);
     final adjust = gFFI.cursorModel.adjustForKeyboard();
     var s = c.scale;
+    double hotx = m.hotx;
+    double hoty = m.hoty;
+    if (m.image == null) {
+      if (m.defaultCache != null) {
+        hotx = m.defaultCache!.hotx;
+        hoty = m.defaultCache!.hoty;
+      }
+    }
     return CustomPaint(
       painter: ImagePainter(
-          image: m.image,
-          x: m.x * s - m.hotx + c.x,
-          y: m.y * s - m.hoty + c.y - adjust,
+          image: m.image ?? m.defaultImage,
+          x: m.x * s - hotx + c.x,
+          y: m.y * s - hoty + c.y - adjust,
           scale: 1),
     );
   }
diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart
index 53a040eed..61185b016 100644
--- a/flutter/lib/models/model.dart
+++ b/flutter/lib/models/model.dart
@@ -721,8 +721,11 @@ class CursorData {
               height: (height * scale).toInt(),
             )
             .getBytes(format: img2.Format.bgra);
-        hotx = (width * scale) / 2;
-        hoty = (height * scale) / 2;
+        if (hotx > 0 && hoty > 0) {
+          // default cursor data
+          hotx = (width * scale) / 2;
+          hoty = (height * scale) / 2;
+        }
       }
     }
     this.scale = scale;
@@ -737,6 +740,7 @@ class CursorData {
 
 class CursorModel with ChangeNotifier {
   ui.Image? _image;
+  ui.Image? _defaultImage;
   final _images = <int, Tuple3<ui.Image, double, double>>{};
   CursorData? _cache;
   final _defaultCacheId = -1;
@@ -756,6 +760,7 @@ class CursorModel with ChangeNotifier {
   WeakReference<FFI> parent;
 
   ui.Image? get image => _image;
+  ui.Image? get defaultImage => _defaultImage;
   CursorData? get cache => _cache;
   CursorData? get defaultCache => _getDefaultCache();
 
@@ -771,30 +776,45 @@ class CursorModel with ChangeNotifier {
       DateTime.now().difference(_last_peer_mouse).inMilliseconds <
       kMouseControlTimeoutMSec;
 
-  CursorModel(this.parent);
+  CursorModel(this.parent) {
+    _getDefaultImage();
+    _getDefaultCache();
+  }
 
   Set<String> get cachedKeys => _cacheKeys;
   addKey(String key) => _cacheKeys.add(key);
 
+  Future<ui.Image?> _getDefaultImage() async {
+    if (_defaultImage == null) {
+      final defaultImg = defaultCursorImage!;
+      // This function is called only one time, no need to care about the performance.
+      Uint8List data = defaultImg.getBytes(format: img2.Format.rgba);
+      _defaultImage = await img.decodeImageFromPixels(
+          data, defaultImg.width, defaultImg.height, ui.PixelFormat.rgba8888);
+    }
+    return _defaultImage;
+  }
+
   CursorData? _getDefaultCache() {
     if (_defaultCache == null) {
+      Uint8List data;
       if (Platform.isWindows) {
-        Uint8List data = defaultCursorImage!.getBytes(format: img2.Format.bgra);
-        _hotx = defaultCursorImage!.width / 2;
-        _hoty = defaultCursorImage!.height / 2;
-
-        _defaultCache = CursorData(
-          peerId: id,
-          id: _defaultCacheId,
-          image: defaultCursorImage?.clone(),
-          scale: 1.0,
-          data: data,
-          hotx: _hotx,
-          hoty: _hoty,
-          width: defaultCursorImage!.width,
-          height: defaultCursorImage!.height,
-        );
+        data = defaultCursorImage!.getBytes(format: img2.Format.bgra);
+      } else {
+        data = Uint8List.fromList(img2.encodePng(defaultCursorImage!));
       }
+      double scale = 1.0;
+      _defaultCache = CursorData(
+        peerId: id,
+        id: _defaultCacheId,
+        image: defaultCursorImage?.clone(),
+        scale: scale,
+        data: data,
+        hotx: (defaultCursorImage!.width * scale) / 2,
+        hoty: (defaultCursorImage!.height * scale) / 2,
+        width: defaultCursorImage!.width,
+        height: defaultCursorImage!.height,
+      );
     }
     return _defaultCache;
   }
@@ -926,13 +946,15 @@ class CursorModel with ChangeNotifier {
     var height = int.parse(evt['height']);
     List<dynamic> colors = json.decode(evt['colors']);
     final rgba = Uint8List.fromList(colors.map((s) => s as int).toList());
-    var pid = parent.target?.id;
     final image = await img.decodeImageFromPixels(
         rgba, width, height, ui.PixelFormat.rgba8888);
-    if (parent.target?.id != pid) return;
     _image = image;
-    _images[id] = Tuple3(image, _hotx, _hoty);
-    await _updateCache(image, id, width, height);
+    if (await _updateCache(image, id, width, height)) {
+      _images[id] = Tuple3(image, _hotx, _hoty);
+    } else {
+      _hotx = 0;
+      _hoty = 0;
+    }
     try {
       // my throw exception, because the listener maybe already dispose
       notifyListeners();
@@ -941,44 +963,33 @@ class CursorModel with ChangeNotifier {
     }
   }
 
-  _updateCache(ui.Image image, int id, int w, int h) async {
-    Uint8List? data;
-    img2.Image? image2;
+  Future<bool> _updateCache(ui.Image image, int id, int w, int h) async {
+    ui.ImageByteFormat imgFormat = ui.ImageByteFormat.png;
     if (Platform.isWindows) {
-      ByteData? data2 =
-          await image.toByteData(format: ui.ImageByteFormat.rawRgba);
-      if (data2 != null) {
-        data = data2.buffer.asUint8List();
-        image2 = img2.Image.fromBytes(w, h, data);
-      } else {
-        data = defaultCursorImage?.getBytes(format: img2.Format.bgra);
-        image2 = defaultCursorImage?.clone();
-        _hotx = defaultCursorImage!.width / 2;
-        _hoty = defaultCursorImage!.height / 2;
-      }
-    } else {
-      ByteData? data2 = await image.toByteData(format: ui.ImageByteFormat.png);
-      if (data2 != null) {
-        data = data2.buffer.asUint8List();
-      } else {
-        data = Uint8List.fromList(img2.encodePng(defaultCursorImage!));
-        _hotx = defaultCursorImage!.width / 2;
-        _hoty = defaultCursorImage!.height / 2;
-      }
+      imgFormat = ui.ImageByteFormat.rawRgba;
     }
 
+    ByteData? imgBytes = await image.toByteData(format: imgFormat);
+    if (imgBytes == null) {
+      return false;
+    }
+
+    Uint8List? data = imgBytes.buffer.asUint8List();
     _cache = CursorData(
       peerId: this.id,
       id: id,
-      image: image2,
+      image: Platform.isWindows ? img2.Image.fromBytes(w, h, data) : null,
       scale: 1.0,
       data: data,
-      hotx: _hotx,
-      hoty: _hoty,
+      hotx: 0,
+      hoty: 0,
+      // hotx: _hotx,
+      // hoty: _hoty,
       width: w,
       height: h,
     );
     _cacheMap[id] = _cache!;
+    return true;
   }
 
   updateCursorId(Map<String, dynamic> evt) async {