diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart
index 38402216d..310a3cadb 100644
--- a/flutter/lib/models/input_model.dart
+++ b/flutter/lib/models/input_model.dart
@@ -45,11 +45,12 @@ class InputModel {
   var command = false;
 
   // trackpad
-  final _trackpadSpeed = 0.01;
-  var trackpadScrollDistance = Offset.zero;
+  final _trackpadSpeed = 0.02;
+  var _trackpadLastDelta = Offset.zero;
   var _trackpadScrollUnsent = Offset.zero;
-  var _newScroll = true;
+  var _stopFling = true;
   Timer? _flingTimer;
+  final _flingBaseDelay = 10;
 
   // mouse
   final isPhysicalMouse = false.obs;
@@ -310,6 +311,7 @@ class InputModel {
   }
 
   void onPointHoverImage(PointerHoverEvent e) {
+    _stopFling = true;
     if (e.kind != ui.PointerDeviceKind.mouse) return;
     if (!isPhysicalMouse.value) {
       isPhysicalMouse.value = true;
@@ -327,29 +329,33 @@ class InputModel {
     }
   }
 
-  void onPointerPanZoomStart(PointerPanZoomStartEvent e) {}
+  void onPointerPanZoomStart(PointerPanZoomStartEvent e) {
+    _stopFling = true;
+  }
 
   // https://docs.flutter.dev/release/breaking-changes/trackpad-gestures
   // TODO(support zoom in/out)
   void onPointerPanZoomUpdate(PointerPanZoomUpdateEvent e) {
     var delta = e.panDelta;
-    trackpadScrollDistance += delta;
+    _trackpadLastDelta = delta;
     _trackpadScrollUnsent += (delta * _trackpadSpeed);
-    final x = _trackpadScrollUnsent.dx.truncate();
-    final y = _trackpadScrollUnsent.dy.truncate();
+    var x = _trackpadScrollUnsent.dx.truncate();
+    var y = _trackpadScrollUnsent.dy.truncate();
     _trackpadScrollUnsent -= Offset(_trackpadScrollUnsent.dx - x.toDouble(),
         _trackpadScrollUnsent.dy - y.toDouble());
 
-    var sendMsg = x != 0 || y != 0;
-    if (_newScroll) {
-      sendMsg = true;
-      _newScroll = false;
+    if (x == 0 && y == 0) {
+      x = delta.dx > 1 ? 1 : (delta.dx < -1 ? -1 : 0);
+      y = delta.dy > 1 ? 1 : (delta.dy < -1 ? -1 : 0);
+      if (x.abs() > y.abs()) {
+        y = 0;
+      } else {
+        x = 0;
+      }
     }
 
-    if (sendMsg) {
-      bind.sessionSendMouse(
-          id: id, msg: '{"type": "trackpad", "x": "$x", "y": "$y"}');
-    }
+    bind.sessionSendMouse(
+        id: id, msg: '{"type": "trackpad", "x": "$x", "y": "$y"}');
   }
 
   // Simple simulation for fling.
@@ -372,20 +378,67 @@ class InputModel {
     });
   }
 
-  void onPointerPanZoomEnd(PointerPanZoomEndEvent e) {
-    _newScroll = true;
-    _trackpadScrollUnsent = Offset.zero;
-    var x = _signOrZero(trackpadScrollDistance.dx);
-    var y = _signOrZero(trackpadScrollDistance.dy);
-    var dx = trackpadScrollDistance.dx.abs() ~/ 40;
-    var dy = trackpadScrollDistance.dy.abs() ~/ 40;
-    _scheduleFling(x, y, dx, dy);
+  void _scheduleFling2(double x, double y, int delay) {
+    if ((x ==0 && y == 0) || _stopFling) {
+      return;
+    }
 
-    trackpadScrollDistance = Offset.zero;
+    _flingTimer = Timer(Duration(milliseconds: delay), () {
+      if (_stopFling) {
+        return;
+      }
+
+      final d = 0.95;
+      x *= d;
+      y *= d;
+      final dx0 = x * _trackpadSpeed * 2;
+      final dy0 = y * _trackpadSpeed * 2;
+
+      // Try set delta (x,y) and delay. 
+      var dx = dx0.toInt();
+      var dy = dy0.toInt();
+      var delay = _flingBaseDelay;
+
+      // Try set min delta (x,y), and increase delay.
+      if (dx == 0 && dy == 0) {
+        final thr = 25;
+        var vx = thr;
+        var vy = thr;
+        if (dx0 != 0) {
+          vx = 1.0 ~/ dx0.abs();
+        }
+        if (dy0 != 0) {
+          vy = 1.0 ~/ dy0.abs();
+        }
+        if (vx < vy && vx < thr) {
+          delay *= vx;
+          dx = dx0 > 0 ? 1 : (dx0 < 0 ? -1 : 0);
+        } else if (vy < thr) {
+          delay *= vy;
+          dy = dy0 > 0 ? 1 : (dy0 < 0 ? -1 : 0);
+        }
+      }
+
+      if (dx == 0 && dy == 0) {
+        return;
+      }
+
+      bind.sessionSendMouse(
+          id: id, msg: '{"type": "trackpad", "x": "$dx", "y": "$dy"}');
+      _scheduleFling2(x, y, delay);
+    });
+  }
+
+  void onPointerPanZoomEnd(PointerPanZoomEndEvent e) {
+    _stopFling = false;
+    _trackpadScrollUnsent = Offset.zero;
+    _scheduleFling2(_trackpadLastDelta.dx, _trackpadLastDelta.dy, _flingBaseDelay);
+    _trackpadLastDelta = Offset.zero;
   }
 
   void onPointDownImage(PointerDownEvent e) {
     debugPrint("onPointDownImage");
+    _stopFling = true;
     if (e.kind != ui.PointerDeviceKind.mouse) {
       if (isPhysicalMouse.value) {
         isPhysicalMouse.value = false;