< 토스 클론코딩 > 플러터로 검색창 구현하기
토스 검색창 화면
위와 같은 토스 어플의 검색창 화면을 구현하려고 한다 !
구현 중 겪은 문제점
먼저 위의 토스 어플을 그려서 골격과 구현되어야 할 기능을 체크했다.
- TextField 위젯은 앱바에 구현되는 것인가 / Body에 구현되는 것인가 ?
- TextField 위젯 밑의 인기 검색 리스트에 있는 값들을 클릭하면 어떻게 자동으로 관련 페이지로 넘어가게 할 것인가?
- 터치 시에 키보드는 어떻게 Pop up하게 만들 것인가 ?
- 키보드 외 화면을 터치 시 어떻게 키보드를 사라지게 할 것인가 ?
- 검색 창에 값을 입력시 자동 완성 기능은 어떻게 구현할 것인가 ?
이렇게 큰 5가지 문제가 있었다. 제일 고난도가 2, 5번인 것 같았다. 해결하기 위해 구글링 고고 !
구글링으로 구현할 수 있는 방법 찾기
구글링 결과 1. TextField 위젯 2. search page 패키지 사용 3. SearchDelegate 클래스 구현 이렇게 세가지 정도를 발견했다.
1. TextField 위젯으로 구현(실패)
1번을 해결하는 과정을 거쳤다. TextField를 앱바에 넣게 되면 저렇게 앱바의 좌측 상단 부분과 TextField
부분의 간격이 벌어지는 것을
볼 수 있는데 따라서 Body property
로 옮기게 되었다.
스캐폴드의 바디로 구현할 시 위와 같이 우리가 원하던 TextField
의 형태를 띄게 되었다 !!
그리고 문제점 3번과 4번을 다음과 같이 해결할 수 있다.
TextField(
onTap: () => focusNode.requestFocus(),
focusNode: focusNode,
autofocus: true,
3번: TextField property인 autofocus
의 값을 true
로 바꾸기
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusManager.instance.primaryFocus?.unfocus();
},
child: MaterialApp();
4번: 최상위 위젯인 MaterialApp 위젯을 GestureDectector
위젯으로 감싸고 onTap
프로퍼티에 FocusManager.instance.primaryFocus?.unfocus();
사용하기
하지만 제일 중요한 2번과 5번을 구현하지 못해서 다음 방법을 사용하기로 하였다.
2. Search Page 패키지 사용하기
다음은 이 패키지를 사용해서 구현한 화면이다.
가장 어려웠던 점은 패키지 코드를 분석하는 일이었다 ㅠㅠ
위 그림과 같이 main.dart
에서 돋보기 아이콘을 눌러서 검색창 화면으로 이동시키려면 아래 코드와 같은 구조를 가져야 한다.
Scaffold(
appbar: AppBar(
actions: [
SearchButton(),
]
),
);
SearchButton()
의 코드는 아래와 같다. 겁먹지 마요 ! 밑에 사진과 함께 쉽게 이해할 수 있을 거에요 ㅎㅎ
IconButton(
onPressed: () {
showSearch(
context: context,
delegate: SearchPage<Stocks>(
items: stocks,
searchLabel: '\'에어팟\'을 검색해보세요',
suggestion: const PreferredStockList(stocks: stocks),
// failure: const Center(
// child: Text('No person found :('),
// ),
filter: (stock) => [
stock.stockName,
stock.rank.toString(),
stock.rate.toString(),
],
sort: (a, b) => a.compareTo(b),
builder: (stock) => ListTile(
title: Text(
stock.stockName,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
color: Color(0xFFC0BFC5),
),
),
leading: Text(
'${stock.rank}',
style: const TextStyle(
fontSize: 18.0,
color: Color(0xFF939298),
),
),
trailing: Text(
stock.rate >= 0.0 ? '+${stock.rate}%' : '${stock.rate}%',
style: TextStyle(
fontSize: 17.0,
color: stock.rate >= 0.0
? kStockRatePlusColour
: kStockRateMinusColour),
),
),
),
);
},
icon: const Icon(
Icons.search,
size: 35.0,
),
);
1. 에어팟을 검색해보세요
searchLabel: '\'에어팟\'을 검색해보세요',
2. 인기 검색 순위
suggestion: const PreferredStockList(stocks: stocks),
3. 검색창 입력시 자동완성
builder: (stock) => ListTile(
title: Text(
stock.stockName,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
color: Color(0xFFC0BFC5),
),
),
leading: Text(
'${stock.rank}',
style: const TextStyle(
fontSize: 18.0,
color: Color(0xFF939298),
),
),
trailing: Text(
stock.rate >= 0.0 ? '+${stock.rate}%' : '${stock.rate}%',
style: TextStyle(
fontSize: 17.0,
color: stock.rate >= 0.0
? kStockRatePlusColour
: kStockRateMinusColour),
),
**4. 검색창 입력시 관련 정보 **
filter: (stock) => [
stock.stockName,
stock.rank.toString(),
stock.rate.toString(),
],
sort: (a, b) => a.compareTo(b),
보완점
하지만 삼성전자를 클릭하면 관련 페이지로 이동이 되어야 하는데 그게 안돼서 다시 구현해야 될 것 같다…
그리고 기능 구현에 집중해서 코드의 역할이 이해가 됐지 더 깊은 이해는 덜 이뤄져서 흔히 말하는 기술 부채가 쌓였다 ㅎㅎ
언젠가 부채를 청산할 날이 오기를 …
To Be Continued..
작성자: Kang Chang Hwan
댓글남기기