Never give up

Flutter - how to get item index in Listview 본문

Flutter

Flutter - how to get item index in Listview

대기만성 개발자 2020. 9. 30. 18:55
반응형

ListView를 다룰 때 List의 index에 따라 위젯을 변경 시켜주거나

 

아이템을 눌렀을 때 해당 index로 이동해야되는 위젯이 있을때

 

처리를 어떻게 해야될지 알아보겠습니다

 

먼저 사용한 패키지는 다음과 같습니다

 

pub.dev/packages/scrollable_positioned_list

 

class _UIExampleState extends State<UIExample> {
  List<String> _list = List.generate(20, (index) => 'Item - $index');
  StreamController<int> _streamController;
  ItemScrollController _itemScrollController = ItemScrollController();
  ItemPositionsListener _itemPositionsListener = ItemPositionsListener.create();
  int temp, itemIndex;

  @override
  void initState() {
    super.initState();
    _streamController = StreamController<int>()..add(1);

    _itemPositionsListener.itemPositions.addListener(listener);
  }

  void listener() {
    ItemPosition position = _itemPositionsListener.itemPositions.value
        .firstWhere((element) => element.itemLeadingEdge <= 0);
    itemIndex = position.index;

    if (temp != itemIndex) {
      _streamController.add(itemIndex + 1);
      temp = itemIndex;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomPadding: false,
      appBar: AppBar(title: Text('UI Example')),
      body: Stack(
        children: [
          Container(
              margin: EdgeInsets.only(left: 100, top: 200, bottom: 390),
              width: 100,
              color: Colors.green),
          Container(
            margin: EdgeInsets.only(top: 200),
            height: 100,
            child: Listener(
              onPointerUp: (value) {
                _itemScrollController.scrollTo(
                    index: itemIndex,
                    duration: Duration(milliseconds: 500),
                    curve: Curves.easeInOut);
              },
              child: ScrollablePositionedList.builder(
                  itemScrollController: _itemScrollController,
                  itemPositionsListener: _itemPositionsListener,
                  scrollDirection: Axis.horizontal,
                  itemCount: _list.length,
                  itemBuilder: (context, index) {
                    return Container(
                        margin: EdgeInsets.all(4),
                        color: Colors.grey,
                        height: 80,
                        width: 92,
                        child: Center(
                          child: ListTile(
                              title: Text(
                                _list[index],
                                style: TextStyle(color: Colors.white),
                                textAlign: TextAlign.center,
                              ),
                              onTap: () => _itemScrollController.scrollTo(
                                  index: index - 1,
                                  duration: Duration(milliseconds: 500),
                                  curve: Curves.easeInOutSine)),
                        ));
                  }),
            ),
          ),
          SizedBox(height: 20),
          Align(
              child: StreamBuilder<int>(
                  stream: _streamController.stream,
                  builder: (context, snapshot) {
                    return Text('Current item - ${snapshot.data}');
                  }))
        ],
      ),
    );
  }
}

 

먼저 ItemScrollController와 ItemPositionsListener를 ScrollablePositionedList에 정의를 해주면

 

ListView의 스크롤이 이동함에 따른 위치 정보를 받아올 수 있습니다

 

controller는 ListTile의 onTap event와 Listener에서 onPointerUp event에 따라

 

scrollTo 메소드를 이용해서 아이템의 index에 해당하는 위치로 이동하게 됩니다

 

여기서 onPointerUp을 이용한 이유는 드래그가 끝나는 시점에서

 

한 index와 다른 index의 사이에 있을때 어떤 위젯을 가리키고 있는지 확실하지 않게 되는데

 

이 부분에서 itemIndex값이 가리키는곳, 즉 Listener가 가리키는 위치로

 

자동으로 이동하도록 만들어 놓기 위해 사용했습니다

 

그리고 listener는 이 예제에서 필요한 아이템의 index값과

 

위치값 등을 계산 및 처리를 하기위해 사용하는데

 

필자가 사용한 조건은

ItemPosition position = _itemPositionsListener.itemPositions.value
        .firstWhere((element) => element.itemLeadingEdge <= 0);

으로 화면에 보이는 첫번째 List item의 위치값이 0이될 때를 확인하기 위함인데

 

예를들어 화면에 2 3 4라는 위젯이 있으면 2번이 출력되게 하기위해 사용했습니다

(다른 형태의 UI를 만들때에는 조건의 변화가 필요합니다)

 

그리고 temp값과 itemIdex값을 비교하여 값이 변경될때만 StreamController를 통해

 

위젯의 값을 바꿔주는데 이 부분은 현재 가벼운 UI를 변화시키기 때문에 setState를 써도 문제없으나

 

조금 더 많은 UI를 다룰때에는 꼭 필요하기 때문에 일단 넣어봤습니다

(해당 예제에서는 굳이.. 그래도 좋은 습관을 들이기 위해..)

 

(참고로 필자가 만든 페르소나4 UI는 이런 방법보다 훨씬 더 간단한 방법으로 만들었습니다)

반응형
Comments