| 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 'layout_builder.dart'; |
| 9 | |
| 10 | /// The signature of the [SliverLayoutBuilder] builder function. |
| 11 | typedef 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. |
| 27 | class 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 | |
| 37 | class _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 | |