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 'dart:math' as math; |

6 | |

7 | import 'box.dart'; |

8 | import 'object.dart'; |

9 | import 'sliver.dart'; |

10 | import 'sliver_fixed_extent_list.dart'; |

11 | |

12 | /// A sliver that contains multiple box children that each fill the viewport. |

13 | /// |

14 | /// [RenderSliverFillViewport] places its children in a linear array along the |

15 | /// main axis. Each child is sized to fill the viewport, both in the main and |

16 | /// cross axis. A [viewportFraction] factor can be provided to size the children |

17 | /// to a multiple of the viewport's main axis dimension (typically a fraction |

18 | /// less than 1.0). |

19 | /// |

20 | /// See also: |

21 | /// |

22 | /// * [RenderSliverFillRemaining], which sizes the children based on the |

23 | /// remaining space rather than the viewport itself. |

24 | /// * [RenderSliverFixedExtentList], which has a configurable [itemExtent]. |

25 | /// * [RenderSliverList], which does not require its children to have the same |

26 | /// extent in the main axis. |

27 | class RenderSliverFillViewport extends RenderSliverFixedExtentBoxAdaptor { |

28 | /// Creates a sliver that contains multiple box children that each fill the |

29 | /// viewport. |

30 | RenderSliverFillViewport({ |

31 | required super.childManager, |

32 | double viewportFraction = 1.0, |

33 | }) : assert(viewportFraction > 0.0), |

34 | _viewportFraction = viewportFraction; |

35 | |

36 | @override |

37 | double get itemExtent => constraints.viewportMainAxisExtent * viewportFraction; |

38 | |

39 | /// The fraction of the viewport that each child should fill in the main axis. |

40 | /// |

41 | /// If this fraction is less than 1.0, more than one child will be visible at |

42 | /// once. If this fraction is greater than 1.0, each child will be larger than |

43 | /// the viewport in the main axis. |

44 | double get viewportFraction => _viewportFraction; |

45 | double _viewportFraction; |

46 | set viewportFraction(double value) { |

47 | if (_viewportFraction == value) { |

48 | return; |

49 | } |

50 | _viewportFraction = value; |

51 | markNeedsLayout(); |

52 | } |

53 | } |

54 | |

55 | /// A sliver that contains a single box child that contains a scrollable and |

56 | /// fills the viewport. |

57 | /// |

58 | /// [RenderSliverFillRemainingWithScrollable] sizes its child to fill the |

59 | /// viewport in the cross axis and to fill the remaining space in the viewport |

60 | /// in the main axis. |

61 | /// |

62 | /// Typically this will be the last sliver in a viewport, since (by definition) |

63 | /// there is never any room for anything beyond this sliver. |

64 | /// |

65 | /// See also: |

66 | /// |

67 | /// * [NestedScrollView], which uses this sliver for the inner scrollable. |

68 | /// * [RenderSliverFillRemaining], which lays out its |

69 | /// non-scrollable child slightly different than this widget. |

70 | /// * [RenderSliverFillRemainingAndOverscroll], which incorporates the |

71 | /// overscroll into the remaining space to fill. |

72 | /// * [RenderSliverFillViewport], which sizes its children based on the |

73 | /// size of the viewport, regardless of what else is in the scroll view. |

74 | /// * [RenderSliverList], which shows a list of variable-sized children in a |

75 | /// viewport. |

