Flutter - 완전 기초 ②: 무한 스크롤 ListView 화면 만들기

2 분 소요


플러터 코드랩을 이어서 무한 스크롤 ListView를 만들어보자


Ex) 무한 스크롤 ListView

이제 ListView를 생성해서, 스크롤하면 무한하게 계속 랜덤 단어 짝을 만드는 화면을 생성해 보자

State 수정

class RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];
  final _biggerFont = const TextStyle(fontSize: 18.0);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Startup Name Generator'),
      ),
      body: _buildSuggestions(),
    );
  }
}

final 변수로 단어 짝을 담을 _suggestions 배열과, TextStyle인 _biggerFont를 추가한다.
build()에서 원래는 그냥 Text()를 반환했었는데, 이번에는 Scaffold 자체를 return 해주기로 한다.

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Temp',
      home: RandomWords(),
    );
  }
}

따라서 MyApp 부분도 이렇게 home 자체를 RandomWords() 스테이트로 대체한다.


그럼 이제 RandomWordsState의 body가 될 _buildSuggestions()를 보자. 당연하지만 RandomWordsState 클래스 안에 정의한다

  Widget _buildSuggestions() {
    return ListView.builder(
        padding: const EdgeInsets.all(16.0),
        itemBuilder: (context, i) {
          if (i.isOdd) return Divider();

          final index = i ~/ 2;
          if (index >= _suggestions.length) {
            _suggestions.addAll(generateWordPairs().take(10));
          }
          return _buildRow(_suggestions[index]);
        });
  }

ListView.builder()로 ListView를 생성해서 return한다.

padding: EdgeInsets.all()을 썼는데, 전체 여백을 지정하는 옵션이다. 상하좌우에 16.0의 여백을 준다. 만약 특정 부분만 하려면 EdgeInsets.only(left: 10.0)과 같이 사용한다. 다른 것도 많이 있음

itemBuilder: 아이템이 화면에 보여질 때 생성해낸다. 지금처럼 무한하게 만들거나 해서 아이템들을 미리 만들어 놓을 수 없을 때 쓰면 좋다.

+ itemCount로 아이템 개수를 정할 수 있는데, 무한하게 늘어나게 할 것이므로 여기서는 사용하지 않는다.


.isOdd로 홀수인지를 아는가 보다. 희한하넹
홀수일 때마다 Divider()를 호출하는데, 이건 구분선으로, 각 항목을 구분하는 역할이다.

홀수마다 구분선이 있으므로, 0, 2, 4, …, 2n 번째마다 아이템이 들어가게 될 거다. 따라서 실제 아이템 index는 i ~/ 2가 된다.

_suggestions은 단어 짝 배열이다. index가 배열 크기보다 같거나 크면, 저장된 단어 짝을 다 썼다는 뜻이므로 10개를 더 생성해서 addAll()_suggestions에 추가해준다.

마지막으로, _buildRow()로 타일을 return 한다.

  Widget _buildRow(WordPair pair) {
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }

_buildRow()는 그냥 ListTile을 return하는 간단한 함수다.


전체 코드

import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Temp',
      home: RandomWords(),
    );
  }
}

class RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];
  final _biggerFont = const TextStyle(fontSize: 18.0);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Startup Name Generator'),
      ),
      body: _buildSuggestions(),
    );
  }

  Widget _buildSuggestions() {
    return ListView.builder(
        padding: const EdgeInsets.all(16.0),
        itemBuilder: (context, i) {
          if (i.isOdd) return Divider();

          final index = i ~/ 2;
          if (index >= _suggestions.length) {
            _suggestions.addAll(generateWordPairs().take(10));
          }
          return _buildRow(_suggestions[index]);
        });
  }

  Widget _buildRow(WordPair pair) {
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }
}

class RandomWords extends StatefulWidget {
  @override
  RandomWordsState createState() => RandomWordsState();
}


실행 화면

4
잘 되는구나


Ex) ListView 구성 바꾸기

그럼 이제 조금 조작해 보자
리스트에 헤더도 달아줄 수도 있고, 10번째마다 다른 타일을 넣고 싶을 수도 있지

  Widget _buildSuggestions() {
    return ListView.builder(
        padding: const EdgeInsets.all(16.0),
        itemBuilder: (context, i) {
          if( i == 0 ) return HeaderTile();
          if( i % 2 == 1 ) return Divider();
          final index = i ~/ 2;
          if( index % 10 == 0 ) return TenthTile();
          if( index >= _suggestions.length ){
            _suggestions.addAll(generateWordPairs().take(10));
          }
          return _buildRow(_suggestions[index]);
        });
  }

_buildSuggestions() 함수에서, i가 0일 때 HeaderTile() 클래스 인스턴스를 리턴해서 헤더를 추가했고, index가 10번째일 때마다 TenthTile()을 리턴하게 추가했다.

class TenthTile extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Image.network("https://..."),
    );
  }
}

Image.network("URL")로 인터넷의 이미지 주소를 통해 불러올 수 있다. 이미지를 Container에 담아 return했다.


실행 화면

5
첫 헤더와 10번째 타일들이 잘 나온다. 이런 식으로 배리에이션을 주면 될 것 같다.



태그:

카테고리:

업데이트:

댓글남기기