[Flutter]인스타그램 클론 - 6. 검색화면 & 커스텀 그리드뷰 제작

한상욱·2023년 8월 2일
0
post-thumbnail

들어가며

이 프로젝트는 개발하는남자님의 유튜브 영상을 참고하여 제작하였습니다. 허나, 원본 영상에서 제작하는 방법과는 다를 수 있습니다.

검색 화면 개요

홈 화면은 마무리되었으니, 그 다음으로 검색화면을 만들어야겠죠. 검색화면에 대한 개요를 먼저 확인하겠습니다.

검색화면에는 다양한 사람들의 피드 혹은 릴스를 볼 수 있는 그리드뷰가 있고요. 상단에는 검색이 가능한 앱바가 있는데, 홈화면처럼 페이드 애니메이션이 있습니다. 검색필드를 누르면 검색화면을 할 수 있는 화면이 나오고, 검색어를 입력해서 완료버튼을 클릭하면 탭바뷰가 등장하죠.

일단 검색화면을 만들기 위해 검색화면의 뼈대를 만들어봅시다. search.dart파일을 만들게요. 해당 파일에는 홈화면처럼 CustomScrollView를 사용해야겠죠?

class Search extends StatelessWidget {
  const Search({super.key});

  
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: CustomScrollView(
          slivers: [
            // _appBar(),
            // _body(),
          ],
        ),
      ),
    );
  }
}

앱바와 그리드뷰 부분으로 나누어서 제작을 하면 편할 것 같습니다. 주석처리로 먼저 만들어줬어요. 이제, body()인 그리드뷰부터 출발하겠습니다.

검색화면 그리드뷰 개요

이번 포스팅에서 이야기할 주제는 검색화면 그리드 제작입니다. 인스타그램의 검색화면은 일반적인 그리드뷰랑은 살짝 달라요. 그래서 커스터마이징한 그리드뷰를 제작해보겠습니다.

검색화면의 그리드는 사이즈가 각각 달라요. 근데, 이 그리드는 일정한 규칙을 가지고 있습니다. 그러면 만들면서 설명을 드릴게요. 이 그리드를 쉽게 구현할 수 있는 패키지를 소개합니다 !

이 패키지를 이용해서 그리드뷰를 만들겁니다. 코드이 내용이 길어질 것 같아서, 해당 그리드뷰를 위젯으로 만들어주도록 할게요. search_grid_view.dart를 만들겠습니다. 이 위젯은 당연히 GridView를 반환해야 되는데, CustomScrollView 안에 Sliver로 들어가기 위해서는 SliverGrid를 이용해야 해요. 그 중에 builder타입을 사용하겠습니다.

class SearchGridView extends StatelessWidget {
  const SearchGridView({super.key});

  
  Widget build(BuildContext context) {
    return SliverGrid.builder(

    );
  }
}

자, 이제 본격적으로 만들어볼게요. 사실 저 패키지에서는 패턴을 지정할 수 있는 gridDelegate를 제공해줘요. 그것을 이용하면 우리가 원하는 커스터마이징된 그리드뷰를 만들 수 있어요.

class SearchGridView extends StatelessWidget {
  const SearchGridView({super.key});

  
  Widget build(BuildContext context) {
    return SliverGrid.builder(
      gridDelegate: SliverQuiltedGridDelegate(
      	//가로 세로 간격 1로 동일
        mainAxisSpacing: 1, 
        crossAxisSpacing: 1,
        //세줄의 그리드 뷰를 만듬
        crossAxisCount: 3,
        //패턴을 지정할 수 있는 속성, 반복 지정
        repeatPattern: QuiltedGridRepeatPattern.same,
        pattern: [
        // 패턴 입력 부분
        ],
      ),
      itemCount: 50,
      itemBuilder: (context, index) => Container(
        color: Colors.primaries[Random().nextInt(Colors.primaries.length)],
      ),
    );
  }
}

반복 패턴은 다양하게 있어요. 동일한 패턴을 무한반복하거나 다시 거꾸로 반복하는 옵션도 있으니, 해당 패키지를 잘 사용해보세요. 이제 패턴만 넣으면 우리가 원하는 그리드 뷰를 제작할 수 앴는 것이죠.

일단 패턴부터 분석을 하겠습니다.

가장 기본적으로 그리드뷰는 3줄로 이루어질겁니다. 해당 한칸의 크기를 1이라고 할때, 첫번째 패턴에서는 1칸짜리 그리드 4개가 등장하고 세로가 2인 그리드가 등장해요. 패턴 1이라고 하겠습니다.

그리고 다음으로는 먼저 세로가 2인 그리드가 등장하고 1칸짜리 그리드가 4개가 등장합니다. 패턴 2라고 하겠습니다.

세번째는 처음과 동일하게 1칸짜리 4개, 세로가 2인 그리드 1개에요. 그리고 나서 가로세로가 2인 그리드 하나와 1칸짜리 2개의 그리드가 등장합니다. 패턴 3이라고 할게요.

그리고 나서는 패턴 1 - 패턴 2 - 패턴 1 - 패턴 2가 반복되고 다시 이 패턴의 반복을 반복합니다.

