【问题标题】:Parallax-style header scrolling performance in flutterFlutter 中的视差式标题滚动性能
【发布时间】:2021-09-23 01:11:48
【问题描述】:

我正在我的颤振应用程序中开发一个视差样式的标题/背景块,它以大约 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,
      )),
    ])
  ],
  );
 }
}

【问题讨论】:

  • 不要在每次滚动后使用setState - 它会重建整个小部件 - 例如使用AnimatedBuilder
  • 你能用条子做你的滚动效果吗?我不知道这个教程有多好,但是有一堆图像显示了不同的条子行为:raywenderlich.com/19539821-slivers-in-flutter-getting-started
  • 但实际上CustomMultiChildLayout 在您的情况下比Stack / AnimatedBuilder / Positioned spaghetti 更方便
  • @pskink 是的,我开始研究 AnimatedBuilder,但我无法完全找到按照我设想的方式处理它的方法,今天我将阅读 customMultiChildLayout!
  • @pskink 嘿伙计,周末没工作。我一直在使用您的代码,并且动画构建器示例几乎是正确的。 CustomMultiChildLayout 也非常好用,但为了简单起见选择了#1。这可能是一个单独的答案,而不是线程中的评论!

标签: flutter dart parallax


【解决方案1】:

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

您可以在下面的代码中看到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;
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多