1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | import 'package:flutter/rendering.dart'; |
6 | |
7 | import 'framework.dart'; |
8 | import 'notification_listener.dart'; |
9 | |
10 | /// Indicates that the size of one of the descendants of the object receiving |
11 | /// this notification has changed, and that therefore any assumptions about that |
12 | /// layout are no longer valid. |
13 | /// |
14 | /// For example, sent by the [SizeChangedLayoutNotifier] widget whenever that |
15 | /// widget changes size. |
16 | /// |
17 | /// This notification can be used for triggering repaints, but if you use this |
18 | /// notification to trigger rebuilds or relayouts, you'll create a backwards |
19 | /// dependency in the frame pipeline because [SizeChangedLayoutNotification]s |
20 | /// are generated during layout, which is after the build phase and in the |
21 | /// middle of the layout phase. This backwards dependency can lead to visual |
22 | /// corruption or lags. |
23 | /// |
24 | /// See [LayoutChangedNotification] for additional discussion of layout |
25 | /// notifications such as this one. |
26 | /// |
27 | /// See also: |
28 | /// |
29 | /// * [SizeChangedLayoutNotifier], which sends this notification. |
30 | /// * [LayoutChangedNotification], of which this is a subclass. |
31 | class SizeChangedLayoutNotification extends LayoutChangedNotification { |
32 | /// Create a new [SizeChangedLayoutNotification]. |
33 | const SizeChangedLayoutNotification(); |
34 | } |
35 | |
36 | /// A widget that automatically dispatches a [SizeChangedLayoutNotification] |
37 | /// when the layout dimensions of its child change. |
38 | /// |
39 | /// The notification is not sent for the initial layout (since the size doesn't |
40 | /// change in that case, it's just established). |
41 | /// |
42 | /// To listen for the notification dispatched by this widget, use a |
43 | /// [NotificationListener<SizeChangedLayoutNotification>]. |
44 | /// |
45 | /// The [Material] class listens for [LayoutChangedNotification]s, including |
46 | /// [SizeChangedLayoutNotification]s, to repaint [InkResponse] and [InkWell] ink |
47 | /// effects. When a widget is likely to change size, wrapping it in a |
48 | /// [SizeChangedLayoutNotifier] will cause the ink effects to correctly repaint |
49 | /// when the child changes size. |
50 | /// |
51 | /// See also: |
52 | /// |
53 | /// * [Notification], the base class for notifications that bubble through the |
54 | /// widget tree. |
55 | class SizeChangedLayoutNotifier extends SingleChildRenderObjectWidget { |
56 | /// Creates a [SizeChangedLayoutNotifier] that dispatches layout changed |
57 | /// notifications when [child] changes layout size. |
58 | const SizeChangedLayoutNotifier({ |
59 | super.key, |
60 | super.child, |
61 | }); |
62 | |
63 | @override |
64 | RenderObject createRenderObject(BuildContext context) { |
65 | return _RenderSizeChangedWithCallback( |
66 | onLayoutChangedCallback: () { |
67 | const SizeChangedLayoutNotification().dispatch(context); |
68 | }, |
69 | ); |
70 | } |
71 | } |
72 | |
73 | class _RenderSizeChangedWithCallback extends RenderProxyBox { |
74 | _RenderSizeChangedWithCallback({ |
75 | RenderBox? child, |
76 | required this.onLayoutChangedCallback, |
77 | }) : super(child); |
78 | |
79 | // There's a 1:1 relationship between the _RenderSizeChangedWithCallback and |
80 | // the `context` that is captured by the closure created by createRenderObject |
81 | // above to assign to onLayoutChangedCallback, and thus we know that the |
82 | // onLayoutChangedCallback will never change nor need to change. |
83 | |
84 | final VoidCallback onLayoutChangedCallback; |
85 | |
86 | Size? _oldSize; |
87 | |
88 | @override |
89 | void performLayout() { |
90 | super.performLayout(); |
91 | // Don't send the initial notification, or this will be SizeObserver all |
92 | // over again! |
93 | if (_oldSize != null && size != _oldSize) { |
94 | onLayoutChangedCallback(); |
95 | } |
96 | _oldSize = size; |
97 | } |
98 | } |
99 | |