【问题标题】:How to track the top Item in the viewport for a list flutter如何跟踪视口中的顶部项目以进行列表颤动
【发布时间】:2021-05-21 11:05:46
【问题描述】:

所以我的脑袋想不出一种方法来设计这个,我有可滚动的无限列表,列表的每个项目都包含一些信息,一个是日期。

所以我想跟踪当前顶部索引项以更新我们所在的部分日期的另一个小部件,有点像某些消息传递应用程序,例如:像“信号”。

我尝试了什么

我以为取整个滚动高度并划分项目编号,但这是错误的,因为项目是可扩展的,所以结果很愚蠢。

有人可以分享一下吗

我正在使用看起来像这样的 CustomScrollView

CustomScrollView(
      slivers: [
        SliverToBoxAdapter(
          child: Container(
             height: 100,
             color: Colors.blue
            ),
        ),
        SliverToBoxAdapter(
          child: Container(
             height: 100,
             color: Colors.blue
            ),
        ),
         SliverPersistentHeader(
          delegate: PersistentHeader(
            widget: Container()
           ),
           pinned: true,
           ),

// I am trying to trace items in this section
         SliverList(
           delegate: SliverChildBuilderDelegate((context, index) {
             return ListTile(title: Text('bron $index'),);
           }))
        
      ],
    )

【问题讨论】:

    标签: flutter dart flutter-layout


    【解决方案1】:

    简而言之,你可以通过在ScrollController中添加Listener来实现。

    A.按滚动偏移量计算

    在此解决方案中,您需要知道列表中的第一项偏移量和每个项的高度。

    final double initialOffset = 300;
    final double itemHeight = 50;
    

    那么你可以在每次滚动ScrollController时改变索引:

    ...
    
    final ScrollController _scrollController = ScrollController();
    
    ...
    @override
    Widget build(BuildContext context) {
      final index = ValueNotifier<int>(-1);
    
      _scrollController.addListener(() {
        index.value = ((_scrollController.offset-initialOffset)/itemHeight).round()-1;
      });
    
      return CustomScrollView(
        controller: _scrollController,
        slivers: [
          SliverToBoxAdapter(
            child: Container(height: 100, color: Colors.blue),
           ),
           SliverToBoxAdapter(
             child: Container(height: 100, color: Colors.blue),
           ),
           ValueListenableBuilder(
             valueListenable: index, 
             builder: (_,value,__){
               return SliverPersistentHeader(
                 pinned: true,
                 delegate: PersistentHeader(value),
               );
             },
           ),
           SliverList(
             delegate: SliverChildBuilderDelegate(
             ...
    

    B.计算孩子在屏幕上的位置

    这要复杂得多。

    它跟踪屏幕内列表中的所有子项,并从屏幕偏移量返回索引:

    bool inTopArea(double dy) => dy <= 60 && dy >=0;
    

    另外,它需要全局键来跟踪所有孩子并获取全局位置(来自here

    extension GlobalKeyExtension on GlobalKey {
      Rect get globalPaintBounds {
        final renderObject = currentContext?.findRenderObject();
        var translation = renderObject?.getTransformTo(null)?.getTranslation();
        if (translation != null && renderObject.paintBounds != null) {
          return renderObject.paintBounds
            .shift(Offset(translation.x, translation.y));
        } else {
          return null;
        }
      }
    }
    
    final keys = <GlobalKey>[];
    

    因为你使用builder来生成列表,所以child必须是一个有状态的widget,包含dispose方法,代表widget在屏幕外:

      ...
    
      SliverList(
        delegate: SliverChildBuilderDelegate(
          (context, index) {
            final uniqueKey = GlobalKey();
            keys.add(uniqueKey);
            return MyChildWidget(
              index: index,
              dispose: (){keys.remove(uniqueKey);},
              key: uniqueKey,
            );
          },
        ),
      ),
    
      ...
    
    class MyChildWidget extends StatefulWidget {
      const MyChildWidget({this.index,this.dispose,Key key}):super(key: key);
    
      final int index;
      final VoidCallback dispose;
    
      @override
      _MyChildWidgetState createState() => _MyChildWidgetState();
    }
    
    class _MyChildWidgetState extends State<MyChildWidget> {
      @override
      Widget build(BuildContext context) {
        return ListTile(
          title: Text('bron ${widget.index}'),
        );
      }
      @override
      void dispose() {
        widget.dispose();
        super.dispose();
      }
    }
    

    最后,滚动时每个子dy位置的计算:

    _scrollController.addListener(() {
      final tops = keys.where((aKey) {
        return inTopArea(aKey.globalPaintBounds.topLeft.dy);        
      });
      if(tops.isNotEmpty){
        index.value = (tops.first.currentState as _MyChildWidgetState).widget.index;
      }else{
        index.value = -1;
      }
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-12
      • 2022-01-10
      • 2014-04-22
      • 2021-02-23
      • 2021-07-07
      • 1970-01-01
      相关资源
      最近更新 更多