Add more docs about scrolling (#9711)
This commit is contained in:
parent
ea96773fd1
commit
49d32d35ea
@ -62,7 +62,7 @@ bool debugPaintBaselinesEnabled = false;
|
|||||||
/// The color to use when painting alphabetic baselines.
|
/// The color to use when painting alphabetic baselines.
|
||||||
Color debugPaintAlphabeticBaselineColor = _kDebugPaintAlphabeticBaselineColor;
|
Color debugPaintAlphabeticBaselineColor = _kDebugPaintAlphabeticBaselineColor;
|
||||||
|
|
||||||
/// The color ot use when painting ideographic baselines.
|
/// The color to use when painting ideographic baselines.
|
||||||
Color debugPaintIdeographicBaselineColor = _kDebugPaintIdeographicBaselineColor;
|
Color debugPaintIdeographicBaselineColor = _kDebugPaintIdeographicBaselineColor;
|
||||||
|
|
||||||
/// Causes each Layer to paint a box around its bounds.
|
/// Causes each Layer to paint a box around its bounds.
|
||||||
|
@ -201,6 +201,15 @@ class ScrollController extends ChangeNotifier {
|
|||||||
return '$runtimeType#$hashCode(${description.join(", ")})';
|
return '$runtimeType#$hashCode(${description.join(", ")})';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add additional information to the given description for use by [toString].
|
||||||
|
///
|
||||||
|
/// This method makes it easier for subclasses to coordinate to provide a
|
||||||
|
/// high-quality [toString] implementation. The [toString] implementation on
|
||||||
|
/// the [ScrollController] base class calls [debugFillDescription] to collect
|
||||||
|
/// useful information from subclasses to incorporate into its return value.
|
||||||
|
///
|
||||||
|
/// If you override this, make sure to start your method with a call to
|
||||||
|
/// `super.debugFillDescription(description)`.
|
||||||
@mustCallSuper
|
@mustCallSuper
|
||||||
void debugFillDescription(List<String> description) {
|
void debugFillDescription(List<String> description) {
|
||||||
if (debugLabel != null)
|
if (debugLabel != null)
|
||||||
|
@ -7,7 +7,7 @@ import 'dart:math' as math;
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
|
||||||
/// A description of a [Scrollable]'s contents, useful for modelling the state
|
/// A description of a [Scrollable]'s contents, useful for modeling the state
|
||||||
/// of its viewport.
|
/// of its viewport.
|
||||||
///
|
///
|
||||||
/// This class defines a current position, [pixels], and a range of values
|
/// This class defines a current position, [pixels], and a range of values
|
||||||
|
@ -39,13 +39,41 @@ abstract class ViewportNotificationMixin extends Notification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [Notification] related to scrolling.
|
||||||
|
///
|
||||||
|
/// [Scrollable] widgets notify their ancestors about scrolling-related changes.
|
||||||
|
/// The notifications have the following lifecycle:
|
||||||
|
///
|
||||||
|
/// * A [ScrollStartNotification], which indicates that the widget has started
|
||||||
|
/// scrolling.
|
||||||
|
/// * Zero or more [ScrollUpdateNotification]s, which indicate that the widget
|
||||||
|
/// has changed its scroll position, mixed with zero or more
|
||||||
|
/// [OverscrollNotification]s, which indicate that the widget has not changed
|
||||||
|
/// its scroll position because the change would have caused its scroll
|
||||||
|
/// position to go outside its scroll bounds.
|
||||||
|
/// * Interspersed with the [ScrollUpdateNotification]s and
|
||||||
|
/// [OverscrollNotification]s are zero or more [UserScrollNotification]s,
|
||||||
|
/// which indicate that the user has changed the direciton in which they are
|
||||||
|
/// scrolling.
|
||||||
|
/// * A [ScrollEndNotification], which indicates that the widget has stopped
|
||||||
|
/// scrolling.
|
||||||
|
/// * A [UserScrollNotification], with a [UserScrollNotification.direciton] of
|
||||||
|
/// [ScrollDirection.idle].
|
||||||
|
///
|
||||||
|
/// Notifications bubble up through the tree, which means a given
|
||||||
|
/// [NotificationListener] will receive notifications for all descendant
|
||||||
|
/// [Scrollable] widgets. To focus on notifications from the nearest
|
||||||
|
/// [Scrollable] descendant, check that the [depth] property of the notification
|
||||||
|
/// is zero.
|
||||||
abstract class ScrollNotification extends LayoutChangedNotification with ViewportNotificationMixin {
|
abstract class ScrollNotification extends LayoutChangedNotification with ViewportNotificationMixin {
|
||||||
/// Creates a notification about scrolling.
|
/// Initializes fields for subclasses.
|
||||||
ScrollNotification({
|
ScrollNotification({
|
||||||
@required this.metrics,
|
@required this.metrics,
|
||||||
@required this.context,
|
@required this.context,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// A description of a [Scrollable]'s contents, useful for modeling the state
|
||||||
|
/// of its viewport.
|
||||||
final ScrollMetrics metrics;
|
final ScrollMetrics metrics;
|
||||||
|
|
||||||
/// The build context of the widget that fired this notification.
|
/// The build context of the widget that fired this notification.
|
||||||
@ -61,13 +89,24 @@ abstract class ScrollNotification extends LayoutChangedNotification with Viewpor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A notification that a [Scrollable] widget has started scrolling.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ScrollEndNotification], which indicates that scrolling has stopped.
|
||||||
|
/// * [ScrollNotification], which describes the notification lifecycle.
|
||||||
class ScrollStartNotification extends ScrollNotification {
|
class ScrollStartNotification extends ScrollNotification {
|
||||||
|
/// Creates a notification that a [Scrollable] widget has started scrolling.
|
||||||
ScrollStartNotification({
|
ScrollStartNotification({
|
||||||
@required ScrollMetrics metrics,
|
@required ScrollMetrics metrics,
|
||||||
@required BuildContext context,
|
@required BuildContext context,
|
||||||
this.dragDetails,
|
this.dragDetails,
|
||||||
}) : super(metrics: metrics, context: context);
|
}) : super(metrics: metrics, context: context);
|
||||||
|
|
||||||
|
/// If the [Scrollable] started scrolling because of a drag, the details about
|
||||||
|
/// that drag start.
|
||||||
|
///
|
||||||
|
/// Otherwise, null.
|
||||||
final DragStartDetails dragDetails;
|
final DragStartDetails dragDetails;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -78,7 +117,17 @@ class ScrollStartNotification extends ScrollNotification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A notification that a [Scrollable] widget has changed its scroll position.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [OverscrollNotification], which indicates that a [Scrollable] widget
|
||||||
|
/// has not changed its scroll position because the change would have caused
|
||||||
|
/// its scroll position to go outside its scroll bounds.
|
||||||
|
/// * [ScrollNotification], which describes the notification lifecycle.
|
||||||
class ScrollUpdateNotification extends ScrollNotification {
|
class ScrollUpdateNotification extends ScrollNotification {
|
||||||
|
/// Creates a notification that a [Scrollable] widget has changed its scroll
|
||||||
|
/// position.
|
||||||
ScrollUpdateNotification({
|
ScrollUpdateNotification({
|
||||||
@required ScrollMetrics metrics,
|
@required ScrollMetrics metrics,
|
||||||
@required BuildContext context,
|
@required BuildContext context,
|
||||||
@ -86,6 +135,10 @@ class ScrollUpdateNotification extends ScrollNotification {
|
|||||||
this.scrollDelta,
|
this.scrollDelta,
|
||||||
}) : super(metrics: metrics, context: context);
|
}) : super(metrics: metrics, context: context);
|
||||||
|
|
||||||
|
/// If the [Scrollable] changed its scroll position because of a drag, the
|
||||||
|
/// details about that drag update.
|
||||||
|
///
|
||||||
|
/// Otherwise, null.
|
||||||
final DragUpdateDetails dragDetails;
|
final DragUpdateDetails dragDetails;
|
||||||
|
|
||||||
/// The distance by which the [Scrollable] was scrolled, in logical pixels.
|
/// The distance by which the [Scrollable] was scrolled, in logical pixels.
|
||||||
@ -100,7 +153,18 @@ class ScrollUpdateNotification extends ScrollNotification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A notification that a [Scrollable] widget has not changed its scroll position
|
||||||
|
/// because the change would have caused its scroll position to go outside of
|
||||||
|
/// its scroll bounds.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ScrollUpdateNotification], which indicates that a [Scrollable] widget
|
||||||
|
/// has changed its scroll position.
|
||||||
|
/// * [ScrollNotification], which describes the notification lifecycle.
|
||||||
class OverscrollNotification extends ScrollNotification {
|
class OverscrollNotification extends ScrollNotification {
|
||||||
|
/// Creates a notification that a [Scrollable] widget has changed its scroll
|
||||||
|
/// position outside of its scroll bounds.
|
||||||
OverscrollNotification({
|
OverscrollNotification({
|
||||||
@required ScrollMetrics metrics,
|
@required ScrollMetrics metrics,
|
||||||
@required BuildContext context,
|
@required BuildContext context,
|
||||||
@ -114,6 +178,10 @@ class OverscrollNotification extends ScrollNotification {
|
|||||||
assert(velocity != null);
|
assert(velocity != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If the [Scrollable] overscrolled because of a drag, the details about that
|
||||||
|
/// drag update.
|
||||||
|
///
|
||||||
|
/// Otherwise, null.
|
||||||
final DragUpdateDetails dragDetails;
|
final DragUpdateDetails dragDetails;
|
||||||
|
|
||||||
/// The number of logical pixels that the [Scrollable] avoided scrolling.
|
/// The number of logical pixels that the [Scrollable] avoided scrolling.
|
||||||
@ -140,13 +208,31 @@ class OverscrollNotification extends ScrollNotification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A notification that a [Scrollable] widget has stopped scrolling.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ScrollStartNotification], which indicates that scrolling has started.
|
||||||
|
/// * [ScrollNotification], which describes the notification lifecycle.
|
||||||
class ScrollEndNotification extends ScrollNotification {
|
class ScrollEndNotification extends ScrollNotification {
|
||||||
|
/// Creates a notification that a [Scrollable] widget has stopped scrolling.
|
||||||
ScrollEndNotification({
|
ScrollEndNotification({
|
||||||
@required ScrollMetrics metrics,
|
@required ScrollMetrics metrics,
|
||||||
@required BuildContext context,
|
@required BuildContext context,
|
||||||
this.dragDetails,
|
this.dragDetails,
|
||||||
}) : super(metrics: metrics, context: context);
|
}) : super(metrics: metrics, context: context);
|
||||||
|
|
||||||
|
/// If the [Scrollable] stopped scrolling because of a drag, the details about
|
||||||
|
/// that drag end.
|
||||||
|
///
|
||||||
|
/// Otherwise, null.
|
||||||
|
///
|
||||||
|
/// If a drag ends with some residual velocity, a typical [ScrollPhysics] will
|
||||||
|
/// start a ballistic scroll, which delays the [ScrollEndNotification] until
|
||||||
|
/// the ballistic simulation completes, at which time [dragDetails] will
|
||||||
|
/// be null. If the residtual velocity is too small to trigger ballistic
|
||||||
|
/// scrolling, then the [ScrollEndNotification] will be dispatched immediately
|
||||||
|
/// and [dragDetails] will be non-null.
|
||||||
final DragEndDetails dragDetails;
|
final DragEndDetails dragDetails;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -157,13 +243,22 @@ class ScrollEndNotification extends ScrollNotification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A notification that the user has changed the direction in which they are
|
||||||
|
/// scrolling.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ScrollNotification], which describes the notification lifecycle.
|
||||||
class UserScrollNotification extends ScrollNotification {
|
class UserScrollNotification extends ScrollNotification {
|
||||||
|
/// Creates a notification that the user has changed the direction in which
|
||||||
|
/// they are scrolling.
|
||||||
UserScrollNotification({
|
UserScrollNotification({
|
||||||
@required ScrollMetrics metrics,
|
@required ScrollMetrics metrics,
|
||||||
@required BuildContext context,
|
@required BuildContext context,
|
||||||
this.direction,
|
this.direction,
|
||||||
}) : super(metrics: metrics, context: context);
|
}) : super(metrics: metrics, context: context);
|
||||||
|
|
||||||
|
/// The direction in which the user is scrolling.
|
||||||
final ScrollDirection direction;
|
final ScrollDirection direction;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -15,12 +15,34 @@ import 'scroll_simulation.dart';
|
|||||||
|
|
||||||
export 'package:flutter/physics.dart' show Tolerance;
|
export 'package:flutter/physics.dart' show Tolerance;
|
||||||
|
|
||||||
|
/// Determines the physics of a [Scrollable] widget.
|
||||||
|
///
|
||||||
|
/// For example, determines how the [Scrollable] will behave when the user
|
||||||
|
/// reaches the maximum scroll extent or when the user stops scrolling.
|
||||||
|
///
|
||||||
|
/// When starting a physics [Simulation], the current scroll position and
|
||||||
|
/// velocity are used as the initial conditions for the particle in the
|
||||||
|
/// simulation. The movement of the particle in the simulation is then used to
|
||||||
|
/// determine the scroll position for the widget.
|
||||||
@immutable
|
@immutable
|
||||||
class ScrollPhysics {
|
class ScrollPhysics {
|
||||||
|
/// Creates an object with the default scroll physics.
|
||||||
const ScrollPhysics({ this.parent });
|
const ScrollPhysics({ this.parent });
|
||||||
|
|
||||||
|
/// If non-null, determines the default behavior for each method.
|
||||||
|
///
|
||||||
|
/// If a subclass of [ScrollPhysics] does not override a method, that subclass
|
||||||
|
/// will inherit an implementation from this base class that defers to
|
||||||
|
/// [parent]. This mechanism lets you assemble novel combinations of
|
||||||
|
/// [ScrollPhysics] subclasses at runtime.
|
||||||
final ScrollPhysics parent;
|
final ScrollPhysics parent;
|
||||||
|
|
||||||
|
/// Return a [ScrollPhysics] with the same [runtimeType] where the [parent]
|
||||||
|
/// has been replaced with the given [parent].
|
||||||
|
///
|
||||||
|
/// The returned object will combine some of the behaviors from this
|
||||||
|
/// [ScrollPhysics] instance and some of the behaviors from the given
|
||||||
|
/// [ScrollPhysics] instance.
|
||||||
ScrollPhysics applyTo(ScrollPhysics parent) => new ScrollPhysics(parent: parent);
|
ScrollPhysics applyTo(ScrollPhysics parent) => new ScrollPhysics(parent: parent);
|
||||||
|
|
||||||
/// Used by [DragScrollActivity] and other user-driven activities to
|
/// Used by [DragScrollActivity] and other user-driven activities to
|
||||||
@ -111,6 +133,7 @@ class ScrollPhysics {
|
|||||||
ratio: 1.1,
|
ratio: 1.1,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// The spring to use for ballistic simulations.
|
||||||
SpringDescription get spring => parent?.spring ?? _kDefaultSpring;
|
SpringDescription get spring => parent?.spring ?? _kDefaultSpring;
|
||||||
|
|
||||||
/// The default accuracy to which scrolling is computed.
|
/// The default accuracy to which scrolling is computed.
|
||||||
@ -121,6 +144,7 @@ class ScrollPhysics {
|
|||||||
distance: 1.0 / ui.window.devicePixelRatio // logical pixels
|
distance: 1.0 / ui.window.devicePixelRatio // logical pixels
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// The tolerance to use for ballistic simulations.
|
||||||
Tolerance get tolerance => parent?.tolerance ?? _kDefaultTolerance;
|
Tolerance get tolerance => parent?.tolerance ?? _kDefaultTolerance;
|
||||||
|
|
||||||
/// The minimum distance an input pointer drag must have moved to
|
/// The minimum distance an input pointer drag must have moved to
|
||||||
|
@ -120,8 +120,16 @@ class ClampingScrollSimulation extends Simulation {
|
|||||||
_distance = (velocity * _duration / _kInitialVelocityPenetration).abs();
|
_distance = (velocity * _duration / _kInitialVelocityPenetration).abs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The position of the particle at the beginning of the simulation.
|
||||||
final double position;
|
final double position;
|
||||||
|
|
||||||
|
/// The velocity at which the particle is traveling at the beginning of the
|
||||||
|
/// simulation.
|
||||||
final double velocity;
|
final double velocity;
|
||||||
|
|
||||||
|
/// The amount of friction the particle experiences as it travels.
|
||||||
|
///
|
||||||
|
/// The more friction the particle experiences, the sooner it stops.
|
||||||
final double friction;
|
final double friction;
|
||||||
|
|
||||||
double _duration;
|
double _duration;
|
||||||
|
@ -24,9 +24,53 @@ import 'viewport.dart';
|
|||||||
|
|
||||||
export 'package:flutter/physics.dart' show Tolerance;
|
export 'package:flutter/physics.dart' show Tolerance;
|
||||||
|
|
||||||
|
/// Signature used by [Scrollable] to build the viewport through which the
|
||||||
|
/// scrollable content is displayed.
|
||||||
typedef Widget ViewportBuilder(BuildContext context, ViewportOffset position);
|
typedef Widget ViewportBuilder(BuildContext context, ViewportOffset position);
|
||||||
|
|
||||||
|
/// A widget that scrolls.
|
||||||
|
///
|
||||||
|
/// [Scrollable] implements the interaction model for a scrollable widget,
|
||||||
|
/// including gesture recognition, but does not have an opinion about how the
|
||||||
|
/// viewport, which actually displays the children, is constructed.
|
||||||
|
///
|
||||||
|
/// It's rare to construct a [Scrollable] directly. Instead, consider [ListView]
|
||||||
|
/// or [GridView], which combine scrolling, viewporting, and a layout model. To
|
||||||
|
/// combine layout models (or to use a custom layout mode), consider using
|
||||||
|
/// [CustomScrollView].
|
||||||
|
///
|
||||||
|
/// The static [Scrollable.of] and [Scrollable.ensureVisible] functions are
|
||||||
|
/// often used to interact with the [Scrollable] widget inside a [ListView] or
|
||||||
|
/// a [GridView].
|
||||||
|
///
|
||||||
|
/// To further customize scrolling behavior with a [Scrollable]:
|
||||||
|
///
|
||||||
|
/// 1. You can provide a [viewportBuilder] to customize the child model. For
|
||||||
|
/// example, [SingleChildScrollView] uses a viewport that displays a single
|
||||||
|
/// box child whereas [CustomScrollView] uses a [Viewport] or a
|
||||||
|
/// [ShrinkWrappingViewport], both of which display a list of slivers.
|
||||||
|
///
|
||||||
|
/// 2. You can provide a custom [ScrollController] that creates a custom
|
||||||
|
/// [ScrollPosition] subclass. For example, [PageView] uses a
|
||||||
|
/// [PageController], which creates a page-oriented scroll position subclass
|
||||||
|
/// that keeps the same page visible when the [Scrollable] resizes.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ListView], which is a commonly used [ScrollView] that displays a
|
||||||
|
/// scrolling, linear list of child widgets.
|
||||||
|
/// * [PageView], which is a scrolling list of child widgets that are each the
|
||||||
|
/// size of the viewport.
|
||||||
|
/// * [GridView], which is a [ScrollView] that displays a scrolling, 2D array
|
||||||
|
/// of child widgets.
|
||||||
|
/// * [CustomScrollView], which is a [ScrollView] that creates custom scroll
|
||||||
|
/// effects using slivers.
|
||||||
|
/// * [SingleChildScrollView], which is a scrollable widget that has a single
|
||||||
|
/// child.
|
||||||
class Scrollable extends StatefulWidget {
|
class Scrollable extends StatefulWidget {
|
||||||
|
/// Creates a widget that scrolls.
|
||||||
|
///
|
||||||
|
/// The [axisDirection] and [viewportBuilder] arguments must not be null.
|
||||||
const Scrollable({
|
const Scrollable({
|
||||||
Key key,
|
Key key,
|
||||||
this.axisDirection: AxisDirection.down,
|
this.axisDirection: AxisDirection.down,
|
||||||
@ -37,14 +81,50 @@ class Scrollable extends StatefulWidget {
|
|||||||
assert(viewportBuilder != null),
|
assert(viewportBuilder != null),
|
||||||
super (key: key);
|
super (key: key);
|
||||||
|
|
||||||
|
/// The direction in which this widget scrolls.
|
||||||
|
///
|
||||||
|
/// For example, if the [axisDirection] is [AxisDirection.down], increasing
|
||||||
|
/// the scroll position will cause content below the bottom of the viewport to
|
||||||
|
/// become visible through the viewport. Similarly, if [axisDirection] is
|
||||||
|
/// [AxisDirection.right], increasing the scroll position will cause content
|
||||||
|
/// beyond the right edge of the viewport to become visible through the
|
||||||
|
/// viewport.
|
||||||
|
///
|
||||||
|
/// Defaults to [AxisDirection.down].
|
||||||
final AxisDirection axisDirection;
|
final AxisDirection axisDirection;
|
||||||
|
|
||||||
|
/// An object that can be used to control the position to which this widget is
|
||||||
|
/// scrolled.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ensureVisible], which animates the scroll position to reveal a given
|
||||||
|
/// [BuildContext].
|
||||||
final ScrollController controller;
|
final ScrollController controller;
|
||||||
|
|
||||||
|
/// How the widgets should respond to user input.
|
||||||
|
///
|
||||||
|
/// For example, determines how the widget continues to animate after the
|
||||||
|
/// user stops dragging the scroll view.
|
||||||
|
///
|
||||||
|
/// Defaults to matching platform conventions.
|
||||||
final ScrollPhysics physics;
|
final ScrollPhysics physics;
|
||||||
|
|
||||||
|
/// Builds the viewport through which the scrollable content is displayed.
|
||||||
|
///
|
||||||
|
/// A typical viewport uses the given [ViewportOffset] to determine which part
|
||||||
|
/// of its content is actually visible through the viewport.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [Viewport], which is a viewport that displays a list of slivers.
|
||||||
|
/// * [ShrinkWrappingViewport], which is a viewport that displays a list of
|
||||||
|
/// slivers and sizes itself based on the size of the slivers.
|
||||||
final ViewportBuilder viewportBuilder;
|
final ViewportBuilder viewportBuilder;
|
||||||
|
|
||||||
|
/// The axis along which the scroll view scrolls.
|
||||||
|
///
|
||||||
|
/// Determined by the [axisDirection].
|
||||||
Axis get axis => axisDirectionToAxis(axisDirection);
|
Axis get axis => axisDirectionToAxis(axisDirection);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user