首页 > 解决方案 > Flutter 中的视差式标题滚动性能

问题描述

我正在我的颤振应用程序中开发一个视差样式的标题/背景块,它以大约 1/3 的前景内容速度向上滚动。前景中的所有部分都在同一个 customScrollView 中,而背景标头位于堆栈顶部的定位容器中。

在此处输入图像描述

在此处输入图像描述

我正在使用 customscrollview 上的侦听器来更新 y 偏移整数,然后使用该整数来更新堆栈内元素的顶部位置。

虽然这可以按预期工作,但我面临的问题是大量的重绘发生在滚动上,这在未来可能会影响性能。我确信可能有一种更有效的方法来实现这一点 - 例如将整个背景放在一个单独的子小部件中并将控制器从父小部件传递给它 - 但是我正在努力寻找有关这样做的任何信息,或者如果这是正确的方法。

有人可以为我指出正确的方向,以将滚动背景与前景断开连接,从而使前景不会不断重绘吗?

class ScrollingWidgetList extends StatefulWidget {
    ScrollingWidgetList();

    @override
    State<StatefulWidget> createState() {
       return _ScrollingWidgetList();
    }
  }

 class _ScrollingWidgetList extends State<ScrollingWidgetList> {
    ScrollController _controller;
    double _offsetY = 0.0;
    _scrollListener() {
    setState(() {
      _offsetY = _controller.offset;
     });
  }

 @override
   void initState() {
    _controller = ScrollController();
    _controller.addListener(_scrollListener);

    super.initState();
  }

  @override
    Widget build(BuildContext context) {
    return Stack(
  children: <Widget>[
    Positioned(
      top: -(_offsetY / 3),
      child: ConstrainedBox(
          constraints: new BoxConstraints(
              maxHeight: 300.0,
              minHeight: MediaQuery.of(context).size.width * 0.35),
          child: Container(
              decoration: BoxDecoration(
                  gradient: LinearGradient(
                begin: Alignment.topRight,
                end: Alignment.bottomLeft,
                colors: [
                  Theme.of(context).primaryColorDark,
                  Colors.blueGrey[900].withOpacity(0.8)
                ],
              )),
              height: MediaQuery.of(context).size.width * 0.35)),
      width: MediaQuery.of(context).size.width,
    ),
    CustomScrollView(controller: _controller, slivers: [
      SliverList(
          delegate: SliverChildListDelegate([
        Padding(
            padding: const EdgeInsets.only(top: 16.0, bottom: 8.0),
            child: ListTile(
              title: Padding(
                padding: const EdgeInsets.only(top: 6.0),
                child: Text('Header text',
                    style: TextStyle(
                        fontSize: 20,
                        fontWeight: FontWeight.w500,
                        color: Colors.white)),
              ),
              subtitle: Padding(
                padding: const EdgeInsets.only(bottom: 8.0),
                child: Text('Subtitle text',
                    style: TextStyle(
                        fontSize: 14,
                        fontWeight: FontWeight.w500,
                        color: Colors.white)),
              ),
            ))
      ])),
      SliverList(
          delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
          return FakeItem(
              executing: false,
              delay: index.isOdd,
              complete: false,
              cancelled: false);
        },
        childCount: 30,
      )),
    ])
  ],
  );
 }
}

标签: flutterdartparallax

解决方案


@pskink 在评论中添加了一个很好的解决方案,但他们似乎已将其删除。对于任何寻求优雅解决方案的人来说,这是确定的基础。

您可以在下面的代码中看到有两种布局正在由CustomMultiChildLayout. 希望这可以帮助任何寻找类似解决方案的人

     class ScrollList extends StatelessWidget {
      final ScrollController _controller = ScrollController();

      @override
      Widget build(BuildContext context) {
        return CustomMultiChildLayout(
          delegate: ScrollingChildComponentDelegate(_controller),
          children: <Widget>[
            // background element layout
            LayoutId(
              id: 'background',
              child: DecoratedBox(
                decoration: BoxDecoration(
                  // box decoration
                ),
              ),
            ),
            // foreground element layout
            LayoutId(
                id: 'scrollview',
                child: CustomScrollView(
                    controller: _controller,
                    physics: AlwaysScrollableScrollPhysics(),
                    slivers: [
                      SliverToBoxAdapter(
                        child: ListTile(
                            title: Text('TitleText'),
                            ),
                            subtitle: Text('SubtitleText'),
                            )),
                      ),
                      SliverList(
                        delegate: SliverChildBuilderDelegate(itemBuilder,
                            childCount: 100),
                      ),
                    ],
          
                )),
          ],
        );
      }
    }

    // itembuilder for child components
    Widget itemBuilder(BuildContext context, int index) {
      return Card(
          margin: EdgeInsets.all(6),
          child: ClipPath(
              clipper: ShapeBorderClipper(
                  shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10))),
              child: Container(
                // child element content
                )));
    }

    // controller for the animation
    class ScrollingChildComponentDelegate extends MultiChildLayoutDelegate {

      final ScrollController _controller;
      ScrollingChildComponentDelegate(this._controller) : super(relayout: _controller);

      @override
      void performLayout(Size size) {
        positionChild('background', Offset(0, -_controller.offset / 3));
        layoutChild('background',
            BoxConstraints.tightFor(width: size.width, height: size.height * 0.2));
        positionChild('scrollview', Offset.zero);
        layoutChild('scrollview', BoxConstraints.tight(size));
      }

      @override
      bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) => true;
    }

推荐阅读