Skip to content

[iOS] significant input lag on iOS devices even with 120Hz (Pro Motion) #170735

@zeyus

Description

@zeyus

Steps to reproduce

This is very likely related to (but not identical to): #110431 this issue is not limited to scroll behavior and shows a lag of >1 frame.

  1. Create a simple flutter app that has a button and shows something on the screen when the button is pressed
  2. Run the app (in debug or release mode, it doesn't matter)
  3. Use some precision measurement tools like an FSR sensor and a photodiode (some results below), or, record a video (attached below) at an extremely high frame rate to get a clear indication of touch input -> screen render latency

Why this matters

  1. Game performance. Input lag / touch / display latency is an important contributing factor to the experience of playing a game.
  2. Research (my case). Research, especially neuroscience, but also behavioural and others, requires precise understanding of timing, and low latency along the entire pipeline is critical to even begin to report results of reaction times, or associate it with brain events (usually sampled at 1000Hz)

My use case

I'm aware this is probably a niche case, but I think it could have a big impact.

I'm creating a research tool for group dynamics research. This is a growing field with advancements like EEG and MEG hyperscanning. Often experiments have to be coded each time for different labs and devices.

Other options like psychopy don't work on mobile devices, and are definitely not geared towards group research, although psychopy does allow for precise timing control on a single device.

I've spent a lot of time getting this off the ground as part of my PhD project and I've built a Dart native library wrapper for handling network coordination and communication, but this issue has me panicking a little bit because I'm not sure that as it stands, flutter can fit the bill.

Summary

I'm going to be doing experiments using iPads. If I can use flutter, I will be publishing it as a platform for other researchers, and will also be at the core of several of my publications.

Flutter has the potential to be a useful, and maybe even the go-to tool for a bunch of types of research, but not if low latency (at least close as possible to the native device level) can be achieved.

Expected results

Same latency as a swiftUI app, or interacting with native UI elements on the device, also should be similar to android and linux, (obviously with device differences).

Actual results

Several frames of latency are introduced, regularly 80+ milliseconds on a real device, similar results are observed using the simulator.

Code sample

Code sample

For a more complete example (attempting different input detection methods, and frame scheduling, see: https://github.com/NexusDynamic/button_display_latency )

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const TestScreen(),
    );
  }
}

class TestScreen extends StatefulWidget {
  const TestScreen({super.key});

  @override
  State<TestScreen> createState() => _TestScreenState();
}

class _TestScreenState extends State<TestScreen> {
  bool _showFlash = false;

  void _onButtonPressed() {
    setState(() {
      _showFlash = true;
    });
    
    Future.delayed(const Duration(milliseconds: 20), () {
      if (mounted) {
        setState(() {
          _showFlash = false;
        });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Listener(
              onPointerDown: (_) => _onButtonPressed(),
              child: Container(
                width: 150,
                height: 150,
                color: Colors.blue,
                alignment: Alignment.center,
                child: const Text(
                  'Press Me',
                  style: TextStyle(color: Colors.white),
                ),
              ),
            ),
            const SizedBox(height: 50),
            Container(
              width: 100,
              height: 100,
              color: _showFlash ? Colors.red : Colors.transparent,
            ),
          ],
        ),
      ),
    );
  }
}

Compare this to the swiftUI version:

//  ContentView.swift
//  uitest


import SwiftUI

extension View {
    func onTouchDown(perform action: @escaping () -> Void) -> some View {
        self.simultaneousGesture(
            DragGesture(minimumDistance: 0)
                .onChanged { _ in
                    action()
                }
        )
    }
}

struct ContentView: View {
    @State private var showRectangle = false
    @State private var useTouchDown = true
    @Environment(\.colorScheme) var colorScheme
    
