[Day26]Flutter Netflix UI ListView中第一个可见的Item显示,其他都变暗

大家好,今天要做的是捕捉ListView里面显示在萤幕上的是哪一个Item,今天会用到NotificationListener、HitTestResult、MetaData、ShaderMask

先为ListView添加监听的NotificationListener
onNotification会传出很多滚动的讯息,例如在列表的ScrollStartNotification开始滚动、ScrollUpdateNotification滚动更新、ScrollEndNotification滚动结束的状态等

 NotificationListener(
        onNotification: (noti) {
          return true;
        },
        child: ListView.builder(
          itemCount: 10,
          itemBuilder: (BuildContext context, index) {
            return _buildItem(index);
          },
        ),
      ),

要找到在列表的什麽位置是简单的事情,因为可以透过ScrollController.offset取得,但是单纯位置不足以判断现在显示的哪一个Widget。除非知道每一个Item的高度,去算现在是第几个。不过,今天我使用的方法是用MetaData。

MetaData

元数据,用MetaData可以把一些讯息放在渲染树中,让用户交互的时候可以提供讯息
HitTestBehavior是一个枚举类,具体请参阅这里

 _buildItem(int index) {
    return MetaData(
      metaData: index, //这边希望放入的是index资讯
      behavior: HitTestBehavior.translucent,
      child: _buildComingSoonItem(index),
    );
  }
 T getMeta<T>(double x, double y) {
     //这两行主要目的是找出一个Offset,要取得讯息的位置
    var renderBox = context.findRenderObject() as RenderBox;
    var offset = renderBox.localToGlobal(Offset(x, y)); 
        
    //这两行是对这个位置做点击测试
    HitTestResult result = HitTestResult();
    WidgetsBinding.instance.hitTest(result, offset);

    //从测试结果取出metaData讯息
    for (var i in result.path) {
      if (i.target is RenderMetaData) {
        var d = i.target as RenderMetaData;
        if (d.metaData is T) {
          return d.metaData as T;
        }
      }
    }
    return null;
  }

onNotification里面调用getMeta,取得讯息就可以对其他UI做控制了
Future.microtask

 onNotification: (noti) {
          Future.microtask(() {
            var info = getMeta(0, MediaQuery.of(context).size.height * .5);
            print("scrolling to ${info}");
            if (info != null) {
              setState(() {
                visibleIndex = info;
              });
            }
          });
          if (noti is ScrollEndNotification) {
            print('开始播放');
          }
          return true;
        },

ShaderMask

直接将原本的每个Item包起来,可以透过LinearGradient快速定义shader

 LinearGradient(colors: [_color, _color]).createShader(rect)
 _buildComingSoonItem(int index) {
     //判断是否是中间正在显示的Widget,来使用遮罩
    Color _color = Colors.white.withOpacity(visibleIndex != index ? 0.4 : 1.0);
    return ShaderMask(
      shaderCallback: (rect) {
        return LinearGradient(colors: [_color, _color]).createShader(rect);
      },
      child: Padding(...),
    );
  }

今日完成的效果,可以看到除了正中间的正常显示,其他的都是被遮住的
Day26


<<:  Day 26:ELK stack for observation system

>>:  Day 26 排程管理

#10 实作篇 — 使用 SWR 抓取和 Cache 资料

嗨大家!昨天跟大家分享一个 library 叫做 SWR,文章在这里~ 今天用 SWR 新增了小功能...

Best Outlook PST Splitter Tool to divide Outlook PST files

Sometimes PST files exceed the storage limit, whic...

给自己学习30天重新认识css

哈罗大家好,我是黄奇昌 我就读岭东科技大学视觉传达设计系 今年是第一次参加13th铁人赛,想跟大家好...

Day 1 Introduction

前情提要 我是 siriuskoan,在这三十天内会把一些关於 flask 的知识写成文章,以供自己...

全端入门Day11_全端之IDE环境尾篇

昨天介绍了什麽是IDE,今天就要介绍我常用的IDE了 IDE大比较 1. Visual Studio...