728x90
flutter Autocomplete optionsView와 fieldView 커스텀 방법입니다.
Autocomplete 관련 검색하는데 자료가 별로 없어서 소스코드를 뜯어보고 핵심 부분을 기록하였습니다.
Autocomplete
Autocomplete<SchoolInfo>(
initialValue: TextEditingValue(
text: //학교에서 잘라서 주소 빼고 보여주기
"${_textControllers[FieldKey.schoolName]!.text.split('학교')[0]}학교",
),
optionsMaxHeight: 200.0,
displayStringForOption: (option) => option.schoolName,
onSelected: (option) async {
// 옵션이 선택될 때의 동작
_isAutocompleteOptionsVisible = false;
_textControllers[FieldKey.schoolName]!.text =
option.schoolName + option.adres;
setState(() {});
},
optionsViewBuilder: _optionsViewBuilder, // >> 이부분만 보면 됩니다.
fieldViewBuilder: _fieldViewBuilder, // >> 이부분만 보면 됩니다.
optionsBuilder: (TextEditingValue textEditingValue) async {
/// '중'이 입력되면 중학교 검색
return await ref.read(careerController).getSchoolList(
gubun: textEditingValue.text.contains('중')
? Gubun.midd_list
: Gubun.elem_list,
searchSchulNm: textEditingValue.text,
);
},
),
>> 이부분만 보면 됩니다
OptionViewBuilder 코드 및 구현 방법
직접 구현했던 코드
/// autoCompleteOptionsView
Widget _optionsViewBuilder(BuildContext context,
void Function(SchoolInfo) onSelected, Iterable<SchoolInfo> options) {
if (!_isAutocompleteOptionsVisible) {
_isAutocompleteOptionsVisible = true;
Future.delayed(
const Duration(milliseconds: 1),
() {
if (mounted) {
setState(() {});
}
},
);
}
return Align(
alignment: Alignment.topLeft,
child: Container(
clipBehavior: Clip.hardEdge,
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(Sizes.size12),
bottomRight: Radius.circular(Sizes.size12),
),
),
child: Material(
elevation: 4.0,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: 200.0,
maxWidth: MediaQuery.of(context).size.width - 32),
child: ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true, // 리스트 자식 높이 크기의 합 만큼으로 영역 고정
itemCount: options.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
onSelected(options.elementAt(index));
},
child: Builder(builder: (context) {
final bool highlight =
AutocompleteHighlightedOption.of(context) == index;
if (highlight) {
SchedulerBinding.instance
.addPostFrameCallback((Duration timeStamp) {
Scrollable.ensureVisible(context, alignment: 0.5);
});
}
return Container(
padding: const EdgeInsets.only(
top: Sizes.size6,
bottom: Sizes.size6,
left: Sizes.size16,
),
color: highlight
? AppColors.grayScale6
: AppColors.grayScale5,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: Sizes.size4),
child: SvgPicture.asset(
'assets/svg/search-icon.svg',
width: 20,
height: 20,
),
),
Gaps.h10,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'학교: ${options.elementAt(index).schoolName}',
style: const TextStyle(
overflow: TextOverflow.ellipsis,
fontSize: Sizes.size14,
),
),
Text(
'주소: ${options.elementAt(index).adres}',
style: const TextStyle(
overflow: TextOverflow.ellipsis,
fontSize: Sizes.size14,
),
)
],
),
),
],
),
);
}),
);
},
),
),
),
),
);
}
코드 다 읽어 볼 필요 없습니다.
Autocomplete 내부 소스에서 _AutocompleteOptions를 따라가면 아래와 같은 코드를 볼 수 있습니다.
핵심은 Align > Meterial > ConstrainedBox > ListView.builder(혹은 원하는 위젯)으로 레이아웃을 잡는 것입니다.
또한 ConstrainedBox의 maxOptionsHeight 값을 AutoComplete의 optionsMaxHeight은 동일한 값을 주면 좋습니다.
select 이벤트는 onSelected(option) 부분과 똑같이 사용하거나 변형해서 사용하면 됩니다.
// The default Material-style Autocomplete options.
class _AutocompleteOptions<T extends Object> extends StatelessWidget {
const _AutocompleteOptions({
super.key,
required this.displayStringForOption,
required this.onSelected,
required this.options,
required this.maxOptionsHeight,
});
final AutocompleteOptionToString<T> displayStringForOption;
final AutocompleteOnSelected<T> onSelected;
final Iterable<T> options;
final double maxOptionsHeight;
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.topLeft,
child: Material(
elevation: 4.0,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: maxOptionsHeight),
child: ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: options.length,
itemBuilder: (BuildContext context, int index) {
final T option = options.elementAt(index);
return InkWell(
onTap: () {
onSelected(option);
},
child: Builder(
builder: (BuildContext context) {
final bool highlight = AutocompleteHighlightedOption.of(context) == index;
if (highlight) {
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
Scrollable.ensureVisible(context, alignment: 0.5);
});
}
return Container(
color: highlight ? Theme.of(context).focusColor : null,
padding: const EdgeInsets.all(16.0),
child: Text(displayStringForOption(option)),
);
}
),
);
},
),
),
),
);
}
}
FieldViewBuilder 코드 및 구현 방법
fieldViewBuilder에는 TextField 위젯을 렌더링하면 됩니다.
핵심은 focusNode와 controller를 꼭 일치 시켜야 합니다.
둘을 일치시켜야 optionView가 제대로 렌더 됩니다.
Widget _fieldViewBuilder(
BuildContext buildContext,
TextEditingController textEditingController,
FocusNode focusNode,
void Function() onFieldSubmitted,
) {
return TextField(
focusNode: focusNode,
cursorColor: AppColors.primary,
controller: textEditingController,
keyboardType: TextInputType.text,
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(
vertical: Sizes.size18,
horizontal: Sizes.size16,
),
prefixIcon: prefixIcon,
filled: true,
fillColor: const Color.fromRGBO(244, 244, 245, 1),
hintText: hintText,
hintStyle: const TextStyle(color: AppColors.grayScale3),
border: inputBorder,
enabledBorder: inputBorder,
focusedBorder: inputBorder,
),
);
}
커스텀 시연
관련 자료
- https://api.flutter.dev/flutter/material/Autocomplete-class.html
728x90
'flutter' 카테고리의 다른 글
구글플레이, App Store 배포 심사 탈락 기록 (1) | 2023.12.07 |
---|---|
Flutter Android 12이상에서의 defalut 스플래시 화면 처리 솔루션 (2) | 2023.12.05 |
Error type argument 'nw_proxy_config_t' (1) | 2023.10.21 |
flutter 간단한 WebView app 만들기 (home_widget 후편) (0) | 2023.09.26 |
flutter 간단한 WebView app 만들기 (home_widget 전편) (0) | 2023.09.25 |
댓글