feat(app/schedule): 최근 검색기록 기능 추가 및 UI 개선

- 최근 검색기록 기능 추가 (SharedPreferences로 최대 10개 저장)
- 추천 검색어 입력 시 프로그레스바 제거
- 추천 검색어 클릭 효과 제거 (InkWell → GestureDetector)
- 달력 요일/날짜 그리드 상단 여백 축소
- 달력 그리드와 오늘 버튼 사이 간격 증가
- 검색 종료 시 날짜 선택 부분 중앙 스크롤

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-01-14 09:12:14 +09:00
parent 2de5cb8f93
commit 7f96ab5fb2
5 changed files with 464 additions and 166 deletions

View file

@ -6,6 +6,7 @@ library;
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/schedule.dart'; import '../models/schedule.dart';
import '../services/schedules_service.dart'; import '../services/schedules_service.dart';
@ -363,3 +364,89 @@ class SuggestionController extends Notifier<SuggestionState> {
final suggestionProvider = NotifierProvider<SuggestionController, SuggestionState>( final suggestionProvider = NotifierProvider<SuggestionController, SuggestionState>(
SuggestionController.new, 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,
);

View file

@ -105,6 +105,11 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
ref.read(searchProvider.notifier).clear(); ref.read(searchProvider.notifier).clear();
ref.read(suggestionProvider.notifier).clear(); ref.read(suggestionProvider.notifier).clear();
_searchFocusNode.unfocus(); _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) { if (query.trim().isNotEmpty) {
_lastSearchTerm = query; // _lastSearchTerm = query; //
ref.read(searchProvider.notifier).search(query); ref.read(searchProvider.notifier).search(query);
ref.read(recentSearchProvider.notifier).addSearch(query); //
setState(() { setState(() {
_showSuggestions = false; _showSuggestions = false;
}); });
@ -175,7 +181,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
/// ///
void _openCalendar(DateTime initialDate) { void _openCalendar(DateTime initialDate) {
final today = DateTime.now(); 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; _yearRangeStart = (initialDate.year ~/ 12) * 12;
// PageView // PageView
@ -218,7 +226,8 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
final dayIndex = selectedDate.day - 1; final dayIndex = selectedDate.day - 1;
const itemWidth = 52.0; // 44 + 8 (gap) const itemWidth = 52.0; // 44 + 8 (gap)
final targetOffset = (dayIndex * itemWidth) - final targetOffset =
(dayIndex * itemWidth) -
(MediaQuery.of(context).size.width / 2) + (MediaQuery.of(context).size.width / 2) +
(itemWidth / 2); (itemWidth / 2);
_dateScrollController.animateTo( _dateScrollController.animateTo(
@ -291,10 +300,7 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
switchInCurve: Curves.easeOut, switchInCurve: Curves.easeOut,
switchOutCurve: Curves.easeIn, switchOutCurve: Curves.easeIn,
transitionBuilder: (child, animation) { transitionBuilder: (child, animation) {
return FadeTransition( return FadeTransition(opacity: animation, child: child);
opacity: animation,
child: child,
);
}, },
child: _isSearchMode child: _isSearchMode
? KeyedSubtree( ? KeyedSubtree(
@ -350,7 +356,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
return GestureDetector( return GestureDetector(
onTap: _closeCalendar, onTap: _closeCalendar,
child: Container( 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) { Widget _buildSuggestions(SuggestionState suggestionState) {
// final recentSearchState = ref.watch(recentSearchProvider);
// -
if (_searchInputController.text.isEmpty) { if (_searchInputController.text.isEmpty) {
if (recentSearchState.searches.isEmpty) {
return Padding( return Padding(
padding: const EdgeInsets.only(top: 100), padding: const EdgeInsets.only(top: 100),
child: Align( child: Align(
@ -492,16 +503,8 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
), ),
); );
} }
//
// return _buildRecentSearches(recentSearchState.searches);
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),
),
);
} }
// //
@ -528,11 +531,12 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
itemCount: suggestionState.suggestions.length, itemCount: suggestionState.suggestions.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final suggestion = suggestionState.suggestions[index]; final suggestion = suggestionState.suggestions[index];
return InkWell( return GestureDetector(
onTap: () { onTap: () {
_searchInputController.text = suggestion; _searchInputController.text = suggestion;
_onSearch(suggestion); _onSearch(suggestion);
}, },
behavior: HitTestBehavior.opaque,
child: Container( child: Container(
padding: const EdgeInsets.symmetric(vertical: 14), padding: const EdgeInsets.symmetric(vertical: 14),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -565,7 +569,8 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
GestureDetector( GestureDetector(
onTap: () { onTap: () {
_searchInputController.text = suggestion; _searchInputController.text = suggestion;
_searchInputController.selection = TextSelection.fromPosition( _searchInputController.selection =
TextSelection.fromPosition(
TextPosition(offset: suggestion.length), TextPosition(offset: suggestion.length),
); );
_onSearchInputChanged(suggestion); _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) { Widget _buildSearchResults(SearchState searchState) {
// //
@ -643,7 +750,8 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
key: ValueKey('search_list_${searchState.searchTerm}'), key: ValueKey('search_list_${searchState.searchTerm}'),
controller: _searchScrollController, controller: _searchScrollController,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
itemCount: searchState.results.length + (searchState.isFetchingMore ? 1 : 0), itemCount:
searchState.results.length + (searchState.isFetchingMore ? 1 : 0),
itemBuilder: (context, index) { itemBuilder: (context, index) {
// //
if (index >= searchState.results.length) { if (index >= searchState.results.length) {
@ -702,7 +810,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
} }
}, },
icon: const Icon(Icons.calendar_today_outlined, size: 20), icon: const Icon(Icons.calendar_today_outlined, size: 20),
color: _showCalendar ? AppColors.primary : AppColors.textSecondary, color: _showCalendar
? AppColors.primary
: AppColors.textSecondary,
), ),
// ( , ) // ( , )
AnimatedSwitcher( AnimatedSwitcher(
@ -733,7 +843,8 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
? () { ? () {
setState(() { setState(() {
_showYearMonthPicker = !_showYearMonthPicker; _showYearMonthPicker = !_showYearMonthPicker;
_yearRangeStart = (_calendarViewDate.year ~/ 12) * 12; _yearRangeStart =
(_calendarViewDate.year ~/ 12) * 12;
}); });
} }
: null, : null,
@ -745,8 +856,14 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
AnimatedSwitcher( AnimatedSwitcher(
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
child: _showCalendar child: _showCalendar
? const SizedBox(key: ValueKey('arrow_space'), width: 20) ? const SizedBox(
: const SizedBox(key: ValueKey('no_space'), width: 0), key: ValueKey('arrow_space'),
width: 20,
)
: const SizedBox(
key: ValueKey('no_space'),
width: 0,
),
), ),
// ( ) // ( )
Text( Text(
@ -765,7 +882,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
child: _showCalendar child: _showCalendar
? Row( ? Row(
key: ValueKey('dropdown_$_showYearMonthPicker'), key: ValueKey(
'dropdown_$_showYearMonthPicker',
),
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const SizedBox(width: 2), 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( return AnimatedBuilder(
animation: _calendarAnimation, animation: _calendarAnimation,
builder: (context, child) { builder: (context, child) {
@ -830,10 +955,7 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
child: Align( child: Align(
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
heightFactor: _calendarAnimation.value, heightFactor: _calendarAnimation.value,
child: Opacity( child: Opacity(opacity: _calendarAnimation.value, child: child),
opacity: _calendarAnimation.value,
child: child,
),
), ),
); );
}, },
@ -860,7 +982,8 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
}, },
transitionBuilder: (child, animation) { transitionBuilder: (child, animation) {
// //
final isYearMonthPicker = child.key == const ValueKey('yearMonth'); final isYearMonthPicker =
child.key == const ValueKey('yearMonth');
if (isYearMonthPicker) { if (isYearMonthPicker) {
return FadeTransition( return FadeTransition(
opacity: animation, opacity: animation,
@ -943,7 +1066,11 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
// //
const Text( const Text(
'년도', '년도',
style: TextStyle(fontFamily: 'Pretendard', fontSize: 12, color: AppColors.textTertiary), style: TextStyle(
fontFamily: 'Pretendard',
fontSize: 12,
color: AppColors.textTertiary,
),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
// (ExpandablePageView로 ) // (ExpandablePageView로 )
@ -969,7 +1096,11 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
// //
const Text( const Text(
'', '',
style: TextStyle(fontFamily: 'Pretendard', fontSize: 12, color: AppColors.textTertiary), style: TextStyle(
fontFamily: 'Pretendard',
fontSize: 12,
color: AppColors.textTertiary,
),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
// //
@ -987,11 +1118,16 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
itemBuilder: (context, index) { itemBuilder: (context, index) {
final month = index + 1; final month = index + 1;
final isSelected = month == _calendarViewDate.month; 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( return GestureDetector(
onTap: () { onTap: () {
setState(() { setState(() {
_calendarViewDate = DateTime(_calendarViewDate.year, month, 1); _calendarViewDate = DateTime(
_calendarViewDate.year,
month,
1,
);
_showYearMonthPicker = false; _showYearMonthPicker = false;
}); });
}, },
@ -1008,7 +1144,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
style: TextStyle( style: TextStyle(
fontFamily: 'Pretendard', fontFamily: 'Pretendard',
fontSize: 14, fontSize: 14,
fontWeight: isSelected || isCurrentMonth ? FontWeight.w600 : FontWeight.w400, fontWeight: isSelected || isCurrentMonth
? FontWeight.w600
: FontWeight.w400,
color: isSelected color: isSelected
? Colors.white ? Colors.white
: isCurrentMonth : isCurrentMonth
@ -1093,7 +1231,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
style: TextStyle( style: TextStyle(
fontFamily: 'Pretendard', fontFamily: 'Pretendard',
fontSize: 14, fontSize: 14,
fontWeight: isSelected || isCurrentYear ? FontWeight.w600 : FontWeight.w400, fontWeight: isSelected || isCurrentYear
? FontWeight.w600
: FontWeight.w400,
color: isSelected color: isSelected
? Colors.white ? Colors.white
: isCurrentYear : isCurrentYear
@ -1109,7 +1249,10 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
} }
/// ///
Widget _buildCalendarGrid(ScheduleState state, ScheduleController controller) { Widget _buildCalendarGrid(
ScheduleState state,
ScheduleController controller,
) {
return Padding( return Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: Column(
@ -1117,7 +1260,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
children: [ children: [
// //
Row( Row(
children: ['', '', '', '', '', '', ''].asMap().entries.map((entry) { children: ['', '', '', '', '', '', ''].asMap().entries.map((
entry,
) {
final index = entry.key; final index = entry.key;
final day = entry.value; final day = entry.value;
return Expanded( return Expanded(
@ -1139,7 +1284,7 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
); );
}).toList(), }).toList(),
), ),
const SizedBox(height: 4), const SizedBox(height: 12),
// (ExpandablePageView로 ) // (ExpandablePageView로 )
ExpandablePageView.builder( ExpandablePageView.builder(
controller: _calendarPageController, controller: _calendarPageController,
@ -1177,7 +1322,7 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
); );
}, },
), ),
const SizedBox(height: 12), const SizedBox(height: 16),
// //
GestureDetector( GestureDetector(
onTap: () { onTap: () {
@ -1219,6 +1364,7 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
}) { }) {
return GridView.builder( return GridView.builder(
shrinkWrap: true, shrinkWrap: true,
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 7, crossAxisCount: 7,
@ -1233,7 +1379,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
final isSelected = controller.isSelected(date); final isSelected = controller.isSelected(date);
final isToday = controller.isToday(date); final isToday = controller.isToday(date);
final dayOfWeek = index % 7; final dayOfWeek = index % 7;
final daySchedules = isCurrentMonth ? state.getDaySchedules(date) : <Schedule>[]; final daySchedules = isCurrentMonth
? state.getDaySchedules(date)
: <Schedule>[];
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
@ -1268,7 +1416,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
style: TextStyle( style: TextStyle(
fontFamily: 'Pretendard', fontFamily: 'Pretendard',
fontSize: 14, fontSize: 14,
fontWeight: isSelected || isToday ? FontWeight.bold : FontWeight.w400, fontWeight: isSelected || isToday
? FontWeight.bold
: FontWeight.w400,
color: !isCurrentMonth color: !isCurrentMonth
? AppColors.textTertiary.withValues(alpha: 0.5) ? AppColors.textTertiary.withValues(alpha: 0.5)
: isSelected : isSelected
@ -1310,7 +1460,10 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
} }
/// ///
Widget _buildDateSelector(ScheduleState state, ScheduleController controller) { Widget _buildDateSelector(
ScheduleState state,
ScheduleController controller,
) {
return Container( return Container(
height: 80, height: 80,
color: Colors.white, color: Colors.white,
@ -1378,8 +1531,9 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
return Container( return Container(
width: 4, width: 4,
height: 4, height: 4,
margin: margin: const EdgeInsets.symmetric(
const EdgeInsets.symmetric(horizontal: 1), horizontal: 1,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: parseColor(schedule.categoryColor), color: parseColor(schedule.categoryColor),
shape: BoxShape.circle, shape: BoxShape.circle,
@ -1410,10 +1564,7 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
return Center( return Center(
child: Text( child: Text(
'${state.selectedDate.month}${state.selectedDate.day}일 일정이 없습니다', '${state.selectedDate.month}${state.selectedDate.day}일 일정이 없습니다',
style: const TextStyle( style: const TextStyle(fontSize: 14, color: AppColors.textTertiary),
fontSize: 14,
color: AppColors.textTertiary,
),
), ),
); );
} }
@ -1426,7 +1577,8 @@ class _ScheduleViewState extends ConsumerState<ScheduleView>
final schedule = state.selectedDateSchedules[index]; final schedule = state.selectedDateSchedules[index];
return Padding( return Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
bottom: index < state.selectedDateSchedules.length - 1 ? 12 : 0), bottom: index < state.selectedDateSchedules.length - 1 ? 12 : 0,
),
child: AnimatedScheduleCard( child: AnimatedScheduleCard(
key: ValueKey('${schedule.id}_${state.selectedDate.toString()}'), key: ValueKey('${schedule.id}_${state.selectedDate.toString()}'),
index: index, index: index,

View file

@ -7,6 +7,7 @@ import Foundation
import package_info_plus import package_info_plus
import path_provider_foundation import path_provider_foundation
import shared_preferences_foundation
import sqflite_darwin import sqflite_darwin
import url_launcher_macos import url_launcher_macos
import video_player_avfoundation import video_player_avfoundation
@ -15,6 +16,7 @@ import wakelock_plus
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))

View file

@ -664,6 +664,62 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.28.0" 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: shelf:
dependency: transitive dependency: transitive
description: description:

View file

@ -50,6 +50,7 @@ dependencies:
video_player: ^2.9.2 video_player: ^2.9.2
chewie: ^1.8.5 chewie: ^1.8.5
expandable_page_view: ^1.0.17 expandable_page_view: ^1.0.17
shared_preferences: ^2.3.5
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: