feat(app/schedule): 최근 검색기록 기능 추가 및 UI 개선
- 최근 검색기록 기능 추가 (SharedPreferences로 최대 10개 저장) - 추천 검색어 입력 시 프로그레스바 제거 - 추천 검색어 클릭 효과 제거 (InkWell → GestureDetector) - 달력 요일/날짜 그리드 상단 여백 축소 - 달력 그리드와 오늘 버튼 사이 간격 증가 - 검색 종료 시 날짜 선택 부분 중앙 스크롤 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2de5cb8f93
commit
7f96ab5fb2
5 changed files with 464 additions and 166 deletions
|
|
@ -6,6 +6,7 @@ library;
|
|||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../models/schedule.dart';
|
||||
import '../services/schedules_service.dart';
|
||||
|
||||
|
|
@ -363,3 +364,89 @@ class SuggestionController extends Notifier<SuggestionState> {
|
|||
final suggestionProvider = NotifierProvider<SuggestionController, SuggestionState>(
|
||||
SuggestionController.new,
|
||||
);
|
||||
|
||||
/// 최근 검색기록 상태
|
||||
class RecentSearchState {
|
||||
final List<String> searches;
|
||||
|
||||
const RecentSearchState({this.searches = const []});
|
||||
|
||||
RecentSearchState copyWith({List<String>? searches}) {
|
||||
return RecentSearchState(searches: searches ?? this.searches);
|
||||
}
|
||||
}
|
||||
|
||||
/// 최근 검색기록 컨트롤러
|
||||
class RecentSearchController extends Notifier<RecentSearchState> {
|
||||
static const int _maxHistory = 10;
|
||||
|
||||
@override
|
||||
RecentSearchState build() {
|
||||
_loadFromStorage();
|
||||
return const RecentSearchState();
|
||||
}
|
||||
|
||||
/// SharedPreferences에서 로드
|
||||
Future<void> _loadFromStorage() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final searches = prefs.getStringList('recent_searches');
|
||||
if (searches != null) {
|
||||
state = state.copyWith(searches: searches);
|
||||
}
|
||||
} catch (e) {
|
||||
// 로드 실패 시 무시
|
||||
}
|
||||
}
|
||||
|
||||
/// 검색어 추가
|
||||
Future<void> addSearch(String query) async {
|
||||
if (query.trim().isEmpty) return;
|
||||
|
||||
final trimmed = query.trim();
|
||||
final newSearches = [
|
||||
trimmed,
|
||||
...state.searches.where((s) => s != trimmed),
|
||||
].take(_maxHistory).toList();
|
||||
|
||||
state = state.copyWith(searches: newSearches);
|
||||
|
||||
// SharedPreferences에 저장
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setStringList('recent_searches', newSearches);
|
||||
} catch (e) {
|
||||
// 저장 실패 시 무시
|
||||
}
|
||||
}
|
||||
|
||||
/// 특정 검색어 삭제
|
||||
Future<void> removeSearch(String query) async {
|
||||
final newSearches = state.searches.where((s) => s != query).toList();
|
||||
state = state.copyWith(searches: newSearches);
|
||||
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setStringList('recent_searches', newSearches);
|
||||
} catch (e) {
|
||||
// 저장 실패 시 무시
|
||||
}
|
||||
}
|
||||
|
||||
/// 전체 삭제
|
||||
Future<void> clearAll() async {
|
||||
state = const RecentSearchState();
|
||||
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setStringList('recent_searches', []);
|
||||
} catch (e) {
|
||||
// 저장 실패 시 무시
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 최근 검색기록 Provider
|
||||
final recentSearchProvider = NotifierProvider<RecentSearchController, RecentSearchState>(
|
||||
RecentSearchController.new,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -105,6 +105,11 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
ref.read(searchProvider.notifier).clear();
|
||||
ref.read(suggestionProvider.notifier).clear();
|
||||
_searchFocusNode.unfocus();
|
||||
// 검색 종료 시 선택된 날짜로 스크롤
|
||||
final selectedDate = ref.read(scheduleProvider).selectedDate;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_scrollToSelectedDate(selectedDate);
|
||||
});
|
||||
}
|
||||
|
||||
/// 추천 검색어 화면 표시 (유튜브 스타일)
|
||||
|
|
@ -136,6 +141,7 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
if (query.trim().isNotEmpty) {
|
||||
_lastSearchTerm = query; // 검색어 저장
|
||||
ref.read(searchProvider.notifier).search(query);
|
||||
ref.read(recentSearchProvider.notifier).addSearch(query); // 최근 검색기록 저장
|
||||
setState(() {
|
||||
_showSuggestions = false;
|
||||
});
|
||||
|
|
@ -175,7 +181,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
/// 달력 열기
|
||||
void _openCalendar(DateTime initialDate) {
|
||||
final today = DateTime.now();
|
||||
final monthDelta = (initialDate.year - today.year) * 12 + (initialDate.month - today.month);
|
||||
final monthDelta =
|
||||
(initialDate.year - today.year) * 12 +
|
||||
(initialDate.month - today.month);
|
||||
_yearRangeStart = (initialDate.year ~/ 12) * 12;
|
||||
|
||||
// 년도 PageView 페이지 계산
|
||||
|
|
@ -218,7 +226,8 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
|
||||
final dayIndex = selectedDate.day - 1;
|
||||
const itemWidth = 52.0; // 44 + 8 (gap)
|
||||
final targetOffset = (dayIndex * itemWidth) -
|
||||
final targetOffset =
|
||||
(dayIndex * itemWidth) -
|
||||
(MediaQuery.of(context).size.width / 2) +
|
||||
(itemWidth / 2);
|
||||
_dateScrollController.animateTo(
|
||||
|
|
@ -291,10 +300,7 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
switchInCurve: Curves.easeOut,
|
||||
switchOutCurve: Curves.easeIn,
|
||||
transitionBuilder: (child, animation) {
|
||||
return FadeTransition(
|
||||
opacity: animation,
|
||||
child: child,
|
||||
);
|
||||
return FadeTransition(opacity: animation, child: child);
|
||||
},
|
||||
child: _isSearchMode
|
||||
? KeyedSubtree(
|
||||
|
|
@ -350,7 +356,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
return GestureDetector(
|
||||
onTap: _closeCalendar,
|
||||
child: Container(
|
||||
color: Colors.black.withValues(alpha: 0.4 * _calendarAnimation.value),
|
||||
color: Colors.black.withValues(
|
||||
alpha: 0.4 * _calendarAnimation.value,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
@ -475,8 +483,11 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
|
||||
/// 추천 검색어 빌드 (유튜브 스타일)
|
||||
Widget _buildSuggestions(SuggestionState suggestionState) {
|
||||
// 입력값이 없을 때
|
||||
final recentSearchState = ref.watch(recentSearchProvider);
|
||||
|
||||
// 입력값이 없을 때 - 최근 검색기록 표시
|
||||
if (_searchInputController.text.isEmpty) {
|
||||
if (recentSearchState.searches.isEmpty) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 100),
|
||||
child: Align(
|
||||
|
|
@ -492,16 +503,8 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 로딩 중
|
||||
if (suggestionState.isLoading && suggestionState.suggestions.isEmpty) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 100),
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: const CircularProgressIndicator(color: AppColors.primary),
|
||||
),
|
||||
);
|
||||
// 최근 검색기록 목록
|
||||
return _buildRecentSearches(recentSearchState.searches);
|
||||
}
|
||||
|
||||
// 추천 검색어 없음
|
||||
|
|
@ -528,11 +531,12 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
itemCount: suggestionState.suggestions.length,
|
||||
itemBuilder: (context, index) {
|
||||
final suggestion = suggestionState.suggestions[index];
|
||||
return InkWell(
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_searchInputController.text = suggestion;
|
||||
_onSearch(suggestion);
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -565,7 +569,8 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
GestureDetector(
|
||||
onTap: () {
|
||||
_searchInputController.text = suggestion;
|
||||
_searchInputController.selection = TextSelection.fromPosition(
|
||||
_searchInputController.selection =
|
||||
TextSelection.fromPosition(
|
||||
TextPosition(offset: suggestion.length),
|
||||
);
|
||||
_onSearchInputChanged(suggestion);
|
||||
|
|
@ -587,6 +592,108 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
);
|
||||
}
|
||||
|
||||
/// 최근 검색기록 빌드
|
||||
Widget _buildRecentSearches(List<String> searches) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 헤더
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
'최근 검색',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Pretendard',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
ref.read(recentSearchProvider.notifier).clearAll();
|
||||
},
|
||||
child: const Text(
|
||||
'전체 삭제',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Pretendard',
|
||||
fontSize: 12,
|
||||
color: AppColors.textTertiary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// 검색기록 목록
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
itemCount: searches.length,
|
||||
itemBuilder: (context, index) {
|
||||
final search = searches[index];
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_searchInputController.text = search;
|
||||
_onSearch(search);
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: AppColors.divider.withValues(alpha: 0.5),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.history,
|
||||
size: 18,
|
||||
color: AppColors.textTertiary,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
search,
|
||||
style: const TextStyle(
|
||||
fontFamily: 'Pretendard',
|
||||
fontSize: 15,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
// 삭제 버튼
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
ref.read(recentSearchProvider.notifier).removeSearch(search);
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.all(4),
|
||||
child: Icon(
|
||||
Icons.close,
|
||||
size: 16,
|
||||
color: AppColors.textTertiary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 검색 결과 빌드
|
||||
Widget _buildSearchResults(SearchState searchState) {
|
||||
// 검색어가 없을 때
|
||||
|
|
@ -643,7 +750,8 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
key: ValueKey('search_list_${searchState.searchTerm}'),
|
||||
controller: _searchScrollController,
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: searchState.results.length + (searchState.isFetchingMore ? 1 : 0),
|
||||
itemCount:
|
||||
searchState.results.length + (searchState.isFetchingMore ? 1 : 0),
|
||||
itemBuilder: (context, index) {
|
||||
// 로딩 인디케이터
|
||||
if (index >= searchState.results.length) {
|
||||
|
|
@ -702,7 +810,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
}
|
||||
},
|
||||
icon: const Icon(Icons.calendar_today_outlined, size: 20),
|
||||
color: _showCalendar ? AppColors.primary : AppColors.textSecondary,
|
||||
color: _showCalendar
|
||||
? AppColors.primary
|
||||
: AppColors.textSecondary,
|
||||
),
|
||||
// 이전 월 (데이트픽커가 펼쳐지면 숨김, 페이드 애니메이션)
|
||||
AnimatedSwitcher(
|
||||
|
|
@ -733,7 +843,8 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
? () {
|
||||
setState(() {
|
||||
_showYearMonthPicker = !_showYearMonthPicker;
|
||||
_yearRangeStart = (_calendarViewDate.year ~/ 12) * 12;
|
||||
_yearRangeStart =
|
||||
(_calendarViewDate.year ~/ 12) * 12;
|
||||
});
|
||||
}
|
||||
: null,
|
||||
|
|
@ -745,8 +856,14 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: _showCalendar
|
||||
? const SizedBox(key: ValueKey('arrow_space'), width: 20)
|
||||
: const SizedBox(key: ValueKey('no_space'), width: 0),
|
||||
? const SizedBox(
|
||||
key: ValueKey('arrow_space'),
|
||||
width: 20,
|
||||
)
|
||||
: const SizedBox(
|
||||
key: ValueKey('no_space'),
|
||||
width: 0,
|
||||
),
|
||||
),
|
||||
// 년월 텍스트 (항상 가운데 고정)
|
||||
Text(
|
||||
|
|
@ -765,7 +882,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
duration: const Duration(milliseconds: 200),
|
||||
child: _showCalendar
|
||||
? Row(
|
||||
key: ValueKey('dropdown_$_showYearMonthPicker'),
|
||||
key: ValueKey(
|
||||
'dropdown_$_showYearMonthPicker',
|
||||
),
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(width: 2),
|
||||
|
|
@ -780,7 +899,10 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
),
|
||||
],
|
||||
)
|
||||
: const SizedBox(key: ValueKey('no_dropdown'), width: 0),
|
||||
: const SizedBox(
|
||||
key: ValueKey('no_dropdown'),
|
||||
width: 0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -822,7 +944,10 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
}
|
||||
|
||||
/// 달력 팝업 빌드
|
||||
Widget _buildCalendarPopup(ScheduleState state, ScheduleController controller) {
|
||||
Widget _buildCalendarPopup(
|
||||
ScheduleState state,
|
||||
ScheduleController controller,
|
||||
) {
|
||||
return AnimatedBuilder(
|
||||
animation: _calendarAnimation,
|
||||
builder: (context, child) {
|
||||
|
|
@ -830,10 +955,7 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
heightFactor: _calendarAnimation.value,
|
||||
child: Opacity(
|
||||
opacity: _calendarAnimation.value,
|
||||
child: child,
|
||||
),
|
||||
child: Opacity(opacity: _calendarAnimation.value, child: child),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
@ -860,7 +982,8 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
},
|
||||
transitionBuilder: (child, animation) {
|
||||
// 년월 선택기에만 슬라이드 효과 추가
|
||||
final isYearMonthPicker = child.key == const ValueKey('yearMonth');
|
||||
final isYearMonthPicker =
|
||||
child.key == const ValueKey('yearMonth');
|
||||
if (isYearMonthPicker) {
|
||||
return FadeTransition(
|
||||
opacity: animation,
|
||||
|
|
@ -943,7 +1066,11 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
// 년도 라벨
|
||||
const Text(
|
||||
'년도',
|
||||
style: TextStyle(fontFamily: 'Pretendard', fontSize: 12, color: AppColors.textTertiary),
|
||||
style: TextStyle(
|
||||
fontFamily: 'Pretendard',
|
||||
fontSize: 12,
|
||||
color: AppColors.textTertiary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
// 년도 그리드 (ExpandablePageView로 스와이프 애니메이션)
|
||||
|
|
@ -969,7 +1096,11 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
// 월 라벨
|
||||
const Text(
|
||||
'월',
|
||||
style: TextStyle(fontFamily: 'Pretendard', fontSize: 12, color: AppColors.textTertiary),
|
||||
style: TextStyle(
|
||||
fontFamily: 'Pretendard',
|
||||
fontSize: 12,
|
||||
color: AppColors.textTertiary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
// 월 그리드
|
||||
|
|
@ -987,11 +1118,16 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
itemBuilder: (context, index) {
|
||||
final month = index + 1;
|
||||
final isSelected = month == _calendarViewDate.month;
|
||||
final isCurrentMonth = month == today.month && _calendarViewDate.year == today.year;
|
||||
final isCurrentMonth =
|
||||
month == today.month && _calendarViewDate.year == today.year;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_calendarViewDate = DateTime(_calendarViewDate.year, month, 1);
|
||||
_calendarViewDate = DateTime(
|
||||
_calendarViewDate.year,
|
||||
month,
|
||||
1,
|
||||
);
|
||||
_showYearMonthPicker = false;
|
||||
});
|
||||
},
|
||||
|
|
@ -1008,7 +1144,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
style: TextStyle(
|
||||
fontFamily: 'Pretendard',
|
||||
fontSize: 14,
|
||||
fontWeight: isSelected || isCurrentMonth ? FontWeight.w600 : FontWeight.w400,
|
||||
fontWeight: isSelected || isCurrentMonth
|
||||
? FontWeight.w600
|
||||
: FontWeight.w400,
|
||||
color: isSelected
|
||||
? Colors.white
|
||||
: isCurrentMonth
|
||||
|
|
@ -1093,7 +1231,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
style: TextStyle(
|
||||
fontFamily: 'Pretendard',
|
||||
fontSize: 14,
|
||||
fontWeight: isSelected || isCurrentYear ? FontWeight.w600 : FontWeight.w400,
|
||||
fontWeight: isSelected || isCurrentYear
|
||||
? FontWeight.w600
|
||||
: FontWeight.w400,
|
||||
color: isSelected
|
||||
? Colors.white
|
||||
: isCurrentYear
|
||||
|
|
@ -1109,7 +1249,10 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
}
|
||||
|
||||
/// 달력 그리드
|
||||
Widget _buildCalendarGrid(ScheduleState state, ScheduleController controller) {
|
||||
Widget _buildCalendarGrid(
|
||||
ScheduleState state,
|
||||
ScheduleController controller,
|
||||
) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
|
|
@ -1117,7 +1260,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
children: [
|
||||
// 요일 헤더
|
||||
Row(
|
||||
children: ['일', '월', '화', '수', '목', '금', '토'].asMap().entries.map((entry) {
|
||||
children: ['일', '월', '화', '수', '목', '금', '토'].asMap().entries.map((
|
||||
entry,
|
||||
) {
|
||||
final index = entry.key;
|
||||
final day = entry.value;
|
||||
return Expanded(
|
||||
|
|
@ -1139,7 +1284,7 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const SizedBox(height: 12),
|
||||
// 날짜 그리드 (ExpandablePageView로 높이 자동 조절)
|
||||
ExpandablePageView.builder(
|
||||
controller: _calendarPageController,
|
||||
|
|
@ -1177,7 +1322,7 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const SizedBox(height: 16),
|
||||
// 오늘 버튼
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
|
|
@ -1219,6 +1364,7 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
}) {
|
||||
return GridView.builder(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 7,
|
||||
|
|
@ -1233,7 +1379,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
final isSelected = controller.isSelected(date);
|
||||
final isToday = controller.isToday(date);
|
||||
final dayOfWeek = index % 7;
|
||||
final daySchedules = isCurrentMonth ? state.getDaySchedules(date) : <Schedule>[];
|
||||
final daySchedules = isCurrentMonth
|
||||
? state.getDaySchedules(date)
|
||||
: <Schedule>[];
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
|
|
@ -1268,7 +1416,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
style: TextStyle(
|
||||
fontFamily: 'Pretendard',
|
||||
fontSize: 14,
|
||||
fontWeight: isSelected || isToday ? FontWeight.bold : FontWeight.w400,
|
||||
fontWeight: isSelected || isToday
|
||||
? FontWeight.bold
|
||||
: FontWeight.w400,
|
||||
color: !isCurrentMonth
|
||||
? AppColors.textTertiary.withValues(alpha: 0.5)
|
||||
: isSelected
|
||||
|
|
@ -1310,7 +1460,10 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
}
|
||||
|
||||
/// 날짜 선택기 빌드
|
||||
Widget _buildDateSelector(ScheduleState state, ScheduleController controller) {
|
||||
Widget _buildDateSelector(
|
||||
ScheduleState state,
|
||||
ScheduleController controller,
|
||||
) {
|
||||
return Container(
|
||||
height: 80,
|
||||
color: Colors.white,
|
||||
|
|
@ -1378,8 +1531,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
return Container(
|
||||
width: 4,
|
||||
height: 4,
|
||||
margin:
|
||||
const EdgeInsets.symmetric(horizontal: 1),
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 1,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: parseColor(schedule.categoryColor),
|
||||
shape: BoxShape.circle,
|
||||
|
|
@ -1410,10 +1564,7 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
return Center(
|
||||
child: Text(
|
||||
'${state.selectedDate.month}월 ${state.selectedDate.day}일 일정이 없습니다',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.textTertiary,
|
||||
),
|
||||
style: const TextStyle(fontSize: 14, color: AppColors.textTertiary),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -1426,7 +1577,8 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
|
|||
final schedule = state.selectedDateSchedules[index];
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: index < state.selectedDateSchedules.length - 1 ? 12 : 0),
|
||||
bottom: index < state.selectedDateSchedules.length - 1 ? 12 : 0,
|
||||
),
|
||||
child: AnimatedScheduleCard(
|
||||
key: ValueKey('${schedule.id}_${state.selectedDate.toString()}'),
|
||||
index: index,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import Foundation
|
|||
|
||||
import package_info_plus
|
||||
import path_provider_foundation
|
||||
import shared_preferences_foundation
|
||||
import sqflite_darwin
|
||||
import url_launcher_macos
|
||||
import video_player_avfoundation
|
||||
|
|
@ -15,6 +16,7 @@ import wakelock_plus
|
|||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
|
||||
|
|
|
|||
|
|
@ -664,6 +664,62 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.28.0"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.18"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.6"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ dependencies:
|
|||
video_player: ^2.9.2
|
||||
chewie: ^1.8.5
|
||||
expandable_page_view: ^1.0.17
|
||||
shared_preferences: ^2.3.5
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue