[英]Flutter: How to handle Renderflex overflow in SliverPersistentHeader
我有一個包含視頻的 SliverPersistentHeader。 此視圖的預期行為是當用戶向上滾動時,視圖應覆蓋或最小化視頻的大小。 視頻 header 是一個包含 Chewie 視頻播放器的小部件。 所需的行為會達到某個點,此時我會出現像素溢出,如 animation 所示:
當滾動到某個點時,視頻無法再調整大小並導致渲染溢出。 期望的行為是視頻繼續調整大小直到它消失,或者捕獲錯誤並從視圖中隱藏或刪除視頻。 呈現此滾動視圖的代碼是:
Widget buildScollView(GenericScreenModel model) {
return CustomScrollView(
slivers: [
StandardHeader(),
SliverFillRemaining(
child: Container(
// color: Colors.transparent,
decoration: BoxDecoration(
border: Border.all(
color: Colors.white,
),
borderRadius: BorderRadius.only(topRight: radius, topLeft: radius)),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Text(model.model?.getContentText ?? 'Empty'),
)),
)
],
);
}
StandardHeader class 是一個包含 Chewie 視頻的簡單小部件。
class _StandardHeaderState extends State<StandardHeader> {
@override
Widget build(BuildContext context) {
return SliverPersistentHeader(
floating: true,
delegate: Delegate(
Colors.blue,
'Header Title',
),
pinned: true,
);
}
}
有沒有辦法捕獲此錯誤並隱藏視頻播放器? 任何人都可以幫助解決這個問題或指出我的資源嗎? 謝謝!
問題似乎與 Chewie 和/或視頻播放器小部件有關。 如果 header 的高度小於 player 的要求高度,就會發生溢出。
您可以使用SingleChildRenderObjectWidget
來達到預期的效果。 我添加了一個您可以輕松刪除的不透明度因子,這使它(在我看來)具有額外的觸感。
我將這個小部件命名為: ClipBelowHeight
Output:
來源:
ClipBelowHeight
是SingleChildRenderObjectWidget
,它通過使用clipHeight
參數將子項的高度限制在不會溢出的高度來添加所需的效果。 它垂直居中它的孩子(在這種情況下是 Chewie 播放器)。
要了解更多信息,請閱讀performLayout
和paint
方法中的注釋。
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class ClipBelowHeight extends SingleChildRenderObjectWidget {
const ClipBelowHeight({
super.key,
super.child,
required this.clipHeight,
required this.opacityFactor,
});
/// The minimum height the [child] must have, as well as the height at which
/// clipping begins.
final double clipHeight;
/// The opacity factor to apply when the height decreases.
final double opacityFactor;
@override
RenderObject createRenderObject(BuildContext context) {
return RenderClipBelowHeight(clipHeight: clipHeight, factor: opacityFactor);
}
@override
void updateRenderObject(
BuildContext context,
RenderClipBelowHeight renderObject,
) {
renderObject
..clipHeight = clipHeight
..factor = opacityFactor;
}
}
class RenderClipBelowHeight extends RenderBox with RenderObjectWithChildMixin {
RenderClipBelowHeight({required double clipHeight, required double factor})
: _clipHeight = clipHeight,
_factor = factor;
double _clipHeight;
double get clipHeight => _clipHeight;
set clipHeight(double value) {
assert(value >= .0);
if (_clipHeight == value) return;
_clipHeight = value;
markNeedsLayout();
}
double _factor;
double get factor => _factor;
set factor(double value) {
assert(value >= .0);
if (_factor == value) return;
_factor = value;
markNeedsLayout();
}
@override
bool get sizedByParent => false;
@override
void performLayout() {
/// The child contraints depend on whether [constraints.maxHeight] is less
/// than [clipHeight]. This RenderObject's responsibility is to ensure that
/// the child's height is never below [clipHeight], because when the
/// child's height is below [clipHeight], then there will be visual
/// overflow.
final childConstraints = constraints.maxHeight < _clipHeight
? BoxConstraints.tight(Size(constraints.maxWidth, _clipHeight))
: constraints;
(child as RenderBox).layout(childConstraints, parentUsesSize: true);
size = Size(constraints.maxWidth, constraints.maxHeight);
}
@override
void paint(PaintingContext context, Offset offset) {
final theChild = child as RenderBox;
/// Clip the painted area to [size], which allows the [child] height to
/// be greater than [size] without overflowing.
context.pushClipRect(
true,
offset,
Offset.zero & size,
(PaintingContext context, Offset offset) {
/// (optional) Set the opacity by applying the specified factor.
context.pushOpacity(
offset,
/// The opacity begins to take effect at approximately half [size].
((255.0 + 128.0) * _factor).toInt(),
(context, offset) {
/// Ensure the child remains centered vertically based on [size].
final centeredOffset =
Offset(.0, (size.height - theChild.size.height) / 2.0);
context.paintChild(theChild, centeredOffset + offset);
},
);
},
);
}
@override
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
final theChild = child as RenderBox;
var childParentData = theChild.parentData as BoxParentData;
final isHit = result.addWithPaintOffset(
offset: childParentData.offset,
position: position,
hitTest: (BoxHitTestResult result, Offset transformed) {
assert(transformed == position - childParentData.offset);
return theChild.hitTest(result, position: transformed);
},
);
return isHit;
}
@override
Size computeDryLayout(BoxConstraints constraints) => constraints.biggest;
@override
double computeMinIntrinsicWidth(double height) =>
(child as RenderBox).getMinIntrinsicWidth(height);
@override
double computeMaxIntrinsicWidth(double height) =>
(child as RenderBox).getMaxIntrinsicWidth(height);
@override
double computeMinIntrinsicHeight(double width) =>
(child as RenderBox).getMinIntrinsicHeight(width);
@override
double computeMaxIntrinsicHeight(double width) =>
(child as RenderBox).getMaxIntrinsicHeight(width);
}
使用ClipBelowHeight
小部件的小部件是您的 header 委托。 這個小部件應該是不言自明的,我認為您將能夠理解它。
class Delegate extends SliverPersistentHeaderDelegate {
Delegate(this.color, this.player);
final Color color;
final Chewie player;
@override
Widget build(
BuildContext context,
double shrinkOffset,
bool overlapsContent,
) {
return Container(
color: color,
child: ClipBelowHeight(
clipHeight: 80.0,
opacityFactor: 1.0 - shrinkOffset / maxExtent,
child: player,
),
);
}
@override
double get maxExtent => 150.0;
@override
double get minExtent => .0;
@override
bool shouldRebuild(Delegate oldDelegate) {
return color != oldDelegate.color || player != oldDelegate.player;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.