76 | class RenderSliverFillRemainingWithScrollable extends RenderSliverSingleBoxAdapter { |

77 | /// Creates a [RenderSliver] that wraps a scrollable [RenderBox] which is |

78 | /// sized to fit the remaining space in the viewport. |

79 | RenderSliverFillRemainingWithScrollable({ super.child }); |

80 | |

81 | @override |

82 | void performLayout() { |

83 | final SliverConstraints constraints = this.constraints; |

84 | final double extent = constraints.remainingPaintExtent - math.min(constraints.overlap, 0.0); |

85 | |

86 | if (child != null) { |

87 | child!.layout(constraints.asBoxConstraints( |

88 | minExtent: extent, |

89 | maxExtent: extent, |

90 | )); |

91 | } |

92 | |

93 | final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: extent); |

94 | assert(paintedChildSize.isFinite); |

95 | assert(paintedChildSize >= 0.0); |

96 | geometry = SliverGeometry( |

97 | scrollExtent: constraints.viewportMainAxisExtent, |

98 | paintExtent: paintedChildSize, |

99 | maxPaintExtent: paintedChildSize, |

100 | hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0, |

101 | ); |

102 | if (child != null) { |

103 | setChildParentData(child!, constraints, geometry!); |

104 | } |

105 | } |

106 | } |

107 | |

108 | /// A sliver that contains a single box child that is non-scrollable and fills |

109 | /// the remaining space in the viewport. |

110 | /// |

111 | /// [RenderSliverFillRemaining] sizes its child to fill the |

112 | /// viewport in the cross axis and to fill the remaining space in the viewport |

113 | /// in the main axis. |

114 | /// |

115 | /// Typically this will be the last sliver in a viewport, since (by definition) |

116 | /// there is never any room for anything beyond this sliver. |

117 | /// |

118 | /// See also: |

119 | /// |

120 | /// * [RenderSliverFillRemainingWithScrollable], which lays out its scrollable |

121 | /// child slightly different than this widget. |

122 | /// * [RenderSliverFillRemainingAndOverscroll], which incorporates the |

123 | /// overscroll into the remaining space to fill. |

124 | /// * [RenderSliverFillViewport], which sizes its children based on the |

125 | /// size of the viewport, regardless of what else is in the scroll view. |

126 | /// * [RenderSliverList], which shows a list of variable-sized children in a |

127 | /// viewport. |

128 | class RenderSliverFillRemaining extends RenderSliverSingleBoxAdapter { |

129 | /// Creates a [RenderSliver] that wraps a non-scrollable [RenderBox] which is |

130 | /// sized to fit the remaining space in the viewport. |

131 | RenderSliverFillRemaining({ super.child }); |

132 | |

133 | @override |

134 | void performLayout() { |

135 | final SliverConstraints constraints = this.constraints; |

136 | // The remaining space in the viewportMainAxisExtent. Can be <= 0 if we have |

137 | // scrolled beyond the extent of the screen. |

138 | double extent = constraints.viewportMainAxisExtent - constraints.precedingScrollExtent; |

139 | |

140 | if (child != null) { |

141 | final double childExtent; |

142 | switch (constraints.axis) { |

143 | case Axis.horizontal: |

144 | childExtent = child!.getMaxIntrinsicWidth(constraints.crossAxisExtent); |

145 | case Axis.vertical: |

146 | childExtent = child!.getMaxIntrinsicHeight(constraints.crossAxisExtent); |

147 | } |

148 | |

149 | // If the childExtent is greater than the computed extent, we want to use |

150 | // that instead of potentially cutting off the child. This allows us to |

151 | // safely specify a maxExtent. |

152 | extent = math.max(extent, childExtent); |

153 | child!.layout(constraints.asBoxConstraints( |

154 | minExtent: extent, |

155 | maxExtent: extent, |

156 | )); |

157 | } |

158 | |

159 | assert(extent.isFinite, |

160 | 'The calculated extent for the child of SliverFillRemaining is not finite. ' |

161 | 'This can happen if the child is a scrollable, in which case, the ' |

162 | 'hasScrollBody property of SliverFillRemaining should not be set to ' |

163 | 'false.', |

164 | ); |

165 | final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: extent); |

166 | assert(paintedChildSize.isFinite); |

167 | assert(paintedChildSize >= 0.0); |

168 | geometry = SliverGeometry( |

169 | scrollExtent: extent, |

170 | paintExtent: paintedChildSize, |

171 | maxPaintExtent: paintedChildSize, |

172 | hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0, |

173 | ); |

174 | if (child != null) { |

175 | setChildParentData(child!, constraints, geometry!); |

176 | } |

177 | } |

