cyjaysong / flutter_meituan_shop

Flutter 实现类似美团外卖店铺页面滑动效果
112 stars 31 forks source link

自定义的ScrollController会导致SliverPersistentHeaderDelegate失效 #1

Closed 0x7c01 closed 2 years ago

0x7c01 commented 4 years ago

样例中的_SliverAppBarDelegate只有在初始化时才会build, 之后滑动不会再rebuild 对比了修改前后的代码,没有什么头绪,请问可以指点下问题大概出在哪里吗?

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate({
    @required this.minHeight,
    @required this.maxHeight,
    @required this.child,
  });

  final double minHeight;
  final double maxHeight;
  final Widget child;

  @override
  double get minExtent => this.minHeight;

  @override
  double get maxExtent => max(maxHeight, minHeight);

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    print('shrinkOffset: $shrinkOffset');
    print('overlapsContent: $overlapsContent');
    return SizedBox.expand(child: child);
  }

  @override
  bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
    return true;
  }
}
cyjaysong commented 4 years ago

我记得滑动的时候,好像SliverPersistentHeaderDelegate似乎本就不会一直 rebuild啊

cyjaysong commented 4 years ago

一直rebuild 的话,性能消耗那么大,flutter 估计不会这么设计

0x7c01 commented 4 years ago

1.是这样的,当SliverPersistentHeader的pinned=true时, SliverPersistentHeaderDelegate中的build方法中的overlapsContent将会从false变成true,我指的是这时候组件没有rebuild. 2.事实上,即使shouldRebuild直接返回true, 也只有当固定到头部或者离开头部的时候组件才会重新rebuild. 我想用SliverPersistentHeaderDelegate中的overlapsContent属性变化来在SliverPersistentHeader固定到头部的时候改变里面Tabbar样式, Tabbar类似这样(其中overlapsContent就是从SliverPersistentHeaderDelegate直接传过来的):

class _CustomTabBar extends StatefulWidget {

  _CustomTabBar({
    Key key,
    this.height,
    this.overlapsContent,
    this.tabController,
  });

  final double height;
  final bool overlapsContent;
  final TabController tabController;

  @override
  State<StatefulWidget> createState() => _CustomTabBarState();
}

class _CustomTabBarState extends State<_CustomTabBar> {

  @override
  Widget build(BuildContext context) {
    return Container(
      height: widget.height,
      color: widget.overlapsContent ? Colors.white : Colors.transparent,
      child: TabBar(
        controller: widget.tabController,
        indicator: UnderlineTabIndicator(
          borderSide: BorderSide(
            width: widget.overlapsContent ? 3.5 : 0.0,
            color: widget.overlapsContent ? Colors.indigoAccent : Colors.transparent,
          )
        ),
        isScrollable: false,
        tabs: [
          {"title": "商品", "subtitle": "subtitle1"},
          {"title": "评价", "subtitle": "subtitle2"},
          {"title": "商家", "subtitle": "subtitle3"},
        ].map((item) => Tab(
          child: Container(
            height: widget.height,
            child: Column(
              mainAxisSize: MainAxisSize.max,
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Expanded(
                  child: Center(
                    child: Text(item["title"], style: TextStyle(fontSize: 18.0, color: Colors.red),),
                  ),
                ),
                Offstage(
                  offstage: widget.overlapsContent,
                  child: SizedBox(
                    height: 18,
                    child: Center(
                      child: Text(item["subtitle"], style: TextStyle(fontSize: 10.0, color: Colors.orange),),
                    ),
                  ),
                )
              ],
            ),
          ),
        )).toList(),
      ),
    );
  }
}

完整的示例代码: https://github.com/ZenoZhengs/shop.sliver.bug

0x7c01 commented 4 years ago

上面代码我在ios模拟器/android模拟器/android真机上都没有使tabbar变化. 更绝的是我根据您代码仿写的代码,去掉了如下代码及其相关调用:

/// ShopScrollCoordinator
  /// 当默认位置不为0时,主部件已下拉距离超过默认位置,但超过的距离不大于该值时,
  /// 若手指离开屏幕,主部件头部会回弹至默认位置
  double _scrollRedundancy = 80;

  /// 当前页面Header最大程度展开状态
  PageExpandState pageExpand = PageExpandState.NotExpand;

  /// 当手指离开屏幕
  void onPointerUp(PointerUpEvent event) {
    final double _pagePixels = _pageScrollPosition.pixels;
    if (0.0 < _pagePixels && _pagePixels < _pageInitialOffset) {
      if (pageExpand == PageExpandState.NotExpand &&
          _pageInitialOffset - _pagePixels > _scrollRedundancy) {
        _pageScrollPosition
            .animateTo(0.0,
                duration: const Duration(milliseconds: 400), curve: Curves.ease)
            .then((value) => pageExpand = PageExpandState.Expanded);
      } else {
        pageExpand = PageExpandState.Expanding;
        _pageScrollPosition
            .animateTo(_pageInitialOffset,
                duration: const Duration(milliseconds: 400), curve: Curves.ease)
            .then((value) => pageExpand = PageExpandState.NotExpand);
      }
    }
  }

会在ios模拟器中出现如上问题,但是在android真机中正常(这也是我今天在真机调试中发现的, 因为这是纯dart代码,所有我当时在模拟器中出现这个现象时就直接提issue了), 如果有需要,再将这个发上来