플러터 학습기(7)-옵션 추가할 수 있는 드롭다운 만들기
개인 프로젝트를 하면서 chatGPT와 5시간 정도 씨름하여 선택지 추가 및 삭제를 할 수 있는 드롭다운을 만들었다.
드롭다운 + 텍스트필드에서 옵션을 입력하면 드롭다운이 열리면서 입력한 옵션을 추가하는 버튼이 나오고, 그 버튼을 누르면 옵션 생성이 완료된다. 생성한 옵션을 선택한 후 선택 해제 버튼을 클릭하여 다시 새로운 옵션을 입력하여 추가할 수 있다. 드롭다운을 클릭해 설정한 다른 옵션을 선택하는 것도 가능하고, 삭제 버튼을 클릭하여 옵션을 삭제할 수도 있다.
이 UX가 최선인지는 아직 고민이 되는데, 원래는 추가하기 버튼을 항상 노출하고 다이얼로그에서 옵션을 추가하게 하려고 했다. 이것보다는 위가 더 나은 방식인 것 같은데, 새로 생성한 옵션이 바로 선택되지 않는 것과 옵션 선택 상태에서 선택 해제와 드롭다운 화살표가 나란히 있는 것이 좀 맘에 걸린다. 첫번째는 chatGPT와 아무리 씨름해도 되지 않았고 두번째는 UI를 조금 손보면 나아지지 않을까 싶다.
드롭다운 하나에 기능이 꽤 많아서 구현에 시간이 꽤 오래 걸렸다.
우선 옵션 추가, 삭제 함수 적용이 필요했다. 그리고 옵션 추가하기 버튼을 기존에 존재하는 옵션과 다른 새로운 옵션을 입력할때만 입력값과 함께 나오게 하고 싶어서 이 부분을 적용하는데도 시간이 오래 걸렸다. 옵션 선택을 해제하면 옵션 추가하기 버튼이 다시 나와서 이 부분을 제어하기 위한 핸들러가 별도로 필요했다. 정리하고 나니 간단한데 직접 짠 것이 아니니 소화하는 시간이 필요할 것 같다.
일단 중간 과정으로 드롭다운 클릭했을때 옵션 추가하기 버튼이 항상 나오는게 하는 코드를 정리해보았다.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: DropdownExample(),
);
}
}
class DropdownExample extends StatefulWidget {
@override
_DropdownExampleState createState() => _DropdownExampleState();
}
class _DropdownExampleState extends State<DropdownExample> {
List<String> options = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Dropdown Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
//하단에 customDropdown을 정의하고 불러온다.
children: [
CustomDropdown(
value: null,
items: options.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (String? newValue) {
setState(() {
// 나중에 설정한 옵션을 선택할 수 있도록 한다
});
},
),
],
),
),
);
}
}
class CustomDropdown extends StatefulWidget {
final String? value;
final List<DropdownMenuItem<String>> items;
final ValueChanged<String?>? onChanged;
CustomDropdown({
required this.value,
required this.items,
required this.onChanged,
});
@override
_CustomDropdownState createState() => _CustomDropdownState();
}
class _CustomDropdownState extends State<CustomDropdown> {
bool _isOpen = false;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
GestureDetector(
onTap: () {
setState(() {
_isOpen = !_isOpen;
});
},
//드롭다운 열기 전 필드 부분 정의
child: Container(
decoration: BoxDecoration(
border: Border.all(),
borderRadius: BorderRadius.circular(4),
),
padding: EdgeInsets.all(8),
child: Row(
children: [
Expanded(
child: Text(
widget.value ?? '카테고리 선택',
overflow: TextOverflow.ellipsis,
),
),
Icon(Icons.arrow_drop_down),
],
),
),
),
//드롭다운 열었을때 옵션 추가 버튼과 생성된 옵션 확인
if (_isOpen)
Container(
margin: EdgeInsets.only(top: 4),
decoration: BoxDecoration(
border: Border.all(),
borderRadius: BorderRadius.circular(4),
),
child: Column(
children: [
...widget.items.map((item) {
return ListTile(
title: item.child!,
onTap: () {
setState(() {
widget.onChanged?.call(item.value);
_isOpen = false;
});
},
)
}).toList(),
ListTile(
title: Row(
children: [
Icon(Icons.add),
SizedBox(width: 8),
Text('카테고리 추가'),
],
),
onTap: () {
//옵션 생성 콜백을 호출해준다.
setState(() {
_isOpen = false;
});
},
),
],
),
),
],
);
}
}
이렇게 긴 코드로 간신히 요만큼을 구현할 수 있다. 흑 ㅠ 옵션 추가 함수가 없어서 카테고리 추가는 아직 동작하지 않는다. 이 부분은 다음 편에..