import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:superport/utils/validators.dart'; class CompanyNameAutocomplete extends StatelessWidget { final TextEditingController nameController; final FocusNode nameFocusNode; final List companyNames; final List filteredCompanyNames; final bool showCompanyNameDropdown; final Function(String) onCompanyNameSelected; final ValueChanged onNameSaved; final String label; final String hint; const CompanyNameAutocomplete({ Key? key, required this.nameController, required this.nameFocusNode, required this.companyNames, required this.filteredCompanyNames, required this.showCompanyNameDropdown, required this.onCompanyNameSelected, required this.onNameSaved, required this.label, required this.hint, }) : super(key: key); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextFormField( controller: nameController, focusNode: nameFocusNode, textInputAction: TextInputAction.next, decoration: InputDecoration( labelText: label, hintText: hint, suffixIcon: nameController.text.isNotEmpty ? IconButton( icon: const Icon(Icons.clear), onPressed: () { nameController.clear(); }, ) : IconButton( icon: const Icon(Icons.arrow_drop_down), onPressed: () {}, ), ), validator: (value) => validateRequired(value, label), onFieldSubmitted: (_) { if (filteredCompanyNames.length == 1 && showCompanyNameDropdown) { onCompanyNameSelected(filteredCompanyNames[0]); } }, onTap: () {}, onSaved: onNameSaved, ), AnimatedContainer( duration: const Duration(milliseconds: 200), height: showCompanyNameDropdown ? (filteredCompanyNames.length > 4 ? 200 : filteredCompanyNames.length * 50.0) : 0, margin: EdgeInsets.only(top: showCompanyNameDropdown ? 4 : 0), child: SingleChildScrollView( physics: const ClampingScrollPhysics(), child: GestureDetector( onTap: () {}, child: Container( width: double.infinity, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(4), border: Border.all(color: Colors.grey.shade300), boxShadow: [ BoxShadow( color: Colors.grey.withAlpha(77), spreadRadius: 1, blurRadius: 3, offset: const Offset(0, 1), ), ], ), child: filteredCompanyNames.isEmpty ? const Padding( padding: EdgeInsets.all(12.0), child: Text('검색 결과가 없습니다'), ) : ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: filteredCompanyNames.length, separatorBuilder: (context, index) => Divider( height: 1, color: Colors.grey.shade200, ), itemBuilder: (context, index) { final companyName = filteredCompanyNames[index]; final text = nameController.text.toLowerCase(); if (text.isEmpty) { return ListTile( dense: true, title: Text(companyName), onTap: () => onCompanyNameSelected(companyName), ); } // 일치하는 부분 찾기 final matchIndex = companyName .toLowerCase() .indexOf(text.toLowerCase()); if (matchIndex < 0) { return ListTile( dense: true, title: Text(companyName), onTap: () => onCompanyNameSelected(companyName), ); } return ListTile( dense: true, title: RichText( text: TextSpan( children: [ // 일치 이전 부분 if (matchIndex > 0) TextSpan( text: companyName.substring( 0, matchIndex, ), style: const TextStyle( color: Colors.black, ), ), // 일치하는 부분 TextSpan( text: companyName.substring( matchIndex, matchIndex + text.length, ), style: TextStyle( fontWeight: FontWeight.bold, color: Theme.of(context).primaryColor, ), ), // 일치 이후 부분 if (matchIndex + text.length < companyName.length) TextSpan( text: companyName.substring( matchIndex + text.length, ), style: TextStyle( color: matchIndex == 0 ? Colors.grey[600] : Colors.black, ), ), ], ), ), onTap: () => onCompanyNameSelected(companyName), ); }, ), ), ), ), ), ], ); } }