정리하자면 한 사이클에서 패턴 1 - 패턴 2 - 패턴 1 - 패턴 3 - 패턴 1 - 패턴 2 - 패턴 1 - 패턴 2가 무한하게 순환하면서 반복합니다. 그리고 이 패턴의 반복을 다음과 같이 패키지를 이용해서 나타낼 수 있어요.

class SearchGridView extends StatelessWidget {
  const SearchGridView({super.key});

  
  Widget build(BuildContext context) {
  	//패턴 1
    final List<QuiltedGridTile> patternOne = [
      const QuiltedGridTile(1, 1),
      const QuiltedGridTile(1, 1),
      const QuiltedGridTile(2, 1),
      const QuiltedGridTile(1, 1),
      const QuiltedGridTile(1, 1),
    ];
	//패턴 2
    final List<QuiltedGridTile> patternTwo = [
      const QuiltedGridTile(2, 1),
      const QuiltedGridTile(1, 1),
      const QuiltedGridTile(1, 1),
      const QuiltedGridTile(1, 1),
      const QuiltedGridTile(1, 1),
    ];
	//패턴 3
    final List<QuiltedGridTile> patternThree = [
      const QuiltedGridTile(2, 2),
      const QuiltedGridTile(1, 1),
      const QuiltedGridTile(1, 1),
    ];
	
    return SliverGrid.builder(
      gridDelegate: SliverQuiltedGridDelegate(
        mainAxisSpacing: 1,
        crossAxisSpacing: 1,
        crossAxisCount: 3,
        repeatPattern: QuiltedGridRepeatPattern.same,
        pattern: [
          //패턴 1
          ...patternOne,
          //패턴 2
          ...patternTwo,
          //패턴 1
          ...patternOne,
          //패턴 3
          ...patternThree,
          //패턴 1
          ...patternOne,
          //패턴 2
          ...patternTwo,
          //패턴 1
          ...patternOne,
          //패턴 2
          ...patternTwo
        ],
      ),
      itemCount: 50,
      itemBuilder: (context, index) => Container(
        color: Colors.primaries[Random().nextInt(Colors.primaries.length)],

      ),
    );
  }
}

cascade연산을 이용해서 굉장히 간결하게 표현할 수 있습니다. 이제 결과좀 봅시다.

class Search extends StatelessWidget {
  const Search({super.key});

  
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: CustomScrollView(
          slivers: [
            _body(),
          ],
        ),
      ),
    );
  }
  //검색화면 그리드 뷰
  Widget _body() {
    return const SearchGridView();
  }
}

의도한대로 패턴이 잘 생성되었습니다 ! 이제 실제로는 게시물이 들어가니까 사진을 넣어볼게요.

class SearchGridView extends StatelessWidget {
  final Widget child; // 자식위젯 전달 받기
  const SearchGridView({super.key, required this.child});

  
  Widget build(BuildContext context) {
    final List<QuiltedGridTile> patternOne = [
      const QuiltedGridTile(1, 1),
      const QuiltedGridTile(1, 1),
      const QuiltedGridTile(2, 1),
      const QuiltedGridTile(1, 1),
      const QuiltedGridTile(1, 1),
    ];

    final List<QuiltedGridTile> patternTwo = [
      const QuiltedGridTile(2, 1),
      const QuiltedGridTile(1, 1),
      const QuiltedGridTile(1, 1),
      const QuiltedGridTile(1, 1),
      const QuiltedGridTile(1, 1),
    ];

    final List<QuiltedGridTile> patternThree = [
      const QuiltedGridTile(2, 2),
      const QuiltedGridTile(1, 1),
      const QuiltedGridTile(1, 1),
    ];

    return SliverGrid.builder(
      gridDelegate: SliverQuiltedGridDelegate(
        mainAxisSpacing: 1,
        crossAxisSpacing: 1,
        crossAxisCount: 3,
        repeatPattern: QuiltedGridRepeatPattern.same,
        pattern: [
          //패턴 1
          ...patternOne,
          //패턴 2
          ...patternTwo,
          //패턴 1
          ...patternOne,
          //패턴 3
          ...patternThree,
          //패턴 1
          ...patternOne,
          //패턴 2
          ...patternTwo,
          //패턴 1
          ...patternOne,
          //패턴 2
          ...patternTwo
        ],
      ),
      itemCount: 50,
      itemBuilder: (context, index) => Container(
        color: Colors.primaries[Random().nextInt(Colors.primaries.length)],
        child: child, //자식 위젯 전달
      ),
    );
  }
}
	...
  Widget _body() {
    return SearchGridView(
    	//이미지 전달
      child: CachedNetworkImage(
        imageUrl:
            'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS75ebrwvgVW5Ks_oLfCbG8Httf3_9g-Ynl_Q&usqp=CAU',
        fit: BoxFit.cover,
      ),
    );
  }

이제 완성입니다 !

profile
자기주도적, 지속 성장하는 모바일앱 개발자가 되기 위해

0개의 댓글

관련 채용 정보