Adds relayout option to CustomMultiChildLayout. (#39252)
This commit is contained in:
parent
86d200a943
commit
bf216110ff
3
AUTHORS
3
AUTHORS
@ -40,4 +40,5 @@ Frederik Schweiger <mail@flschweiger.net>
|
||||
Martin Staadecker <machstg@gmail.com>
|
||||
Igor Katsuba <katsuba.igor@gmail.com>
|
||||
Diego Velásquez <diego.velasquez.lopez@gmail.com>
|
||||
Sarbagya Dhaubanjar <mail@sarbagyastha.com.np>
|
||||
Simon Lightfoot <simon@devangels.london>
|
||||
Sarbagya Dhaubanjar <mail@sarbagyastha.com.np>
|
||||
|
@ -20,6 +20,9 @@ class MultiChildLayoutParentData extends ContainerBoxParentData<RenderBox> {
|
||||
|
||||
/// A delegate that controls the layout of multiple children.
|
||||
///
|
||||
/// Used with [CustomMultiChildLayout] (in the widgets library) and
|
||||
/// [RenderCustomMultiChildLayoutBox] (in the rendering library).
|
||||
///
|
||||
/// Delegates must be idempotent. Specifically, if two delegates are equal, then
|
||||
/// they must produce the same layout. To change the layout, replace the
|
||||
/// delegate with a different instance whose [shouldRelayout] returns true when
|
||||
@ -38,8 +41,11 @@ class MultiChildLayoutParentData extends ContainerBoxParentData<RenderBox> {
|
||||
/// Override [shouldRelayout] to determine when the layout of the children needs
|
||||
/// to be recomputed when the delegate changes.
|
||||
///
|
||||
/// Used with [CustomMultiChildLayout], the widget for the
|
||||
/// [RenderCustomMultiChildLayoutBox] render object.
|
||||
/// The most efficient way to trigger a relayout is to supply a `relayout`
|
||||
/// argument to the constructor of the [MultiChildLayoutDelegate]. The custom
|
||||
/// layout will listen to this value and relayout whenever the Listenable
|
||||
/// notifies its listeners, such as when an [Animation] ticks. This allows
|
||||
/// the custom layout to avoid the build phase of the pipeline.
|
||||
///
|
||||
/// Each child must be wrapped in a [LayoutId] widget to assign the id that
|
||||
/// identifies it to the delegate. The [LayoutId.id] needs to be unique among
|
||||
@ -94,7 +100,20 @@ class MultiChildLayoutParentData extends ContainerBoxParentData<RenderBox> {
|
||||
/// The leader and follower widget will paint in the order they appear in the
|
||||
/// child list, regardless of the order in which [layoutChild] is called on
|
||||
/// them.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [CustomMultiChildLayout], the widget that uses this delegate.
|
||||
/// * [RenderCustomMultiChildLayoutBox], render object that uses this
|
||||
/// delegate.
|
||||
abstract class MultiChildLayoutDelegate {
|
||||
/// Creates a layout delegate.
|
||||
///
|
||||
/// The layout will update whenever [relayout] notifies its listeners.
|
||||
MultiChildLayoutDelegate({ Listenable relayout }) : _relayout = relayout;
|
||||
|
||||
final Listenable _relayout;
|
||||
|
||||
Map<Object, RenderBox> _idToChild;
|
||||
Set<RenderBox> _debugChildrenNeedingLayout;
|
||||
|
||||
@ -300,13 +319,30 @@ class RenderCustomMultiChildLayoutBox extends RenderBox
|
||||
/// The delegate that controls the layout of the children.
|
||||
MultiChildLayoutDelegate get delegate => _delegate;
|
||||
MultiChildLayoutDelegate _delegate;
|
||||
set delegate(MultiChildLayoutDelegate value) {
|
||||
assert(value != null);
|
||||
if (_delegate == value)
|
||||
set delegate(MultiChildLayoutDelegate newDelegate) {
|
||||
assert(newDelegate != null);
|
||||
if (_delegate == newDelegate)
|
||||
return;
|
||||
if (value.runtimeType != _delegate.runtimeType || value.shouldRelayout(_delegate))
|
||||
final MultiChildLayoutDelegate oldDelegate = _delegate;
|
||||
if (newDelegate.runtimeType != oldDelegate.runtimeType || newDelegate.shouldRelayout(oldDelegate))
|
||||
markNeedsLayout();
|
||||
_delegate = value;
|
||||
_delegate = newDelegate;
|
||||
if (attached) {
|
||||
oldDelegate?._relayout?.removeListener(markNeedsLayout);
|
||||
newDelegate?._relayout?.addListener(markNeedsLayout);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void attach(PipelineOwner owner) {
|
||||
super.attach(owner);
|
||||
_delegate?._relayout?.addListener(markNeedsLayout);
|
||||
}
|
||||
|
||||
@override
|
||||
void detach() {
|
||||
_delegate?._relayout?.removeListener(markNeedsLayout);
|
||||
super.detach();
|
||||
}
|
||||
|
||||
Size _getSize(BoxConstraints constraints) {
|
||||
|
@ -962,10 +962,11 @@ class RenderFractionallySizedOverflowBox extends RenderAligningShiftedBox {
|
||||
/// is provided, to check if the new instance actually represents different
|
||||
/// information.
|
||||
///
|
||||
/// The most efficient way to trigger a relayout is to supply a relayout
|
||||
/// The most efficient way to trigger a relayout is to supply a `relayout`
|
||||
/// argument to the constructor of the [SingleChildLayoutDelegate]. The custom
|
||||
/// object will listen to this value and relayout whenever the animation
|
||||
/// ticks, avoiding both the build phase of the pipeline.
|
||||
/// layout will listen to this value and relayout whenever the Listenable
|
||||
/// notifies its listeners, such as when an [Animation] ticks. This allows
|
||||
/// the custom layout to avoid the build phase of the pipeline.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
|
@ -73,6 +73,23 @@ class PreferredSizeDelegate extends MultiChildLayoutDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
class NotifierLayoutDelegate extends MultiChildLayoutDelegate {
|
||||
NotifierLayoutDelegate(this.size) : super(relayout: size);
|
||||
|
||||
final ValueNotifier<Size> size;
|
||||
|
||||
@override
|
||||
Size getSize(BoxConstraints constraints) => size.value;
|
||||
|
||||
@override
|
||||
void performLayout(Size size) { }
|
||||
|
||||
@override
|
||||
bool shouldRelayout(NotifierLayoutDelegate oldDelegate) {
|
||||
return size != oldDelegate.size;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
testWidgets('Control test for CustomMultiChildLayout', (WidgetTester tester) async {
|
||||
final TestMultiChildLayoutDelegate delegate = TestMultiChildLayoutDelegate();
|
||||
@ -161,4 +178,25 @@ void main() {
|
||||
expect(box.size.width, equals(350.0));
|
||||
expect(box.size.height, equals(250.0));
|
||||
});
|
||||
|
||||
testWidgets('Can use listener for relayout', (WidgetTester tester) async {
|
||||
final ValueNotifier<Size> size = ValueNotifier<Size>(const Size(100.0, 200.0));
|
||||
|
||||
await tester.pumpWidget(
|
||||
Center(
|
||||
child: CustomMultiChildLayout(
|
||||
delegate: NotifierLayoutDelegate(size),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
RenderBox box = tester.renderObject(find.byType(CustomMultiChildLayout));
|
||||
expect(box.size, equals(const Size(100.0, 200.0)));
|
||||
|
||||
size.value = const Size(150.0, 240.0);
|
||||
await tester.pump();
|
||||
|
||||
box = tester.renderObject(find.byType(CustomMultiChildLayout));
|
||||
expect(box.size, equals(const Size(150.0, 240.0)));
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user