CustomSingleChildLayout should support listenable (#8470)
This commit is contained in:
parent
65835af4e6
commit
a9e9e3fde6
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
import 'box.dart';
|
import 'box.dart';
|
||||||
import 'debug.dart';
|
import 'debug.dart';
|
||||||
import 'object.dart';
|
import 'object.dart';
|
||||||
@ -699,12 +701,37 @@ class RenderFractionallySizedOverflowBox extends RenderAligningShiftedBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A delegate for computing the layout of a render object with a single child.
|
/// A delegate for computing the layout of a render object with a single child.
|
||||||
|
///
|
||||||
|
/// Used by [CustomSingleChildLayout] (in the widgets library) and
|
||||||
|
/// [RenderCustomSingleChildLayoutBox] (in the rendering library).
|
||||||
|
///
|
||||||
|
/// When asked to layout, [CustomSingleChildLayout] first calls [getSize] with
|
||||||
|
/// its incoming constraints to determine its size. It then calls
|
||||||
|
/// [getConstraintsForChild] to determine the constraints to apply to the child.
|
||||||
|
/// After the child completes its layout, [RenderCustomSingleChildLayoutBox]
|
||||||
|
/// calls [getPositionForChild] to determine the child's position.
|
||||||
|
///
|
||||||
|
/// The [shouldRelayout] method is called when a new instance of the class
|
||||||
|
/// 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
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [CustomSingleChildLayout], the widget that uses this delegate.
|
||||||
|
/// * [RenderCustomSingleChildLayoutBox], render object that uses this
|
||||||
|
/// delegate.
|
||||||
abstract class SingleChildLayoutDelegate {
|
abstract class SingleChildLayoutDelegate {
|
||||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
/// Creates a layout delegate.
|
||||||
/// const constructors so that they can be used in const expressions.
|
///
|
||||||
const SingleChildLayoutDelegate();
|
/// The layout will update whenever [relayout] notifies its listeners.
|
||||||
|
const SingleChildLayoutDelegate({ Listenable relayout }) : _relayout = relayout;
|
||||||
|
|
||||||
// TODO(abarth): This class should take a Listenable to drive relayout.
|
final Listenable _relayout;
|
||||||
|
|
||||||
/// The size of this object given the incoming constraints.
|
/// The size of this object given the incoming constraints.
|
||||||
///
|
///
|
||||||
@ -777,9 +804,26 @@ class RenderCustomSingleChildLayoutBox extends RenderShiftedBox {
|
|||||||
assert(newDelegate != null);
|
assert(newDelegate != null);
|
||||||
if (_delegate == newDelegate)
|
if (_delegate == newDelegate)
|
||||||
return;
|
return;
|
||||||
if (newDelegate.runtimeType != _delegate.runtimeType || newDelegate.shouldRelayout(_delegate))
|
final SingleChildLayoutDelegate oldDelegate = _delegate;
|
||||||
|
if (newDelegate.runtimeType != oldDelegate.runtimeType || newDelegate.shouldRelayout(oldDelegate))
|
||||||
markNeedsLayout();
|
markNeedsLayout();
|
||||||
_delegate = newDelegate;
|
_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) {
|
Size _getSize(BoxConstraints constraints) {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
@ -65,6 +66,25 @@ class FixedSizeLayoutDelegate extends SingleChildLayoutDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NotifierLayoutDelegate extends SingleChildLayoutDelegate {
|
||||||
|
NotifierLayoutDelegate(ValueNotifier<Size> size) : size = size, super(relayout: size);
|
||||||
|
|
||||||
|
final ValueNotifier<Size> size;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Size getSize(BoxConstraints constraints) => size.value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
|
||||||
|
return new BoxConstraints.tight(size.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRelayout(NotifierLayoutDelegate oldDelegate) {
|
||||||
|
return size != oldDelegate.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget buildFrame(SingleChildLayoutDelegate delegate) {
|
Widget buildFrame(SingleChildLayoutDelegate delegate) {
|
||||||
return new Center(
|
return new Center(
|
||||||
child: new CustomSingleChildLayout(
|
child: new CustomSingleChildLayout(
|
||||||
@ -137,4 +157,19 @@ void main() {
|
|||||||
box = tester.renderObject(find.byType(CustomSingleChildLayout));
|
box = tester.renderObject(find.byType(CustomSingleChildLayout));
|
||||||
expect(box.size, equals(const Size(150.0, 240.0)));
|
expect(box.size, equals(const Size(150.0, 240.0)));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Can use listener for relayout', (WidgetTester tester) async {
|
||||||
|
ValueNotifier<Size> size = new ValueNotifier<Size>(const Size(100.0, 200.0));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildFrame(new NotifierLayoutDelegate(size)));
|
||||||
|
|
||||||
|
RenderBox box = tester.renderObject(find.byType(CustomSingleChildLayout));
|
||||||
|
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(CustomSingleChildLayout));
|
||||||
|
expect(box.size, equals(const Size(150.0, 240.0)));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user