개발하는 두더지

Flutter - 무한 스크롤링 리스트뷰 만들기 본문

Flutter

Flutter - 무한 스크롤링 리스트뷰 만들기

덜지 2019. 4. 21. 23:53

사용자가 스크롤을 해서 무한대로 리스트를 보여주려면 ListView의 Builder 팩토리 생성자를 이용하면 됩니다. 상황에 따라서 목록의 뷰를 느리게 빌드할 수 있습니다.

_buildSuggestions 메서드가 리스트 뷰의 row를 그려주는 기능인데 itemBuilder는 리스트뷰에 row 하나가 추가될 때마다 호출되는 콜백을 포함하고 있습니다.

메서드가 실행되면 itemBuilder의 i 값이 0 일때 최초 호출되고 10개의 생성된 영어단어를 추가하게됩니다.
그리고 인덱스가 0인 row를 ListTile 위젯을 이용해 그리게 됩니다. row가 하나 추가되었으니 다시 itemBuilder 콜백이 호출되고
이후에는 인덱스가 홀수일때는 divider를 그리고 짝수일때는 2로 나눈 몫의 값을 이용해 index를 새로 구해서 새로운 row를 만들어나가는 방식으로 무한 스크롤링을 구현합니다.

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


void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      title: 'Startup Name Generator',
      theme: ThemeData(
        primaryColor: Colors.white,
      ),
      home: RandomWords(),
    );
  }
}

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

class RandomWordsState extends State<RandomWords> {

  final List<WordPair> _suggestions = <WordPair>[];
  final Set<WordPair> _saved = Set<WordPair>();
  final TextStyle _biggerFont = const TextStyle(fontSize: 18);

  @override
  Widget build(BuildContext context) {

    return Scaffold (
      appBar: AppBar(
        title: Text('Startup Name Generator'),
        actions: <Widget>[
          IconButton(icon: Icon(Icons.list), onPressed: _pushSaved),
        ],
      ),
      body: _buildSuggestions(),
    );
  }

  void _pushSaved() {
    Navigator.of(context).push(
      MaterialPageRoute<void>(
        builder: (BuildContext context) {
          final Iterable<ListTile> tiles = _saved.map(
                (WordPair pair) {
              return ListTile(
                title: Text(
                  pair.asPascalCase,
                  style: _biggerFont,
                ),
              );
            },
          );
          final List<Widget> divided = ListTile
              .divideTiles(
            context: context,
            tiles: tiles,
          )
              .toList();

          return Scaffold(
            appBar: AppBar(
              title: Text('Saved Suggestions'),
            ),
            body: ListView(children: divided),
          );
        },
      ),
    );
  }

  Widget _buildSuggestions() {
    return ListView.builder(
        padding: const EdgeInsets.all(16),
        itemBuilder: (BuildContext _context, int i) {
          if (i.isOdd) {
            return Divider();
          }

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

  Widget _buildRow(WordPair pair) {
    final bool alreadySaved = _saved.contains(pair);
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
      trailing: Icon(
        alreadySaved ? Icons.favorite : Icons.favorite_border,
        color: alreadySaved ? Colors.red : null,
      ),
      onTap: () {
        setState(() {
          if (alreadySaved) {
            _saved.remove(pair);
          } else {
            _saved.add(pair);
          }
        });
      },
    );
  }
}
Comments