blob: 5fdfbba89b9c32884a5a4528be76306ab93136ca [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/rendering.dart';
import 'framework.dart';
import 'notification_listener.dart';
/// Indicates that the size of one of the descendants of the object receiving
/// this notification has changed, and that therefore any assumptions about that
/// layout are no longer valid.
///
/// For example, sent by the [SizeChangedLayoutNotifier] widget whenever that
/// widget changes size.
///
/// This notification can be used for triggering repaints, but if you use this
/// notification to trigger rebuilds or relayouts, you'll create a backwards
/// dependency in the frame pipeline because [SizeChangedLayoutNotification]s
/// are generated during layout, which is after the build phase and in the
/// middle of the layout phase. This backwards dependency can lead to visual
/// corruption or lags.
///
/// See [LayoutChangedNotification] for additional discussion of layout
/// notifications such as this one.
///
/// See also:
///
/// * [SizeChangedLayoutNotifier], which sends this notification.
/// * [LayoutChangedNotification], of which this is a subclass.
class SizeChangedLayoutNotification extends LayoutChangedNotification {
/// Create a new [SizeChangedLayoutNotification].
const SizeChangedLayoutNotification();
}
/// A widget that automatically dispatches a [SizeChangedLayoutNotification]
/// when the layout dimensions of its child change.
///
/// The notification is not sent for the initial layout (since the size doesn't
/// change in that case, it's just established).
///
/// To listen for the notification dispatched by this widget, use a
/// [NotificationListener<SizeChangedLayoutNotification>].
///
/// The [Material] class listens for [LayoutChangedNotification]s, including
/// [SizeChangedLayoutNotification]s, to repaint [InkResponse] and [InkWell] ink
/// effects. When a widget is likely to change size, wrapping it in a
/// [SizeChangedLayoutNotifier] will cause the ink effects to correctly repaint
/// when the child changes size.
///
/// See also:
///
/// * [Notification], the base class for notifications that bubble through the
/// widget tree.
class SizeChangedLayoutNotifier extends SingleChildRenderObjectWidget {
/// Creates a [SizeChangedLayoutNotifier] that dispatches layout changed
/// notifications when [child] changes layout size.
const SizeChangedLayoutNotifier({
super.key,
super.child,
});
@override
RenderObject createRenderObject(BuildContext context) {
return _RenderSizeChangedWithCallback(
onLayoutChangedCallback: () {
const SizeChangedLayoutNotification().dispatch(context);
},
);
}
}
class _RenderSizeChangedWithCallback extends RenderProxyBox {
_RenderSizeChangedWithCallback({
RenderBox? child,
required this.onLayoutChangedCallback,
}) : assert(onLayoutChangedCallback != null),
super(child);
// There's a 1:1 relationship between the _RenderSizeChangedWithCallback and
// the `context` that is captured by the closure created by createRenderObject
// above to assign to onLayoutChangedCallback, and thus we know that the
// onLayoutChangedCallback will never change nor need to change.
final VoidCallback onLayoutChangedCallback;
Size? _oldSize;
@override
void performLayout() {
super.performLayout();
// Don't send the initial notification, or this will be SizeObserver all
// over again!
if (_oldSize != null && size != _oldSize) {
onLayoutChangedCallback();
}
_oldSize = size;
}
}