簡體   English   中英

Flutter:如何處理SliverPersistentHeader中的Renderflex溢出

[英]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:

渲染輸出.gif

來源:

ClipBelowHeightSingleChildRenderObjectWidget ,它通過使用clipHeight參數將子項的高度限制在不會溢出的高度來添加所需的效果。 它垂直居中它的孩子(在這種情況下是 Chewie 播放器)。

要了解更多信息,請閱讀performLayoutpaint方法中的注釋。

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM