Getting started
해당 링크까지는 완료된 상태에서 이번 연습을 해보았다.
stateless widget은 변경할 수 없다. (all values are final)
stateful widget은 변경될 수 있는 상태를 유지한다.
stateful widget을 구현하는데 적어도 2개의 class가 필요하다.
- a StatefulWidget class (state class의 instance를 만든다.)
- a State class
lib/main.dart의 코드를 수정해준다.
해당 프로젝트에서는 english_words package가 필요하다.
튜토리얼에서 소개하는 순서대로 진행한다.
RandomWords (stateful widget)과 _RandomWordsState(state widget) 를 추가한다. (RandomWords가 state class인 _RandwomWords class를 만든다.)
RandomWords class는 MyApp(stateless) 내에서 child로서 사용할 수 있다.
class RandomWords extends StatefulWidget {
const RandomWords({ Key? key }) : super(key: key);
@override
_RandomWordsState createState() => _RandomWordsState();
}
class _RandomWordsState extends State<RandomWords> {
@override
Widget build(BuildContext context) {
final wordPair = WordPair.random();
return Text(wordPair.asPascalCase);
}
}
//build 함수 업데이트 하였다.
class RandomWords extends StatefulWidget {
const RandomWords({Key? key}) : super(key: key);
@override
State<RandomWords> createState() => _RandomWordsState();
}
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: const Text('Startup Name Generator'),
),
body: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: /*1*/ (context, i) {
if (i.isOdd) return const Divider(); /*2*/
final index = i ~/ 2; /*3*/
if (i >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10)); /*4*/
}
return ListTile(
title: Text(
_suggestions[i].asPascalCase,
style: _biggerFont,
),
);
},
),
);
}
}
나는 inifinite item을 위해 ListView.builder를 사용한다고 이해했다.
/1/
itemBuilder는 callback함수이다. (ListView나 flutter framework는 무언가 보여줄 것이 있을 때마다 itemBuilder를 호출한다.- callback 함수가 실행되는 시점)
i는 wordPairing에 대해 2번 증가한다. (한번은 ListTile, 또 다른 한번은 Divider에 대해)
/2/
/3/
/4/
아이콘 추가하고, interactivity 추가, 새로운 페이지 (플러터에서는 route라고 부른다) 와 기존 페이지를 연결하기를 구현해본다.
final _saved = <WordPair>{}; // NEW
final alreadySaved = _saved.contains(pair); // NEW
trailing: Icon( // NEW from here...
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: alreadySaved ? Colors.red : null,
semanticLabel: alreadySaved ? 'Remove from saved' : 'Save',
),
아직 interactivity는 추가되지 않았다.
이 단계에서는 하트 아이콘을 탭으로 만든다. 사용자가 목록의 엔트리를 탭하여 원하는 상태로 전환하면 해당 word pairing이 _saved set에 추가 또는 삭제된다.
onTap: () { // NEW lines from here...
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
},
새로운 페이지를 추가한다. (flutter에서는 route라고 부른다.)
route간에 이동함을 연습해본다.
Navigator는 앱의 route를 포함하는 스택을 관리한다.
route를 네비게이터 스택에 푸시하면 해당 route로 표시가 업데이트된다.
Navigator stack에서 pop하면 이전 route로 돌아간다.
void _pushSaved() {
Navigator.of(context).push(
// Add lines from here...
MaterialPageRoute<void>(
builder: (context) {
final tiles = _saved.map(
(pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
);
},
);
final divided = tiles.isNotEmpty
? ListTile.divideTiles(
context: context,
tiles: tiles,
).toList()
: <Widget>[];
return Scaffold(
appBar: AppBar(
title: const Text('Saved Suggestions'),
),
body: ListView(children: divided),
);
},
), // ...to here.
);
}
Navigator.of(context).push{
MaterialPageRoute<void>(
...
)
}
의 코드를 통해 Navigator에 새로운 route를 push 한다. (예제에서 새로운 route는 하트가 눌려진 이름들을 보여주는 페이지이다.)
새 페이지의 내용은 MaterialPageRoute의 빌더 속성에 익명 함수로 작성된다.
tiles는 ListTile을 리턴하며, _saved 내의 pair를 나타낸다.
divided는 tiles가 비어있다면 빈칸을, 비어있지 않다면 구분선을 나타내는 것으로 이해했다.