【问题标题】:Flutter: Display SliverList in "shaped" out area of SliverPersistentHeaderFlutter:在 SliverPersistentHeader 的“成形”区域中显示 SliverList
【发布时间】:2021-10-18 17:39:37
【问题描述】:

我有一个 CustomScrollView 持有

  • SliverPersistentHeader 以及
  • SliverList

我将一些ShapeBorder应用于SliverPersistentHeaderDelegate中返回的Widget

我现在希望我的“下方”放置SliverList 以使用SliverPersistentHeader 的一些剪切区域,如此屏幕截图所示:

这怎么可能?

这是我的代码:

import 'package:flutter/material.dart';

void main() => runApp(
    MediaQuery(data: MediaQueryData(), child: MaterialApp(home: MyApp())));

class MyApp extends StatefulWidget {
  // This widget is the root of your application.
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Container(
            child: CustomScrollView(
      slivers: <Widget>[
        SliverPersistentHeader(
          pinned: true,
          delegate: CustomSliverPersistentHeader(),
        ),
        SliverList(delegate: SliverChildBuilderDelegate(
          (BuildContext context, int index) {
            return Card(
                child: Padding(
              padding: EdgeInsets.all(10),
              child: Text('text $index'),
            ));
          },
        ))
      ],
    )));
  }
}

class CustomSliverPersistentHeader extends SliverPersistentHeaderDelegate {
  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return LayoutBuilder(builder: (context, constraints) {
      return Container(
          decoration: ShapeDecoration(
              color: Colors.amber, shape: CustomShape(shrinkOffset)),
          height: constraints.maxHeight,
          child: Container());
    });
  }

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate _) => true;

  @override
  double get maxExtent => 400.0;

  @override
  double get minExtent => 100.0;
}

class CustomShape extends ShapeBorder {
  final double shrinkOffset;

  CustomShape(this.shrinkOffset);

  @override
  Path getOuterPath(Rect rect, {TextDirection textDirection}) {
    double scrollOffset = 0.0;
    if (shrinkOffset <= 100) {
      scrollOffset = 100.00 - shrinkOffset;
    }

    Offset controllPoint1 = Offset(0, rect.size.height - scrollOffset);
    Offset endPoint1 = Offset(scrollOffset, rect.size.height - scrollOffset);
    Offset controllPoint2 =
        Offset(rect.size.width, rect.size.height - scrollOffset);
    Offset endPoint2 =
        Offset(rect.size.width, rect.size.height - scrollOffset * 2);

    return Path()
      ..lineTo(0, rect.size.height)
      ..quadraticBezierTo(
          controllPoint1.dx, controllPoint1.dy, endPoint1.dx, endPoint1.dy)
      ..lineTo(rect.size.width - scrollOffset, rect.size.height - scrollOffset)
      ..quadraticBezierTo(
          controllPoint2.dx, controllPoint2.dy, endPoint2.dx, endPoint2.dy)
      ..lineTo(rect.size.width, 0);
  }

  @override
  EdgeInsetsGeometry get dimensions => EdgeInsets.only(bottom: 0);

  @override
  Path getInnerPath(Rect rect, {TextDirection textDirection}) => null;

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {}

  @override
  ShapeBorder scale(double t) => this;
}

【问题讨论】:

  • SizedOverflowBox 也许?
  • 我能够以不同的方式向上移动列表,但我们的标题覆盖了 ListView 的顶部项目。我认为最好的方法是使用堆栈。
  • 谢谢@pskink!我尝试将它作为父级应用到我的SliverList,然后必须将其转换为ListView,并且所有这些都必须变为SliverToBoxAdapter,这将我的List 向上移动,但它的行为很奇怪,其余的List 被切断了。在上面的代码中,您将在何处以及如何应用SizedOverflowBox
  • 所以它不是那么聪明;-( - 似乎我最初的想法更好:但是使用OverflowBox(不是SizedOverflowBox),检查paste.ubuntu.com/p/CQ9BrjQ4j3(老实说我不知道​​为什么它有效:size: constraints.biggest + Offset(0, 100) - 恕我直言,这里不应该修复100 ...)
  • 非常感谢,检查paste.ubuntu.com/p/ctSMCqxFdh - 我更改了leftRect / rightRect 计算,现在它使用size: constraints.biggest + Offset(0, max(0, 100 - shrinkOffset)), 而不是size: constraints.biggest + Offset(0, 100), - 恕我直言,现在看起来更好了 - 如果你想要你可以在你的自我回答中使用它

标签: flutter flutter-sliver


【解决方案1】:

感谢@pskink 在这方面的大力帮助,我了解到我必须使用 OverflowBox(这会让它的子级溢出):

import 'dart:math';
import 'package:flutter/material.dart';

void main() => runApp(
    MediaQuery(data: MediaQueryData(), child: MaterialApp(home: MyApp())));

class MyApp extends StatefulWidget {
  // This widget is the root of your application.
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Container(
            child: CustomScrollView(
      slivers: <Widget>[
        SliverPersistentHeader(
          pinned: true,
          delegate: CustomSliverPersistentHeader(),
        ),
        SliverList(delegate: SliverChildBuilderDelegate(
          (BuildContext context, int index) {
            return Card(
                child: Padding(
              padding: EdgeInsets.all(10),
              child: Text('text $index'),
            ));
          },
        ))
      ],
    )));
  }
}

class CustomSliverPersistentHeader extends SliverPersistentHeaderDelegate {
  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return LayoutBuilder(builder: (context, constraints) {
      return OverflowBox(
        maxHeight: constraints.biggest.height + 100,
        alignment: Alignment.topCenter,
        child: SizedBox.fromSize(
          size: constraints.biggest + Offset(0, max(0, 100 - shrinkOffset)),
          // The following Container can be replaced by a ClipPath with a
          // ShapeBorderClipper, albeit at the expense of not being able to add
          // shadows and other fancy "Container" stuff ;-)
          child: Container(
            clipBehavior: Clip.antiAlias,
            decoration: ShapeDecoration(shape: CustomShape(shrinkOffset)),
            child: Container(
              color: Colors.amber,
            ),
          ),
        ),
      );
    });
  }

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate _) => true;

  @override
  double get maxExtent => 400.0;

  @override
  double get minExtent => 100.0;
}

class CustomShape extends ShapeBorder {
  final double shrinkOffset;

  CustomShape(this.shrinkOffset);

  @override
  Path getOuterPath(Rect rect, {TextDirection textDirection}) {
    double scrollOffset = 0.0;
    if (shrinkOffset <= 100) {
      scrollOffset = 100.00 - shrinkOffset;
    }

    Offset controllPoint1 = Offset(0, rect.size.height - scrollOffset);
    Offset endPoint1 = Offset(scrollOffset, rect.size.height - scrollOffset);
    Offset controllPoint2 =
        Offset(rect.size.width, rect.size.height - scrollOffset);
    Offset endPoint2 =
        Offset(rect.size.width, rect.size.height - scrollOffset * 2);

    return Path()
      ..lineTo(0, rect.size.height)
      ..quadraticBezierTo(
          controllPoint1.dx, controllPoint1.dy, endPoint1.dx, endPoint1.dy)
      ..lineTo(rect.size.width - scrollOffset, rect.size.height - scrollOffset)
      ..quadraticBezierTo(
          controllPoint2.dx, controllPoint2.dy, endPoint2.dx, endPoint2.dy)
      ..lineTo(rect.size.width, 0);
  }

  @override
  EdgeInsetsGeometry get dimensions => EdgeInsets.only(bottom: 0);

  @override
  Path getInnerPath(Rect rect, {TextDirection textDirection}) => null;

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {}

  @override
  ShapeBorder scale(double t) => this;
}

如果您想在SliverPersistentHeaderSliverList 之间添加一些间距,您可以将后者包装在SliverPadding 中。

【讨论】:

    猜你喜欢
    • 2018-10-26
    • 2022-01-11
    • 1970-01-01
    • 1970-01-01
    • 2020-07-10
    • 2018-03-15
    • 2019-10-31
    • 2021-01-22
    • 2015-01-02
    相关资源
    最近更新 更多