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
5import 'package:flutter/rendering.dart';
6
7import 'framework.dart';
8import 'layout_builder.dart';
9
10/// The signature of the [SliverLayoutBuilder] builder function.
11typedef SliverLayoutWidgetBuilder =
12 Widget Function(BuildContext context, SliverConstraints constraints);
13
14/// Builds a sliver widget tree that can depend on its own [SliverConstraints].
15///
16/// Similar to the [LayoutBuilder] widget except its builder should return a sliver
17/// widget, and [SliverLayoutBuilder] is itself a sliver. The framework calls the
18/// [builder] function at layout time and provides the current [SliverConstraints].
19/// The [SliverLayoutBuilder]'s final [SliverGeometry] will match the [SliverGeometry]
20/// of its child.
21///
22/// {@macro flutter.widgets.ConstrainedLayoutBuilder}
23///
24/// See also:
25///
26/// * [LayoutBuilder], the non-sliver version of this widget.
27class SliverLayoutBuilder extends ConstrainedLayoutBuilder<SliverConstraints> {
28 /// Creates a sliver widget that defers its building until layout.
29 const SliverLayoutBuilder({super.key, required super.builder});
30
31 @override
32 RenderConstrainedLayoutBuilder<SliverConstraints, RenderSliver> createRenderObject(
33 BuildContext context,
34 ) => _RenderSliverLayoutBuilder();
35}
36
37class _RenderSliverLayoutBuilder extends RenderSliver
38 with
39 RenderObjectWithChildMixin<RenderSliver>,
40 RenderObjectWithLayoutCallbackMixin,
41 RenderConstrainedLayoutBuilder<SliverConstraints, RenderSliver> {
42 @override
43 double childMainAxisPosition(RenderObject child) {
44 assert(child == this.child);
45 return 0;
46 }
47
48 @override
49 void performLayout() {
50 runLayoutCallback();
51 child?.layout(constraints, parentUsesSize: true);
52 geometry = child?.geometry ?? SliverGeometry.zero;
53 }
54
55 @override
56 void applyPaintTransform(RenderObject child, Matrix4 transform) {
57 assert(child == this.child);
58 // child's offset is always (0, 0), transform.translate(0, 0) does not mutate the transform.
59 }
60
61 @override
62 void paint(PaintingContext context, Offset offset) {
63 // This renderObject does not introduce additional offset to child's position.
64 if (child?.geometry?.visible ?? false) {
65 context.paintChild(child!, offset);
66 }
67 }
68
69 @override
70 bool hitTestChildren(
71 SliverHitTestResult result, {
72 required double mainAxisPosition,
73 required double crossAxisPosition,
74 }) {
75 return child != null &&
76 child!.geometry!.hitTestExtent > 0 &&
77 child!.hitTest(
78 result,
79 mainAxisPosition: mainAxisPosition,
80 crossAxisPosition: crossAxisPosition,
81 );
82 }
83}
84