178 | } |

179 | |

180 | /// A sliver that contains a single box child that is non-scrollable and fills |

181 | /// the remaining space in the viewport including any overscrolled area. |

182 | /// |

183 | /// [RenderSliverFillRemainingAndOverscroll] sizes its child to fill the |

184 | /// viewport in the cross axis and to fill the remaining space in the viewport |

185 | /// in the main axis with the overscroll area included. |

186 | /// |

187 | /// Typically this will be the last sliver in a viewport, since (by definition) |

188 | /// there is never any room for anything beyond this sliver. |

189 | /// |

190 | /// See also: |

191 | /// |

192 | /// * [RenderSliverFillRemainingWithScrollable], which lays out its scrollable |

193 | /// child without overscroll. |

194 | /// * [RenderSliverFillRemaining], which lays out its |

195 | /// non-scrollable child without overscroll. |

196 | /// * [RenderSliverFillViewport], which sizes its children based on the |

197 | /// size of the viewport, regardless of what else is in the scroll view. |

198 | /// * [RenderSliverList], which shows a list of variable-sized children in a |

199 | /// viewport. |

200 | class RenderSliverFillRemainingAndOverscroll extends RenderSliverSingleBoxAdapter { |

201 | /// Creates a [RenderSliver] that wraps a non-scrollable [RenderBox] which is |

202 | /// sized to fit the remaining space plus any overscroll in the viewport. |

203 | RenderSliverFillRemainingAndOverscroll({ super.child }); |

204 | |

205 | @override |

206 | void performLayout() { |

207 | final SliverConstraints constraints = this.constraints; |

208 | // The remaining space in the viewportMainAxisExtent. Can be <= 0 if we have |

209 | // scrolled beyond the extent of the screen. |

210 | double extent = constraints.viewportMainAxisExtent - constraints.precedingScrollExtent; |

211 | // The maxExtent includes any overscrolled area. Can be < 0 if we have |

212 | // overscroll in the opposite direction, away from the end of the list. |

213 | double maxExtent = constraints.remainingPaintExtent - math.min(constraints.overlap, 0.0); |

214 | |

215 | if (child != null) { |

216 | final double childExtent; |

217 | switch (constraints.axis) { |

218 | case Axis.horizontal: |

219 | childExtent = child!.getMaxIntrinsicWidth(constraints.crossAxisExtent); |

220 | case Axis.vertical: |

221 | childExtent = child!.getMaxIntrinsicHeight(constraints.crossAxisExtent); |

222 | } |

223 | |

224 | // If the childExtent is greater than the computed extent, we want to use |

225 | // that instead of potentially cutting off the child. This allows us to |

226 | // safely specify a maxExtent. |

227 | extent = math.max(extent, childExtent); |

228 | // The extent could be larger than the maxExtent due to a larger child |

229 | // size or overscrolling at the top of the scrollable (rather than at the |

230 | // end where this sliver is). |

231 | maxExtent = math.max(extent, maxExtent); |

232 | child!.layout(constraints.asBoxConstraints(minExtent: extent, maxExtent: maxExtent)); |

233 | } |

234 | |

235 | assert(extent.isFinite, |

236 | 'The calculated extent for the child of SliverFillRemaining is not finite. ' |

237 | 'This can happen if the child is a scrollable, in which case, the ' |

238 | 'hasScrollBody property of SliverFillRemaining should not be set to ' |

239 | 'false.', |

240 | ); |

241 | final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: extent); |

242 | assert(paintedChildSize.isFinite); |

243 | assert(paintedChildSize >= 0.0); |

244 | geometry = SliverGeometry( |

245 | scrollExtent: extent, |

246 | paintExtent: math.min(maxExtent, constraints.remainingPaintExtent), |

247 | maxPaintExtent: maxExtent, |

248 | hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0, |

249 | ); |

250 | if (child != null) { |

251 | setChildParentData(child!, constraints, geometry!); |

252 | } |

253 | } |

254 | } |

255 |