Skip to main content

Several time bases

They can be converted back and forth via TimeConverter class.

DateTime.now(), SimpleDateTime.now()​

The one everyone is familiar with.

The SimpleDateTime has same semantics as DateTime but probably more lightweight.

SystemFrameTimeStamp, fml::TimePoint​

Who uses it

  • SchedulerBinding.currentSystemFrameTimeStamp
  • The "rawTimeStamp" argument in handleBeginFrame(Duration? rawTimeStamp)
  • The fml::TimePoint use extensively in the C++ engine.

Notice the engine wants to accept fml::TimePoint most of the time. Therefore, for example, when we want to submit a vsync target time to engine, we should (usually) convert it to fml::TimePoint.

By the way, the dig if you are interested are as follows. Or, look at dig here: https://github.com/flutter/flutter/issues/112610#issuecomment-1264096036

Dig in deteails (if you are interested)
// animator.cc
const fml::TimePoint frame_target_time =
frame_timings_recorder_->GetVsyncTargetTime();
delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number);

// ... which finally calls platfrom_configuration.cc
void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime,
uint64_t frame_number) {
tonic::CheckAndHandleError(
tonic::DartInvoke(begin_frame_.Get(), {
Dart_NewInteger(microseconds),
Dart_NewInteger(frame_number),
}));
}

AdjustedFrameTimeStamp, Ticker​

Who uses it

  • SchedulerBinding.currentFrameTimeStamp
  • The time stamp passed from Ticker's onTick callback

As can be seen in SchedulerBinding source code, this one is effectively converted from SchedulerBinding.currentSystemFrameTimeStamp by _adjustForEpoch method.

PointerEventTimeStamp, PointerEvent.timeStamp​

Who uses it:

  • PointerEvent.timeStamp

Digging into code, we see it comes from a time stamp provided together with pointer events from the Android and iOS operating system.

Dig in deteails (if you are interested)

Android​

static void DispatchPointerDataPacket(JNIEnv* env,
jobject jcaller,
jlong shell_holder,
jobject buffer,
jint position) {
uint8_t* data = static_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
auto packet = std::make_unique<flutter::PointerDataPacket>(data, position);
ANDROID_SHELL_HOLDER->GetPlatformView()->DispatchPointerDataPacket(
std::move(packet));
}

...

// Start of methods from FlutterView
{
.name = "nativeDispatchPointerDataPacket",
.signature = "(JLjava/nio/ByteBuffer;I)V",
.fnPtr = reinterpret_cast<void*>(&DispatchPointerDataPacket),
},

FlutterJNI.java

  // ------ Start Touch Interaction Support ---
/** Sends a packet of pointer data to Flutter's engine. */
@UiThread
public void dispatchPointerDataPacket(@NonNull ByteBuffer buffer, int position) {
ensureRunningOnMainThread();
ensureAttachedToNative();
nativeDispatchPointerDataPacket(nativeShellHolderId, buffer, position);
}

AndroidTouchProcessor.java

  public boolean onTouchEvent(@NonNull MotionEvent event, @NonNull Matrix transformMatrix) {
addPointerForIndex;
renderer.dispatchPointerDataPacket(packet, packet.position());

private void addPointerForIndex(MotionEvent event
long timeStamp = event.getEventTime() * 1000; // Convert from milliseconds to microseconds.

https://developer.android.com/reference/android/view/MotionEvent#getEventTime()

Retrieve the time this event occurred, in the SystemClock.uptimeMillis() time base.

Thus it is SystemClock.uptimeMillis.

iOS​

FlutterEngine.mm

- (void)dispatchPointerDataPacket:(std::unique_ptr<flutter::PointerDataPacket>)packet {
if (!self.platformView) {
return;
}
self.platformView->DispatchPointerDataPacket(std::move(packet));
}

Then

// Dispatches the UITouches to the engine. Usually, the type of change of the touch is determined
// from the UITouch's phase. However, FlutterAppDelegate fakes touches to ensure that touch events
// in the status bar area are available to framework code. The change type (optional) of the faked
// touch is specified in the second argument.
- (void)dispatchTouches:(NSSet*)touches
pointerDataChangeOverride:(flutter::PointerData::Change*)overridden_change
event:(UIEvent*)event {
[_engine.get() dispatchPointerDataPacket:std::move(packet)];

About timestamp generation:

- (void)dispatchTouches:(NSSet*)touches
pointer_data.time_stamp = touch.timestamp * kMicrosecondsPerSecond;

- (flutter::PointerData)generatePointerDataForFake {
// `UITouch.timestamp` is defined as seconds since system startup. Synthesized events can get this
// time with `NSProcessInfo.systemUptime`. See
// https://developer.apple.com/documentation/uikit/uitouch/1618144-timestamp?language=objc
pointer_data.time_stamp = [[NSProcessInfo processInfo] systemUptime] * kMicrosecondsPerSecond;

Thus, it isUITouch.timestamp, indeed also system uptime.

https://developer.apple.com/documentation/uikit/uitouch/1618144-timestamp?language=objc

The value of this property is the time, in seconds since system startup, that the touch originated or was last changed. For a definition of the time since system startup, see the description of the systemUptime method of the NSProcessInfo class.