blob: 0665bc0153a575e5b0d249c9decee5b770616db5 [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 'package:flutter/foundation.dart';
import 'framework.dart';
/// Signature for [Notification] listeners.
///
/// Return true to cancel the notification bubbling. Return false to allow the
/// notification to continue to be dispatched to further ancestors.
///
/// [NotificationListener] is useful when listening scroll events
/// in [ListView],[NestedScrollView],[GridView] or any Scrolling widgets.
/// Used by [NotificationListener.onNotification].
typedef NotificationListenerCallback<T extends Notification> = bool Function(T notification);
/// A notification that can bubble up the widget tree.
///
/// You can determine the type of a notification using the `is` operator to
/// check the [runtimeType] of the notification.
///
/// To listen for notifications in a subtree, use a [NotificationListener].
///
/// To send a notification, call [dispatch] on the notification you wish to
/// send. The notification will be delivered to any [NotificationListener]
/// widgets with the appropriate type parameters that are ancestors of the given
/// [BuildContext].
///
/// {@tool dartpad}
/// This example shows a [NotificationListener] widget
/// that listens for [ScrollNotification] notifications. When a scroll
/// event occurs in the [NestedScrollView],
/// this widget is notified. The events could be either a
/// [ScrollStartNotification]or[ScrollEndNotification].
///
/// ** See code in examples/api/lib/widgets/notification_listener/notification.0.dart **
/// {@end-tool}
///
/// See also:
///
/// * [ScrollNotification] which describes the notification lifecycle.
/// * [ScrollStartNotification] which returns the start position of scrolling.
/// * [ScrollEndNotification] which returns the end position of scrolling.
/// * [NestedScrollView] which creates a nested scroll view.
///
abstract class Notification {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const Notification();
/// Start bubbling this notification at the given build context.
///
/// The notification will be delivered to any [NotificationListener] widgets
/// with the appropriate type parameters that are ancestors of the given
/// [BuildContext]. If the [BuildContext] is null, the notification is not
/// dispatched.
void dispatch(BuildContext? target) {
target?.dispatchNotification(this);
}
@override
String toString() {
final List<String> description = <String>[];
debugFillDescription(description);
return '${objectRuntimeType(this, 'Notification')}(${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 [Notification] base class calls [debugFillDescription] to collect
/// useful information from subclasses to incorporate into its return value.
///
/// Implementations of this method should start with a call to the inherited
/// method, as in `super.debugFillDescription(description)`.
@protected
@mustCallSuper
void debugFillDescription(List<String> description) { }
}
/// A widget that listens for [Notification]s bubbling up the tree.
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=cAnFbFoGM50}
///
/// Notifications will trigger the [onNotification] callback only if their
/// [runtimeType] is a subtype of `T`.
///
/// To dispatch notifications, use the [Notification.dispatch] method.
class NotificationListener<T extends Notification> extends ProxyWidget {
/// Creates a widget that listens for notifications.
const NotificationListener({
super.key,
required super.child,
this.onNotification,
});
/// Called when a notification of the appropriate type arrives at this
/// location in the tree.
///
/// Return true to cancel the notification bubbling. Return false to
/// allow the notification to continue to be dispatched to further ancestors.
///
/// Notifications vary in terms of when they are dispatched. There are two
/// main possibilities: dispatch between frames, and dispatch during layout.
///
/// For notifications that dispatch during layout, such as those that inherit
/// from [LayoutChangedNotification], it is too late to call [State.setState]
/// in response to the notification (as layout is currently happening in a
/// descendant, by definition, since notifications bubble up the tree). For
/// widgets that depend on layout, consider a [LayoutBuilder] instead.
final NotificationListenerCallback<T>? onNotification;
@override
Element createElement() {
return _NotificationElement<T>(this);
}
}
/// An element used to host [NotificationListener] elements.
class _NotificationElement<T extends Notification> extends ProxyElement with NotifiableElementMixin {
_NotificationElement(NotificationListener<T> super.widget);
@override
bool onNotification(Notification notification) {
final NotificationListener<T> listener = widget as NotificationListener<T>;
if (listener.onNotification != null && notification is T) {
return listener.onNotification!(notification);
}
return false;
}
@override
void notifyClients(covariant ProxyWidget oldWidget) {
// Notification tree does not need to notify clients.
}
}
/// Indicates that the layout of one of the descendants of the object receiving
/// this notification has changed in some way, and that therefore any
/// assumptions about that layout are no longer valid.
///
/// Useful if, for instance, you're trying to align multiple descendants.
///
/// To listen for notifications in a subtree, use a
/// [NotificationListener<LayoutChangedNotification>].
///
/// To send a notification, call [dispatch] on the notification you wish to
/// send. The notification will be delivered to any [NotificationListener]
/// widgets with the appropriate type parameters that are ancestors of the given
/// [BuildContext].
///
/// In the widgets library, only the [SizeChangedLayoutNotifier] class and
/// [Scrollable] classes dispatch this notification (specifically, they dispatch
/// [SizeChangedLayoutNotification]s and [ScrollNotification]s respectively).
/// Transitions, in particular, do not. Changing one's layout in one's build
/// function does not cause this notification to be dispatched automatically. If
/// an ancestor expects to be notified for any layout change, make sure you
/// either only use widgets that never change layout, or that notify their
/// ancestors when appropriate, or alternatively, dispatch the notifications
/// yourself when appropriate.
///
/// Also, since this notification is sent when the layout is changed, it is only
/// useful for paint effects that depend on the layout. If you were to use this
/// notification to change the build, for instance, you would always be one
/// frame behind, which would look really ugly and laggy.
class LayoutChangedNotification extends Notification {
/// Create a new [LayoutChangedNotification].
const LayoutChangedNotification();
}