    private func triggerRectangle() {
        showRectangle = true
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.02) {
            showRectangle = false
        }
    }
    
    var body: some View {
        VStack {
            Toggle("Use TouchDown", isOn: $useTouchDown)
                .padding()
            
            Button(action: useTouchDown ? {} : triggerRectangle) {
                Text("Tap me")
                    .padding()
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .cornerRadius(8)
            }
            .onTouchDown {
                if useTouchDown {
                    triggerRectangle()
                }
            }
            
            Rectangle()
                .fill(showRectangle ? (colorScheme == .dark ? Color.white : Color.black) : Color.clear)
                .frame(width: 200, height: 200)
                .padding()
        }
        .padding()
    }
}

#Preview {
    ContentView()
}

Screenshots or Video

Screenshots / Video demonstration

Flutter (exact MWE as above) on an iPad M3 Pro:

PXL_20250617_111446337_flutter.mp4

Swift (exact swift MWE as above) on iPad M3 Pro:

PXL_20250617_095533582_ipad_swift.mp4

Comprehensive example (linked above), on a Pixel 7a (Android 16 BP31.250523.006):

IMG_0002_crop_android.mov

Comprehensive example (Arch linux on a Razer Blade Stealth (2017) with touchscreen):

IMG_0003_crop_linux.mov

Comprehensive example (iOS simulator):

flutter_demo_latency.mp4

Some results (which have, a small native-lib + network stack overhead ~<1ms). Theses were recorded using a FSR sensor placed on top of the on-screen button (t=0) and sending a network packet (reference point) and then showing a square on screen captured by a photodiode). The implementation for the test is here: https://github.com/NexusDynamic/liblsl.dart/tree/main/packages/liblsl_test
These are the results for two iPads (one sender with FSR sensor, both consumers with photodiode):

Image

Logs

Logs
# N/A

Flutter Doctor output

Doctor output
 flutter doctor -v
[✓] Flutter (Channel main, 3.33.0-1.0.pre.516, on macOS 15.5 24F74 darwin-arm64, locale en-US) [6.9s]
    • Flutter version 3.33.0-1.0.pre.516 on channel main at /Users/au662726/development/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision e2e543dbd7 (2 days ago), 2025-06-15 02:17:28 -0400
    • Engine revision e2e543dbd7
    • Dart version 3.9.0 (build 3.9.0-235.0.dev)
    • DevTools version 2.48.0-dev.0

[✓] Android toolchain - develop for Android devices (Android SDK version 36.0.0) [5.4s]
    • Android SDK at /Users/au662726/Library/Android/sdk
    • Emulator version 35.4.9.0 (build_id 13025442) (CL:N/A)
    • Platform android-36, build-tools 36.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
      This is the JDK bundled with the latest Android Studio installation on this machine.
      To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build 21.0.5+-13047016-b750.29)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 16.4) [7.6s]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 16F6
    • CocoaPods version 1.16.2

[✗] Chrome - develop for the web (Cannot find Chrome executable at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome) [15ms]
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.

[✓] Android Studio (version 2024.3) [15ms]
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 21.0.5+-13047016-b750.29)

[✓] VS Code (version 1.102.0-insider) [12ms]
    • VS Code at /Applications/Visual Studio Code - Insiders.app/Contents
    • Flutter extension version 3.113.20250603

[✓] Connected device (2 available) [12.4s]
    • xxx iPad (wireless) (mobile) • xxx-xxxx • ios          • iOS 18.1 22B83
    • macOS (desktop)                • macos                     • darwin-arm64 • macOS 15.5 24F74 darwin-arm64
    ! Error: Browsing on the local area network for xxx iPad (2). Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)

[✓] Network resources [697ms]
    • All expected network resources are available.

! Doctor found issues in 1 category.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listc: performanceRelates to speed or footprint issues (see "perf:" labels)e: device-specificOnly manifests on certain devicesengineflutter/engine repository. See also e: labels.platform-iosiOS applications specificallyteam-iosOwned by iOS platform teamtriaged-iosTriaged by iOS platform team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy