게시물을 검색하기 위한 Search-bar를 구현해보았다.
검색하고 싶은 값은 TextField 위젯을 사용했는데, 보통 컨트롤러로 입력 받은 값을 사용하지만, 나는 실시간으로 입력되는 글을 바로 바로
서버로 보내는 것을 하고 싶어서 onChanged() 를 사용했다.
TextField(
focusNode: _focus,
keyboardType: TextInputType.text,
onChanged: (text){
_streamSearch.add(text);
},
decoration: InputDecoration(
hintText: lang(0),
border: InputBorder.none,
icon: Padding(
padding: EdgeInsets.only(left: 13),
child: Icon(Icons.search))),
),),
입력된 값은 _streamSeach 라는 미리 만들어둔 스트림 통로를 타고 String값이 이동하는데, 이 통로와 연결되어 있는
StreamBuilder가 통로에서 값을 받고 Node.js 서버에 전달한다.
StreamBuilder(
stream: _streamSearch.stream,
builder: (context,snapshot){
if(snapshot.hasData)
return FutureBuilder(
future: _fetchSearch(snapshot.data),
builder: (context,snaps){});
}
)
Node.js 에서는 아래와 같이 값을 전달받아 데이터베이스에 쿼리를 날려 검색을 하도록 하였다.
//--------------------------- 검색
app.get('/search/:name',(req,res)=> {
let sql = 'SELECT * FROM POSTS WHERE title REGEXP ?';
let params =[req.params.name];
connection.query(
sql,params,
(err, rows, fields) => {
res.send(rows);
}
);
});
만약 검색으로 '일'을 검색하였다면 돌아온 응답 값은 아래와 같다.
[{"id":15,"title":"일본이 전자 산업 분야에서 내수시장을 방어하는 방법","content":"일반 재단법인 일본 원자력 문화 재단에서 인용\n일본 전기의 주파수는 시즈오카 현의 후지 천 당을 경계로 동쪽은 50Hz, 서일본이 60Hz와 다릅니다.","date":"2020-03-10T15:00:00.000Z","views":99,"good":30,"bad":60,"pic_thumb":"/image/1f71317100b29d5250f418db15a7c700","tag_0":"일본","tag_1":"전압","tag_2":"시즈오카","tag_3":null,"tag_4":null},
{"id":23,"title":"일류를 꿈꾸는 삼류 조폭의 이야기","content":"송강호란 배우의 이름을 확실히 사람들에게 각인시켜준 작품이었음. 이후로 송강호는 한국을 대표하는 배우로 성장했지","date":"2020-03-11T02:24:54.000Z","views":54,"good":30,"bad":21,"pic_thumb":"/image/36127119c67709878706cdae88ef15e4","tag_0":"영화","tag_1":"송강호","tag_2":"기생충","tag_3":"한국인","tag_4":null},
{"id":25,"title":"핵무기가 만들어지면 벌어지는 일들","content":"첫번째 이유는 우방국의 핵우산, 두번째는 박정희 정부때 찌라씨로 만들고 있었다가 완성 직전에 들켜서 npt에 가입한 걸로 알고 있음","date":"2020-03-10T15:00:00.000Z","views":75,"good":30,"bad":15,"pic_thumb":"/image/fcd451dba2f33e30da8133428448a36e","tag_0":"한국","tag_1":"핵무기","tag_2":"박정희","tag_3":"대한민국","tag_4":null},
{"id":36,"title":"80년대 일본을 느껴보자 감성 시티팝 플레이","content":"버블 경제 당시 최고의 음향장비와 굉장한 녹음시설 그리고 최고의 인력들이 다시는 만들지 못할 음악을 만들어냈다","date":"2020-03-11T05:43:39.000Z","views":0,"good":0,"bad":0,"pic_thumb":null,"tag_0":null,"tag_1":null,"tag_2":null,"tag_3":null,"tag_4":null}]
포스트의 '일'이라는 문자가 포함되어 있기 때문이다.
위에서 받아온 값은 JSON형식이기 때문에, 클라이언트단에서 받아 각 필드들을 읽고 맵핑하여 구조체 안에 넣는 작업을 했다.
class Post {
final int postID;
final String title;
final String content;
final String date;
final String thumb;
final int views;
final int good;
final int bad;
Post(
{this.postID,
this.title,
this.content,
this.date,
this.thumb,
this.views,
this.good,
this.bad});
factory Post.fromJSON(Map<String, dynamic> json) {
return Post(
postID: json['id'],
title: json['title'],
content: json['content'],
date: json['date'],
thumb: json['pic_thumb'],
views: json['views'],
good: json['good'],
bad: json['bad'],
);
}
}
비동기식으로 이루어지는 것임으로 Future 로 캡슐화를 시켜두었다.
서버로부터 받은 값을 response 에 담아두고 위에서 설명한 맵핑 과정을 거친다.
Future<List<Post>> _fetchSearch(String name) async {
List<Post> _posts = [];
final response = await http.get('http://127.0.0.1/search/'+name);
final List<Post> parsedResponse = jsonDecode(response.body)
.map<Post>((json) => Post.fromJSON(json))
.toList();
_posts.clear();
_posts.addAll(parsedResponse);
return _posts;
}
위의 Future 식은 FutureBuilder와 연결하여 데이터를 다 받아왔을 경우 열려서 위젯을 return 하도록 설계하였다.
네트워크 기반의 어플은 서버로부터 데이터를 주고 받는 작업이 필수이다.
플러터를 하면서 http와의 통신에 대해 공부를 많이 하고 있는데 그중에 스트림(Stream)과
비동기(Async) 프로그래밍의 매력을 크게 느끼고 있다.
이것을 잘 정리해 두어 후에 다른 앱을 개발할 때 두고 두고 응용해야겠다
_끗
'프로그래밍 > Flutter' 카테고리의 다른 글
Shared Preferences (1) | 2020.04.28 |
---|---|
Cupertino ActionSheet (0) | 2020.04.05 |
image shake animation (0) | 2020.04.04 |
flutter_staggered_grid_view (0) | 2020.03.15 |
플러터 - 크로스 플랫폼 (1) | 2020.03.11 |