iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 26
1
Mobile Development

新手試試用Flutter做Netflix UI系列 第 26

[Day26]Flutter Netflix UI ListView中第一個可見的Item顯示,其他都變暗

  • 分享至 

  • xImage
  •  

大家好,今天要做的是捕捉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

GitHub連結: flutter-netflix-clone


上一篇
[Day25] Flutter Netflix UI Internationalization國際化
下一篇
[Day27]Flutter Netflix UI 使用json_serializable轉換Model
系列文
新手試試用Flutter做Netflix UI30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言