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
6/// @docImport 'notification_listener.dart';
7/// @docImport 'scroll_view.dart';
8/// @docImport 'sliver_floating_header.dart';
9/// @docImport 'sliver_persistent_header.dart';
10/// @docImport 'sliver_resizing_header.dart';
11library;
12
13import 'dart:math' as math;
14
15import 'package:flutter/foundation.dart';
16import 'package:flutter/rendering.dart';
17
18import 'framework.dart';
19
20/// A sliver that keeps its Widget child at the top of the a [CustomScrollView].
21///
22/// This sliver is preferable to the general purpose [SliverPersistentHeader]
23/// for its relatively narrow use case because there's no need to create a
24/// [SliverPersistentHeaderDelegate] or to predict the header's size.
25///
26/// {@tool dartpad}
27/// This example demonstrates that the sliver's size can change. Pressing the
28/// floating action button replaces the one line of header text with two lines.
29///
30/// ** See code in examples/api/lib/widgets/sliver/pinned_header_sliver.0.dart **
31/// {@end-tool}
32///
33/// {@tool dartpad}
34/// A more elaborate example which creates an app bar that's similar to the one
35/// that appears in the iOS Settings app. In this example the pinned header
36/// starts out transparent and the first item in the list serves as the app's
37/// "Settings" title. When the title item has been scrolled completely behind
38/// the pinned header, the header animates its opacity from 0 to 1 and its
39/// (centered) "Settings" title appears. The fact that the header's opacity
40/// depends on the height of the title item - which is unknown until the list
41/// has been laid out - necessitates monitoring the title item's
42/// [SliverGeometry.scrollExtent] and the header's [SliverConstraints.scrollOffset]
43/// from a scroll [NotificationListener]. See the source code for more details.
44///
45/// ** See code in examples/api/lib/widgets/sliver/pinned_header_sliver.1.dart **
46/// {@end-tool}
47///
48/// See also:
49///
50/// * [SliverResizingHeader] - which similarly pins the header at the top
51/// of the [CustomScrollView] but reacts to scrolling by resizing the header
52/// between its minimum and maximum extent limits.
53/// * [SliverFloatingHeader] - which animates the header in and out of view
54/// in response to downward and upwards scrolls.
55/// * [SliverPersistentHeader] - a general purpose header that can be
56/// configured as a pinned, resizing, or floating header.
57class PinnedHeaderSliver extends SingleChildRenderObjectWidget {
58 /// Creates a sliver whose [Widget] child appears at the top of a
59 /// [CustomScrollView].
60 const PinnedHeaderSliver({
61 super.key,
62 super.child,
63 });
64
65 @override
66 RenderObject createRenderObject(BuildContext context) {
67 return _RenderPinnedHeaderSliver();
68 }
69}
70
71class _RenderPinnedHeaderSliver extends RenderSliverSingleBoxAdapter {
72 _RenderPinnedHeaderSliver();
73
74 double get childExtent {
75 if (child == null) {
76 return 0.0;
77 }
78 assert(child!.hasSize);
79 return switch (constraints.axis) {
80 Axis.vertical => child!.size.height,
81 Axis.horizontal => child!.size.width,
82 };
83 }
84
85 @override
86 double childMainAxisPosition(covariant RenderObject child) => 0;
87
88 @override
89 void performLayout() {
90 final SliverConstraints constraints = this.constraints;
91 child?.layout(constraints.asBoxConstraints(), parentUsesSize: true);
92
93 final double layoutExtent = clampDouble(childExtent - constraints.scrollOffset, 0, constraints.remainingPaintExtent);
94 final double paintExtent = math.min(childExtent, constraints.remainingPaintExtent - constraints.overlap);
95 geometry = SliverGeometry(
96 scrollExtent: childExtent,
97 paintOrigin: constraints.overlap,
98 paintExtent: paintExtent,
99 layoutExtent: layoutExtent,
100 maxPaintExtent: childExtent,
101 maxScrollObstructionExtent: childExtent,
102 cacheExtent: calculateCacheOffset(constraints, from: 0.0, to: childExtent),
103 hasVisualOverflow: true, // Conservatively say we do have overflow to avoid complexity.
104 );
105 }
106}
107