이 프로젝트는 개발하는남자님의 유튜브 영상을 참고하여 제작하였습니다. 허나, 원본 영상에서 제작하는 방법과는 다를 수 있습니다.
검색화면에서 AppBar역시 홈화면과 동일하게 페이드 애니메이션이 있다고 했습니다.
그렇다면 SliverAppBar를 이용해서 간편하게 만들 수 있는데, 중요한건 저 검색필드를 누르면 글을 적어야 한다는 것이에요.
근데 실제 글 입력은 다음 화면으로 넘어가니까 해당 부분은 TextField가 아니라 Container를 이용해서 버튼처럼 만들면 됩니다. 간단하니까 코드바로 보시죠.
...
Widget _appBar() {
return SliverAppBar(
floating: true,
title: InkWell(
// 누르면 버튼 효과
highlightColor: Colors.black12,
// Container와 동일한 Radius 설정
borderRadius: BorderRadius.circular(8.0),
onTap: () {
//한번 눌렀을 때 페이지 이동함
Get.to(() => const SearchFocus(),
transition: Transition.fadeIn,
binding: SearchFocusBinding(),
id: 1);
},
onLongPress: () {
//길게 눌러도 페이지 이동함
Get.to(() => const SearchFocus(),
transition: Transition.fadeIn,
binding: SearchFocusBinding(),
id: 1);
},
// TextField인 척하는 컨테이너
child: Container(
padding: const EdgeInsets.all(8.0),
height: 40,
decoration: BoxDecoration(
color: Colors.black12, borderRadius: BorderRadius.circular(8.0)),
child: const Row(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: Icon(
Icons.search,
color: Color(0xff7d7d7d),
size: 20.0,
),
),
Text(
'검색',
style: TextStyle(color: Color(0xff7d7d7d), fontSize: 18),
)
],
),
),
),
);
}
...
Container와 InkWell 위젯을 이용해서 쉽게 해당사항을 구현할 수 있었습니다. 그리고 저거를 누르면 페이지 이동을 하는데, 길게 눌러도 페이지 이동을 하더라고요? 아무튼 이동하면서 페이드인 되는 애니메이션을 사용했고, 이동한 페이지에서 사용할 Controller도 바인딩을 통해 생성합니다.
일단 베이스가되는 화면부터 만들어보죠.
class SearchFocus extends StatelessWidget {
const SearchFocus({super.key});
Widget build(BuildContext context) {
return Scaffold();
}
}
이제 TextField위젯을 커스터마이징해서 제작하겠습니다. search_text_field.dart파일을 만들고 위젯을 생성합니다.
class SearchTextField extends StatelessWidget {
final TextEditingController controller;
final bool focus;
const SearchTextField(
{super.key, required this.controller, required this.focus});
Widget build(BuildContext context) {
return Container(
height: 40,
width: double.infinity,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(8.0)),
child: TextField(
autofocus: focus,
onSubmitted: Get.find<SearchFocusController>().submitted,
controller: controller,
cursorColor: const Color(0xff7f7f7f),
decoration: InputDecoration(
isDense: true,
isCollapsed: true,
contentPadding: const EdgeInsets.symmetric(vertical: 8.0),
border: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.transparent),
borderRadius: BorderRadius.circular(8.0),
gapPadding: 0.0),
focusedBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.transparent),
borderRadius: BorderRadius.circular(8.0),
gapPadding: 0.0),
enabledBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.transparent),
borderRadius: BorderRadius.circular(8.0),
gapPadding: 0.0),
hintText: '검색',
hintStyle: const TextStyle(fontSize: 18),
focusColor: const Color(0xff7d7d7d),
filled: true,
fillColor: Colors.black12,
prefixIconColor: const Color(0xff7d7d7d),
prefixIcon: const Align(
widthFactor: 1.0,
heightFactor: 1.0,
child: Icon(
Icons.search,
size: 20,
),
)),
),
);
}
}
autoFocus와 controller는 외부에서 전달받을 수 있도록 생성자에 포함시켜줍니다.
UI는 만들었으니, 컨트롤러도 있어야 되고 페이지 이동 함수도 있어야 되니까 새로운 컨트롤러를 생성하겠습니다. 컨트롤러와 바인딩 각각 만들어줄게요.
//바인딩
class SearchFocusBinding implements Bindings {
void dependencies() {
Get.put(SearchFocusController());
}
}
//컨트롤러
class SearchFocusController extends GetxController {
final TextEditingController _controller = TextEditingController();
TextEditingController get searchController => _controller;
//완료버튼 클릭 시 결과화면으로 이동함
void submitted(String value) {
//결과화면은 페이지가 전환되야함.
Get.off(() => const SearchResult());
}
}
완료버튼을 누르면 페이지가 이동해야겠죠? 그래서 value를 전달받아서 다음 페이지로 이동시키면 됩니다. 뭐 사실상 value는 전달안해도되요. 결과페이지에서도 동일한 컨트롤러를 사용할겁니다. 근데, 페이지를 이동할 때, Get.off 메소드를 이용해서 이동해야합니다. 그래야 맞아요.
이제 TextEditingController도 생성되었으니 모두 전달합시다.
class SearchFocus extends StatelessWidget {
const SearchFocus({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: SearchTextField(
focus: true,
controller: Get.find<SearchFocusController>().searchController,
),
),
);
}
}
검색 버튼을 클릭한 후, 하단을 아래로 스크롤하거나 위로 스크롤하면 검색 포커스가 해제됩니다, 해당 사항은 GestureDetector의 onPanUpdate를 통해서 쉽게 만들 수 있습니다.
class SearchFocus extends StatelessWidget {
const SearchFocus({super.key});
Widget build(BuildContext context) {
return GestureDetector(
onPanUpdate: (details) => FocusScope.of(context).unfocus(),
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: SearchTextField(
focus: true,
controller: ,
),
),
),
);
}
}
gif에서는 안보이지만 실제로 위아래로 스크롤하면 포커스가 해제됩니다 !