원본영상(www.youtube.com/watch?v=EKwVNTyRPq0&list=PLwUg6hFuXV86arSYNF9x_5Vm_lKdIBpf9&index=54)
AsIs : Scaffold >> ListView >> ListTile >> setState >> 재시작
ToBe : Scaffold >> StreamBuilder >> ListView >> ListTile >> bloc 메소드 >> StreamBuilder
<신규> Bloc.dart
import 'dart:async'; // StreamController
import 'package:english_words/english_words.dart';
class Bloc {
Set<WordPair> saved = Set<WordPair>();
// StreamController 는 dispose 에서 close 해야 함.
final _saveController = StreamController<Set<WordPair>>.broadcast(); // 동시 수신
// Stream<Set<WordPair>> SavedStream() { // 아래 함수명으로 변경가능함
get savedStream {
return _saveController.stream;
}
// Stream 으로 데이터를 한번 보내주기, 수신하는곳은 모두 받음.
get addCurrentSaved => _saveController.sink.add(saved);
addToOrRemoveFromSavedList(WordPair item){
// 이미 Set 에 있다면 Set 에서 제거
if(saved.contains(item)){
saved.remove(item);
} else {
// 이미 Set 에 없다면 Set 에서 추가
saved.add(item);
}
// Stream 에 변경 사항을 전달
_saveController.sink.add(saved);
}
dispose() {
_saveController.close();
}
}
var bloc = Bloc();
<수정> randdom_list.dart
import 'package:bloc_stream/src/saved.dart';
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
import 'bloc/Bloc.dart'; // Bloc 패턴 추가
class RandomList extends StatefulWidget {
@override
_RandomListState createState() => _RandomListState();
}
class _RandomListState extends State<RandomList> {
final List<WordPair> _suggestions = <WordPair>[];
// final Set<WordPair> _saved = Set<WordPair>(); // Stream 으로 대체
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Naming App'),
actions: <Widget>[
IconButton(icon: Icon(Icons.list), onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SavedList()) // bloc 사용(saved: _saved 삭제)
).then((value) { // 여기선 이부분도 주석처리해도 무방함.
//저장화면에서 돌아오면 변경사항을 다시 그리기
// Future/async/await 처리 안해도 됨.
// 리턴시 처리 필요없음 return Future.value(true);
// setState(() {
// });
});
})],
),
body: _buildList(),
);
}
Widget _buildList() {
return StreamBuilder<Set<WordPair>>( // StreamBuilder 로 ListView.builder 감싸기
stream: bloc.savedStream, // Stream 연결
builder: (context, snapshot) {
return ListView.builder(itemBuilder: (context, index) {
// 0,2,4,6,8 is real items
// 1,3,5,7,9 is dividers
if (index.isOdd) {
return Divider();
}
// 몫을 구하는 방법, 파이썬은 // 2개로처리.
var realIndex = index ~/ 2;
if (realIndex >= _suggestions.length) {
// 화면에 표시할 내용이 단어 개수보다 많으면(같거나) 단어를 10 추가 생성할것.
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(snapshot.data, _suggestions[realIndex]);
// return _buildRow(_suggestions[realIndex]);
});
}
);
}
// Widget _buildRow(WordPair pair) {
Widget _buildRow(Set<WordPair> saved, WordPair pair) {
// 해당 단어(Set<WordPair> saved)가 저장되어 있는지 확인하고 아이콘 결정
// 글로벌 변수대신(_saved) 인자로 넘어오는(saved) 값 사용
// final bool alreadySaved = saved.contains(pair); // _saved => saved
final bool alreadySaved = saved==null ? false : saved.contains(pair);// _saved => saved
return ListTile(
title: Text(
pair.asPascalCase,
textScaleFactor: 1.5,
),
trailing: Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color: Colors.pinkAccent,
),
onTap: () {
// setState 전체 대신 addToOrRemoveFromSavedList() 함수로 처리.
bloc.addToOrRemoveFromSavedList(pair);
print(saved.toString()); // 저장된 리스트 화면 출력
// setState(() {
// if (alreadySaved)
// _saved.remove(pair);
// else
// _saved.add(pair);
// print(_saved.toString());
// });
},
);
}
}
<수정> saved.dart
import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'bloc/Bloc.dart';
class SavedList extends StatefulWidget {
// 'saved' 리스트는 레퍼런스로 가져오기 때문에 여기서 삭제 가능
// final Set<WordPair> saved; // bloc 패턴으로 대체
// SavedList({@required this.saved}); // bloc 패턴으로 대체
@override
_SavedListState createState() => _SavedListState();
}
class _SavedListState extends State<SavedList> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Saved'),
),
body: _buildList(),
);
}
Widget _buildList() {
return StreamBuilder<Set<WordPair>>( // StreamBuilder 로 ListView.builder 감싸기
stream: bloc.savedStream, // Stream 연결
builder: (context, snapshot) {
// 데이터가 없으면 가져오기 시도
if(!snapshot.hasData) {
bloc.addCurrentSaved;
return Center(child: CircularProgressIndicator());
}
// var saved = snapshot.data ; // 아래에서 snapshot 을 직접사용함
return ListView.builder(
itemCount: snapshot.data.length * 2,
itemBuilder: (context, index) {
if (index.isOdd) return Divider();
var realIndex = index ~/ 2;
return _buildRow(snapshot.data.toList()[realIndex]);
});
}
);
}
Widget _buildRow(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
textScaleFactor: 1.5,
),
onTap: () {
// setState 전체 대신 addToOrRemoveFromSavedList() 함수로 처리.
bloc.addToOrRemoveFromSavedList(pair);
print(pair.toString()); // 제거된 단어(클릭된 단어) 출력
// setState(() {
// // 'saved' 리스트는 레퍼런스로 가져오기 때문에 여기서 삭제 가능
// widget.saved.remove(pair);
// });
},
);
}
}
'Flutter > 00 Legacy' 카테고리의 다른 글
[Flutter] Bloc, Stream - setState 을 Provider 로 변경 (0) | 2021.05.04 |
---|---|
[Flutter] Provider with Flutter sample - ChangeNotifierProvider (0) | 2021.05.04 |
[Flutter] Bloc, Stream - setState 로 구현 (0) | 2021.05.03 |
[Flutter] StreamBuilder with FirebaseFirestore (0) | 2021.02.03 |
[Flutter] CRUD with FirebaseFirestore & FirebaseStorage (2) | 2021.02.03 |