blob: c36209c541bc8a11c3766320cc0bb0c2ced70123 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui';
import 'package:flutter/foundation.dart';
/// Enum representing the edge from which a swipe starts in a back gesture.
///
/// This is used in [PredictiveBackEvent] to indicate the starting edge of the
/// swipe gesture.
enum SwipeEdge {
/// Indicates that the swipe gesture starts from the left edge of the screen.
left,
/// Indicates that the swipe gesture starts from the right edge of the screen.
right,
}
/// Object used to report back gesture progress in Android.
///
/// Holds information about the touch event, swipe direction, and the animation
/// progress that predictive back animations should follow.
@immutable
final class PredictiveBackEvent {
/// Creates a new [PredictiveBackEvent] instance.
const PredictiveBackEvent._({
required this.touchOffset,
required this.progress,
required this.swipeEdge,
}) : assert(progress >= 0.0 && progress <= 1.0);
/// Creates an [PredictiveBackEvent] from a Map, typically used when converting
/// data received from a platform channel.
factory PredictiveBackEvent.fromMap(Map<String?, Object?> map) {
final List<Object?>? touchOffset = map['touchOffset'] as List<Object?>?;
return PredictiveBackEvent._(
touchOffset:
touchOffset == null
? null
: Offset((touchOffset[0]! as num).toDouble(), (touchOffset[1]! as num).toDouble()),
progress: (map['progress']! as num).toDouble(),
swipeEdge: SwipeEdge.values[map['swipeEdge']! as int],
);
}
/// The global position of the touch point as an `Offset`, or `null` if the
/// event is triggered by a button press.
///
/// This represents the touch location that initiates or interacts with the
/// back gesture. When `null`, it indicates the gesture was not started by a
/// touch event, such as a back button press in devices with hardware buttons.
final Offset? touchOffset;
/// Returns a value between 0.0 and 1.0 representing how far along the back
/// gesture is.
///
/// This value is driven by the horizontal location of the touch point, and
/// should be used as the fraction to seek the predictive back animation with.
/// Specifically,
///
/// - The progress is 0.0 when the touch is at the starting edge of the screen
/// (left or right), and the animation should seek to its start state.
/// - The progress is approximately 1.0 when the touch is at the opposite side
/// of the screen, and the animation should seek to its end state. Exact end
/// value may vary depending on screen size.
///
/// When the gesture is canceled, the progress value continues to update,
/// animating back to 0.0 until the cancellation animation completes.
///
/// In-between locations are linearly interpolated based on horizontal
/// distance from the starting edge and smooth clamped to 1.0 when the
/// distance exceeds a system-wide threshold.
final double progress;
/// The screen edge from which the swipe gesture starts.
final SwipeEdge swipeEdge;
/// Indicates if the event was triggered by a system back button press.
///
/// Returns false for a predictive back gesture.
bool get isButtonEvent =>
// The Android documentation for BackEvent
// (https://developer.android.com/reference/android/window/BackEvent#getTouchX())
// says that getTouchX and getTouchY should return NaN when the system
// back button is pressed, but in practice it seems to return 0.0, hence
// the check for Offset.zero here. This was tested directly in the engine
// on Android emulator running API 34.
touchOffset == null || (progress == 0.0 && touchOffset == Offset.zero);
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
if (other.runtimeType != runtimeType) {
return false;
}
return other is PredictiveBackEvent &&
touchOffset == other.touchOffset &&
progress == other.progress &&
swipeEdge == other.swipeEdge;
}
@override
int get hashCode => Object.hash(touchOffset, progress, swipeEdge);
@override
String toString() {
return 'PredictiveBackEvent{touchOffset: $touchOffset, progress: $progress, swipeEdge: $swipeEdge}';
}
}