diff --git a/assets/translations/en.json b/assets/translations/en.json index 66a939b8..876cf46d 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -4,6 +4,7 @@ "alert": "Alert", "cancel": "Cancel", "delete": "Delete", + "add": "Add", "reset": "Reset", "search": "Search", "upload": "Upload", diff --git a/assets/translations/ko.json b/assets/translations/ko.json index 19e9ffff..e5ecf7b7 100644 --- a/assets/translations/ko.json +++ b/assets/translations/ko.json @@ -4,6 +4,7 @@ "alert": "경고", "cancel": "취소", "delete": "삭제", + "add": "추가하기", "reset": "초기화", "search": "검색", "upload": "업로드", diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 506d96fc..76e70eb4 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,52 +1,52 @@ PODS: - - Firebase/Analytics (10.6.0): + - Firebase/Analytics (10.12.0): - Firebase/Core - - Firebase/Core (10.6.0): + - Firebase/Core (10.12.0): - Firebase/CoreOnly - - FirebaseAnalytics (~> 10.6.0) - - Firebase/CoreOnly (10.6.0): - - FirebaseCore (= 10.6.0) - - Firebase/Crashlytics (10.6.0): + - FirebaseAnalytics (~> 10.12.0) + - Firebase/CoreOnly (10.12.0): + - FirebaseCore (= 10.12.0) + - Firebase/Crashlytics (10.12.0): - Firebase/CoreOnly - - FirebaseCrashlytics (~> 10.6.0) - - firebase_analytics (10.1.6): - - Firebase/Analytics (= 10.6.0) + - FirebaseCrashlytics (~> 10.12.0) + - firebase_analytics (10.4.4): + - Firebase/Analytics (= 10.12.0) - firebase_core - Flutter - - firebase_core (2.8.0): - - Firebase/CoreOnly (= 10.6.0) + - firebase_core (2.15.0): + - Firebase/CoreOnly (= 10.12.0) - Flutter - - firebase_crashlytics (3.0.17): - - Firebase/Crashlytics (= 10.6.0) + - firebase_crashlytics (3.3.4): + - Firebase/Crashlytics (= 10.12.0) - firebase_core - Flutter - - FirebaseAnalytics (10.6.0): - - FirebaseAnalytics/AdIdSupport (= 10.6.0) + - FirebaseAnalytics (10.12.0): + - FirebaseAnalytics/AdIdSupport (= 10.12.0) - FirebaseCore (~> 10.0) - FirebaseInstallations (~> 10.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.8) - - GoogleUtilities/MethodSwizzler (~> 7.8) - - GoogleUtilities/Network (~> 7.8) - - "GoogleUtilities/NSData+zlib (~> 7.8)" + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" - nanopb (< 2.30910.0, >= 2.30908.0) - - FirebaseAnalytics/AdIdSupport (10.6.0): + - FirebaseAnalytics/AdIdSupport (10.12.0): - FirebaseCore (~> 10.0) - FirebaseInstallations (~> 10.0) - - GoogleAppMeasurement (= 10.6.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.8) - - GoogleUtilities/MethodSwizzler (~> 7.8) - - GoogleUtilities/Network (~> 7.8) - - "GoogleUtilities/NSData+zlib (~> 7.8)" + - GoogleAppMeasurement (= 10.12.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" - nanopb (< 2.30910.0, >= 2.30908.0) - - FirebaseCore (10.6.0): + - FirebaseCore (10.12.0): - FirebaseCoreInternal (~> 10.0) - GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/Logger (~> 7.8) - - FirebaseCoreExtension (10.10.0): + - FirebaseCoreExtension (10.13.0): - FirebaseCore (~> 10.0) - - FirebaseCoreInternal (10.10.0): + - FirebaseCoreInternal (10.13.0): - "GoogleUtilities/NSData+zlib (~> 7.8)" - - FirebaseCrashlytics (10.6.0): + - FirebaseCrashlytics (10.12.0): - FirebaseCore (~> 10.5) - FirebaseInstallations (~> 10.0) - FirebaseSessions (~> 10.5) @@ -54,12 +54,12 @@ PODS: - GoogleUtilities/Environment (~> 7.8) - nanopb (< 2.30910.0, >= 2.30908.0) - PromisesObjC (~> 2.1) - - FirebaseInstallations (10.10.0): + - FirebaseInstallations (10.13.0): - FirebaseCore (~> 10.0) - GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/UserDefaults (~> 7.8) - PromisesObjC (~> 2.1) - - FirebaseSessions (10.10.0): + - FirebaseSessions (10.13.0): - FirebaseCore (~> 10.5) - FirebaseCoreExtension (~> 10.0) - FirebaseInstallations (~> 10.0) @@ -72,48 +72,48 @@ PODS: - Flutter - flutter_widgetkit (0.0.1): - Flutter - - GoogleAppMeasurement (10.6.0): - - GoogleAppMeasurement/AdIdSupport (= 10.6.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.8) - - GoogleUtilities/MethodSwizzler (~> 7.8) - - GoogleUtilities/Network (~> 7.8) - - "GoogleUtilities/NSData+zlib (~> 7.8)" + - GoogleAppMeasurement (10.12.0): + - GoogleAppMeasurement/AdIdSupport (= 10.12.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleAppMeasurement/AdIdSupport (10.6.0): - - GoogleAppMeasurement/WithoutAdIdSupport (= 10.6.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.8) - - GoogleUtilities/MethodSwizzler (~> 7.8) - - GoogleUtilities/Network (~> 7.8) - - "GoogleUtilities/NSData+zlib (~> 7.8)" + - GoogleAppMeasurement/AdIdSupport (10.12.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 10.12.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleAppMeasurement/WithoutAdIdSupport (10.6.0): - - GoogleUtilities/AppDelegateSwizzler (~> 7.8) - - GoogleUtilities/MethodSwizzler (~> 7.8) - - GoogleUtilities/Network (~> 7.8) - - "GoogleUtilities/NSData+zlib (~> 7.8)" + - GoogleAppMeasurement/WithoutAdIdSupport (10.12.0): + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleDataTransport (9.2.3): + - GoogleDataTransport (9.2.5): - GoogleUtilities/Environment (~> 7.7) - nanopb (< 2.30910.0, >= 2.30908.0) - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/AppDelegateSwizzler (7.11.1): + - GoogleUtilities/AppDelegateSwizzler (7.11.5): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (7.11.1): + - GoogleUtilities/Environment (7.11.5): - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Logger (7.11.1): + - GoogleUtilities/Logger (7.11.5): - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (7.11.1): + - GoogleUtilities/MethodSwizzler (7.11.5): - GoogleUtilities/Logger - - GoogleUtilities/Network (7.11.1): + - GoogleUtilities/Network (7.11.5): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (7.11.1)" - - GoogleUtilities/Reachability (7.11.1): + - "GoogleUtilities/NSData+zlib (7.11.5)" + - GoogleUtilities/Reachability (7.11.5): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (7.11.1): + - GoogleUtilities/UserDefaults (7.11.5): - GoogleUtilities/Logger - nanopb (2.30909.0): - nanopb/decode (= 2.30909.0) @@ -127,9 +127,9 @@ PODS: - FlutterMacOS - "permission_handler (5.1.0+2)": - Flutter - - PromisesObjC (2.2.0) - - PromisesSwift (2.2.0): - - PromisesObjC (= 2.2.0) + - PromisesObjC (2.3.1) + - PromisesSwift (2.3.1): + - PromisesObjC (= 2.3.1) - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -201,30 +201,30 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/webview_flutter_wkwebview/ios" SPEC CHECKSUMS: - Firebase: f13680471b021937f2230ea8503c7809d8c29806 - firebase_analytics: 97d12c9683531ba3f923a798a95362e7590f0757 - firebase_core: 58542d7399889ebdbb034baa72d081e54c5c814d - firebase_crashlytics: 5f6296621a0e8ed7d15a7499c8131fbdcf176e3b - FirebaseAnalytics: 9f382605c5ee412b039212f054bf7a403d9850c1 - FirebaseCore: fa80ad16a62d52f67274b5b88304c3a318bbf9a4 - FirebaseCoreExtension: 8d93ebbf6838a874b4ed82a564c9d6705f8365dd - FirebaseCoreInternal: 971029061d326000d65bfdc21f5502c75c8b0893 - FirebaseCrashlytics: ede07e7f433a0a2270112baf2d156b111cfb422d - FirebaseInstallations: 52153982b057d3afcb4e1fbb3eb0b6d00611e681 - FirebaseSessions: 5f9e62cd4096e24d2011cbd845b0efceffaee1ec + Firebase: 07150e75d142fb9399f6777fa56a187b17f833a0 + firebase_analytics: 3ff822ee2e90f95b61f0da300df20b378d380fbb + firebase_core: e477125798fc37cd4ab43ca6a8536bf7e0929c00 + firebase_crashlytics: 6043ce85800f96e53f15ee5051f9cfad10cce73d + FirebaseAnalytics: 0270389efbe3022b54ec4588862dabec3477ee98 + FirebaseCore: f86a1394906b97ac445ae49c92552a9425831bed + FirebaseCoreExtension: ce60f9db46d83944cf444664d6d587474128eeca + FirebaseCoreInternal: b342e37cd4f5b4454ec34308f073420e7920858e + FirebaseCrashlytics: c4d111b7430c49744c74bcc6346ea00868661ac8 + FirebaseInstallations: b28af1b9f997f1a799efe818c94695a3728c352f + FirebaseSessions: 991fb4c20b3505eef125f7cbfa20a5b5b189c2a4 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_web_browser: 7bccaafbb0c5b8862afe7bcd158f15557109f61f flutter_widgetkit: a0e9b0d50ee9bec366dad36c639509daaafc397a - GoogleAppMeasurement: 686b48c3c895f3c55c70719041913d5d150b74f6 - GoogleDataTransport: f0308f5905a745f94fb91fea9c6cbaf3831cb1bd - GoogleUtilities: 9aa0ad5a7bc171f8bae016300bfcfa3fb8425749 + GoogleAppMeasurement: 2d800fab85e7848b1e66a6f8ce5bca06c5aad892 + GoogleDataTransport: 54dee9d48d14580407f8f5fbf2f496e92437a2f2 + GoogleUtilities: 13e2c67ede716b8741c7989e26893d151b2b2084 nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 open_app_file: 205f73051668bfbd68356005fef8a62e620f0a77 - path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0 - PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef - PromisesSwift: cf9eb58666a43bbe007302226e510b16c1e10959 - shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 + PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 + PromisesSwift: 28dca69a9c40779916ac2d6985a0192a5cb4a265 + shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4 webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7 webview_flutter_wkwebview: b7e70ef1ddded7e69c796c7390ee74180182971f diff --git a/lib/home.dart b/lib/home.dart index 2451880b..c0f74732 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -1,14 +1,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:otlplus/providers/hall_of_fame_model.dart'; +import 'package:otlplus/providers/course_search_model.dart'; import 'package:otlplus/providers/lecture_search_model.dart'; import 'package:otlplus/providers/timetable_model.dart'; import 'package:otlplus/utils/build_app_bar.dart'; import 'package:otlplus/utils/build_page_route.dart'; -import 'package:otlplus/providers/course_search_model.dart'; import 'package:otlplus/widgets/hall_of_fame_control.dart'; import 'package:otlplus/widgets/review_mode_control.dart'; import 'package:otlplus/widgets/timetable_mode_control.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:otlplus/widgets/pop_up.dart'; import 'package:otlplus/widgets/semester_picker.dart'; import 'package:provider/provider.dart'; @@ -131,14 +132,12 @@ class _OTLHomeState extends State with SingleTickerProviderStateMixin { PreferredSizeWidget _buildDictionaryAppBar() { return AppBar( - title: appBarPadding( - GestureDetector( + title: appBarPadding(ClipRRect( + borderRadius: BorderRadius.circular(8.0), + child: BackgroundButton( + tapEffectColorRatio: 0.04, onTap: () => Navigator.push(context, buildCourseSearchPageRoute()), - child: Container( - decoration: BoxDecoration( - color: OTLColor.grayF, - borderRadius: BorderRadius.circular(8.0), - ), + child: Padding( padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), child: Row( children: [ @@ -154,8 +153,9 @@ class _OTLHomeState extends State with SingleTickerProviderStateMixin { ], ), ), + color: OTLColor.grayF, ), - ), + )), flexibleSpace: SafeArea(child: Container(color: OTLColor.pinksMain, height: 5.0)), toolbarHeight: kToolbarHeight + 5.0, @@ -259,6 +259,9 @@ class _OTLHomeState extends State with SingleTickerProviderStateMixin { BottomNavigationBar _buildBottomNavigationBar() { return BottomNavigationBar( + selectedFontSize: 12.0, + unselectedFontSize: 12.0, + enableFeedback: false, currentIndex: _currentIndex, type: BottomNavigationBarType.fixed, showSelectedLabels: true, diff --git a/lib/pages/course_detail_page.dart b/lib/pages/course_detail_page.dart index 916d2564..fa7247a1 100644 --- a/lib/pages/course_detail_page.dart +++ b/lib/pages/course_detail_page.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:otlplus/constants/text_styles.dart'; import 'package:otlplus/models/review.dart'; import 'package:otlplus/utils/build_app_bar.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/extensions/course.dart'; @@ -95,32 +96,29 @@ class CourseDetailPage extends StatelessWidget { return SliverPersistentHeader( pinned: true, delegate: CustomHeaderDelegate( - height: 24.0, - padding: const EdgeInsets.only(bottom: 4.0), - onTap: (shrinkOffset) async { - if (shrinkOffset > 0) { - _scrollController.animateTo(0, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut); - } else { - await Scrollable.ensureVisible(headerKey.currentContext!, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut); - _scrollController.jumpTo(_scrollController.offset + 2); - } - }, - builder: (shrinkOffset) => Row( - key: headerKey, - children: [ - Text("dictionary.reviews".tr(), style: bodyBold), - FittedBox( - child: (shrinkOffset > 0) - ? const Icon(Icons.keyboard_arrow_up) - : const Icon(Icons.keyboard_arrow_down), - ), - ], - ), - ), + height: 24.0, + padding: const EdgeInsets.only(bottom: 4.0), + builder: (shrinkOffset) => IconTextButton( + direction: 'row-reversed', + padding: EdgeInsets.zero, + onTap: () async { + if (shrinkOffset > 0) { + _scrollController.animateTo(0, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut); + } else { + await Scrollable.ensureVisible(headerKey.currentContext!, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut); + _scrollController.jumpTo(_scrollController.offset + 2); + } + }, + key: headerKey, + text: "dictionary.reviews".tr(), + textStyle: bodyBold, + icon: (shrinkOffset > 0) + ? Icons.keyboard_arrow_up + : Icons.keyboard_arrow_down)), ); } diff --git a/lib/pages/course_search_page.dart b/lib/pages/course_search_page.dart index 08d77bea..7d98db53 100644 --- a/lib/pages/course_search_page.dart +++ b/lib/pages/course_search_page.dart @@ -4,6 +4,7 @@ import 'package:otlplus/constants/color.dart'; import 'package:otlplus/constants/text_styles.dart'; import 'package:otlplus/providers/course_search_model.dart'; import 'package:otlplus/utils/build_app_bar.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:otlplus/widgets/search_filter_panel.dart'; import 'package:otlplus/widgets/search_textfield.dart'; import 'package:provider/provider.dart'; @@ -46,9 +47,10 @@ class _CourseSearchPageState extends State { title: appBarPadding( Row( children: [ - IconButton( - onPressed: () => Navigator.pop(context), - icon: const Icon(Icons.navigate_before), + IconTextButton( + onTap: () => Navigator.pop(context), + icon: Icons.navigate_before, + padding: EdgeInsets.fromLTRB(0, 16, 16, 16), ), Expanded( child: SearchTextfield( diff --git a/lib/pages/lecture_detail_page.dart b/lib/pages/lecture_detail_page.dart index 284c6507..541a6ed0 100644 --- a/lib/pages/lecture_detail_page.dart +++ b/lib/pages/lecture_detail_page.dart @@ -5,6 +5,7 @@ import 'package:otlplus/constants/text_styles.dart'; import 'package:otlplus/models/review.dart'; import 'package:otlplus/utils/build_app_bar.dart'; import 'package:otlplus/utils/build_page_route.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/extensions/lecture.dart'; @@ -92,7 +93,8 @@ class LectureDetailPage extends StatelessWidget { final isAdded = context.select( (model) => model.currentTimetable.lectures.contains(lecture)); - return InkWell( + return IconTextButton( + padding: const EdgeInsets.all(0), onTap: () { final timetableModel = context.read(); @@ -112,16 +114,20 @@ class LectureDetailPage extends StatelessWidget { content: const Text( "시간이 겹치는 수업이 있습니다. 추가하시면 해당 수업은 삭제됩니다.\n시간표에 추가하시겠습니까?"), actions: [ - TextButton( - child: const Text("취소"), - onPressed: () { + IconTextButton( + padding: EdgeInsets.all(12), + text: 'common.cancel'.tr(), + color: OTLColor.pinksMain, + onTap: () { result = false; Navigator.pop(context); }, ), - TextButton( - child: const Text("추가하기"), - onPressed: () { + IconTextButton( + padding: EdgeInsets.all(12), + text: 'common.add'.tr(), + color: OTLColor.pinksMain, + onTap: () { result = true; Navigator.pop(context); }, @@ -135,25 +141,11 @@ class LectureDetailPage extends StatelessWidget { ); } }, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - isAdded - ? const Icon( - Icons.close, - size: 14.0, - ) - : const Icon( - Icons.add, - size: 14.0, - ), - const SizedBox(width: 4.0), - Text( - isAdded ? "시간표에서 제거" : "시간표에 추가", - style: const TextStyle(fontSize: 12.0), - ), - ], - ), + text: isAdded ? "시간표에서 제거" : "시간표에 추가", + textStyle: const TextStyle(fontSize: 12.0), + icon: isAdded ? Icons.close : Icons.add, + iconSize: 14, + spaceBetween: 4.0, ); } @@ -161,18 +153,16 @@ class LectureDetailPage extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - InkWell( + IconTextButton( onTap: () { context.read().loadCourse(lecture.course); Navigator.push(context, buildCourseDetailPageRoute()); }, - child: Text( - "dictionary.dictionary".tr(), - style: bodyRegular.copyWith(color: OTLColor.pinksMain), - ), + text: "dictionary.dictionary".tr(), + textStyle: bodyRegular.copyWith(color: OTLColor.pinksMain), ), const SizedBox(width: 8.0), - InkWell( + IconTextButton( onTap: () => FlutterWebBrowser.openWebPage( url: _getSyllabusUrl(lecture), customTabsOptions: CustomTabsOptions( @@ -190,10 +180,8 @@ class LectureDetailPage extends StatelessWidget { modalPresentationCapturesStatusBarAppearance: true, ), ), - child: Text( - "dictionary.syllabus".tr(), - style: bodyRegular.copyWith(color: OTLColor.pinksMain), - ), + text: "dictionary.syllabus".tr(), + textStyle: bodyRegular.copyWith(color: OTLColor.pinksMain), ), ], ); @@ -223,29 +211,27 @@ class LectureDetailPage extends StatelessWidget { delegate: CustomHeaderDelegate( height: 24.0, padding: const EdgeInsets.only(bottom: 4.0), - onTap: (shrinkOffset) async { - if (shrinkOffset > 0) { - _scrollController.animateTo(0, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut); - } else { - await Scrollable.ensureVisible(headerKey.currentContext!, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut); - _scrollController.jumpTo(_scrollController.offset + 2); - } - }, - builder: (shrinkOffset) => Row( - key: headerKey, - children: [ - Text("dictionary.reviews".tr(), style: bodyBold), - FittedBox( - child: (shrinkOffset > 0) - ? const Icon(Icons.keyboard_arrow_up) - : const Icon(Icons.keyboard_arrow_down), - ), - ], - ), + builder: (shrinkOffset) => IconTextButton( + direction: 'row-reversed', + padding: EdgeInsets.zero, + onTap: () async { + if (shrinkOffset > 0) { + _scrollController.animateTo(0, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut); + } else { + await Scrollable.ensureVisible(headerKey.currentContext!, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut); + _scrollController.jumpTo(_scrollController.offset + 2); + } + }, + key: headerKey, + text: "dictionary.reviews".tr(), + textStyle: bodyBold, + icon: (shrinkOffset > 0) + ? Icons.keyboard_arrow_up + : Icons.keyboard_arrow_down), ), ); } diff --git a/lib/pages/lecture_search_page.dart b/lib/pages/lecture_search_page.dart index c96c81ed..82608d03 100644 --- a/lib/pages/lecture_search_page.dart +++ b/lib/pages/lecture_search_page.dart @@ -5,6 +5,7 @@ import 'package:otlplus/constants/text_styles.dart'; import 'package:otlplus/providers/lecture_search_model.dart'; import 'package:otlplus/providers/timetable_model.dart'; import 'package:otlplus/utils/build_app_bar.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:otlplus/widgets/search_filter_panel.dart'; import 'package:otlplus/widgets/search_textfield.dart'; import 'package:provider/provider.dart'; @@ -47,9 +48,10 @@ class _LectureSearchPageState extends State { title: appBarPadding( Row( children: [ - IconButton( - onPressed: () => Navigator.pop(context), - icon: const Icon(Icons.navigate_before), + IconTextButton( + onTap: () => Navigator.pop(context), + icon: Icons.navigate_before, + padding: EdgeInsets.fromLTRB(0, 16, 16, 16), ), Expanded( child: SearchTextfield( diff --git a/lib/pages/main_page.dart b/lib/pages/main_page.dart index 030b7c1b..f4c6ba5f 100644 --- a/lib/pages/main_page.dart +++ b/lib/pages/main_page.dart @@ -3,6 +3,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:otlplus/constants/text_styles.dart'; import 'package:otlplus/providers/course_search_model.dart'; import 'package:otlplus/utils/build_page_route.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/models/semester.dart'; @@ -54,95 +55,95 @@ class _MainPageState extends State { child: Stack( alignment: Alignment.topCenter, children: [ - Padding( - padding: EdgeInsets.all(14.0), - child: SizedBox( - height: 28, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Image.asset( + SizedBox( + height: 56, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 14.0), + child: Image.asset( "assets/images/logo.png", height: 27.0, ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () => Navigator.push( - context, buildUserPageRoute()), - child: SvgPicture.asset( - 'assets/icons/person.svg', - height: 24.0, - width: 24.0, - colorFilter: ColorFilter.mode( - OTLColor.pinksMain, BlendMode.srcIn)), - ), - SizedBox(width: 16.0), - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () => Navigator.push( - context, buildSettingsPageRoute()), - child: SvgPicture.asset( - 'assets/icons/gear.svg', - height: 24.0, - width: 24.0, - colorFilter: ColorFilter.mode( - OTLColor.pinksMain, BlendMode.srcIn)), - ), - ], - ) - ], - ), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconTextButton( + onTap: () => Navigator.push( + context, buildUserPageRoute()), + icon: 'assets/icons/person.svg', + iconSize: 24, + color: OTLColor.pinksMain, + tapEffect: 'darken', + padding: + EdgeInsets.fromLTRB(16.0, 16.0, 8.0, 16.0), + ), + IconTextButton( + onTap: () => Navigator.push( + context, buildSettingsPageRoute()), + icon: 'assets/icons/gear.svg', + iconSize: 24, + color: OTLColor.pinksMain, + tapEffect: 'darken', + padding: + EdgeInsets.fromLTRB(8.0, 16.0, 16.0, 16.0), + ), + ], + ) + ], ), ), Center( child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - context - .read() - .resetCourseFilter(); - Navigator.push( - context, buildCourseSearchPageRoute()) - .then((e) { - if (e == true) { - widget.changeIndex(2); - } - }); - }, - child: Container( - decoration: BoxDecoration( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: ClipRRect( + borderRadius: BorderRadius.circular(8.0), + child: BackgroundButton( + tapEffectColorRatio: 0.04, + onTap: () { + context + .read() + .resetCourseFilter(); + Navigator.push( + context, buildCourseSearchPageRoute()) + .then((e) { + if (e == true) { + widget.changeIndex(2); + } + }); + }, + tapEffect: 'darken', color: OTLColor.grayF, - borderRadius: BorderRadius.circular(8.0), - ), - padding: EdgeInsets.symmetric( - horizontal: 12.0, vertical: 6.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SvgPicture.asset('assets/icons/search.svg', - height: 24.0, - width: 24.0, - colorFilter: ColorFilter.mode( - OTLColor.pinksMain, BlendMode.srcIn)), - const SizedBox(width: 12.0), - Expanded( - child: Text( - "common.search_hint".tr(), - style: evenBodyRegular.copyWith( - color: OTLColor.grayA), - ), + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 12.0, vertical: 6.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SvgPicture.asset('assets/icons/search.svg', + height: 24.0, + width: 24.0, + colorFilter: ColorFilter.mode( + OTLColor.pinksMain, + BlendMode.srcIn)), + const SizedBox(width: 12.0), + Expanded( + child: Text( + "common.search_hint".tr(), + style: evenBodyRegular.copyWith( + color: OTLColor.grayA, + height: 1.24), + ), + ), + ], ), - ], + ), ), - ), - ), - ), + )), ), ], ), @@ -152,41 +153,87 @@ class _MainPageState extends State { borderRadius: BorderRadius.vertical(top: Radius.circular(16.0)), child: Container( - constraints: const BoxConstraints.expand(), - child: ColoredBox( - color: Colors.white, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 16.0, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - children: [ - _buildTimetable(infoModel.user, semester, now), - const SizedBox(height: 24.0), - _buildDivider(), - const SizedBox(height: 24.0), - _buildSchedule(now, infoModel.currentSchedule), - const SizedBox(height: 24.0), - _buildDivider(), - ], + constraints: const BoxConstraints.expand(), + child: CustomScrollView( + reverse: true, + slivers: [ + SliverFillRemaining( + hasScrollBody: false, + child: ColoredBox( + color: Colors.white, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 16.0, + ), + child: Column( + children: [ + Column( + children: [ + _buildTimetable( + infoModel.user, semester, now), + const SizedBox(height: 24.0), + _buildDivider(), + const SizedBox(height: 24.0), + _buildSchedule( + now, infoModel.currentSchedule), + const SizedBox(height: 24.0), + _buildDivider(), + ], + ), + Spacer(), + Column( + children: [ + _buildLogo(), + const SizedBox(height: 4.0), + _buildCopyRight(), + _buildTextButtons(context), + ], + ) + ], + ), + ), ), - Column( - children: [ - _buildLogo(), - const SizedBox(height: 4.0), - _buildCopyRight(), - _buildTextButtons(context), - ], - ) - ], - ), + ), + ], + ) + + // SingleChildScrollView( + // child: ColoredBox( + // color: Colors.white, + // child: Padding( + // padding: const EdgeInsets.symmetric( + // horizontal: 16.0, + // vertical: 16.0, + // ), + // child: Column( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Column( + // children: [ + // _buildTimetable(infoModel.user, semester, now), + // const SizedBox(height: 24.0), + // _buildDivider(), + // const SizedBox(height: 24.0), + // _buildSchedule(now, infoModel.currentSchedule), + // const SizedBox(height: 24.0), + // _buildDivider(), + // ], + // ), + // Column( + // children: [ + // _buildLogo(), + // const SizedBox(height: 4.0), + // _buildCopyRight(), + // _buildTextButtons(context), + // ], + // ) + // ], + // ), + // ), + // ), + // ), ), - ), - ), ), ), ], @@ -204,23 +251,23 @@ class _MainPageState extends State { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - TextButton( - onPressed: () { + IconTextButton( + onTap: () { Navigator.push(context, buildPrivacyPageRoute()); }, - child: Text( - 'title.privacy'.tr(), - style: labelRegular.copyWith(color: OTLColor.gray75), - ), + text: 'title.privacy'.tr(), + textStyle: labelRegular.copyWith(color: OTLColor.gray75), + padding: EdgeInsets.symmetric(vertical: 12.0, horizontal: 8.0), + tapEffect: 'lighten', ), - TextButton( - onPressed: () { + IconTextButton( + onTap: () { Navigator.push(context, buildPeoplePageRoute()); }, - child: Text( - 'title.credit'.tr(), - style: labelRegular.copyWith(color: OTLColor.gray75), - ), + text: 'title.credit'.tr(), + textStyle: labelRegular.copyWith(color: OTLColor.gray75), + padding: EdgeInsets.symmetric(vertical: 12.0, horizontal: 8.0), + tapEffect: 'lighten', ), ], ); @@ -247,14 +294,15 @@ class _MainPageState extends State { ); } - Widget _buildSchedule(DateTime now, Map? currentSchedule) { - final isEn = EasyLocalization.of(context)?.currentLocale == Locale('en'); - late int days, hours, minutes; + List getRemainedTime(Duration timeDiff) { + final days = timeDiff.inDays; + final hours = timeDiff.inHours - timeDiff.inDays * 24; + final minutes = timeDiff.inMinutes - timeDiff.inHours * 60; + return [days.toString(), hours.toString(), minutes.toString()]; + } - final timeDiff = currentSchedule?["time"].difference(now) as Duration; - days = timeDiff.inDays; - hours = timeDiff.inHours - timeDiff.inDays * 24; - minutes = timeDiff.inMinutes - timeDiff.inHours * 60; + Widget _buildSchedule(DateTime now, Map? currentSchedule) { + final isEn = EasyLocalization.of(context)!.currentLocale == Locale('en'); return Column( crossAxisAlignment: CrossAxisAlignment.center, @@ -262,11 +310,9 @@ class _MainPageState extends State { Text( (currentSchedule == null) ? "common.no_info".tr() - : "home.remained_datetime".tr(args: [ - days.toString(), - hours.toString(), - minutes.toString() - ]), + : "home.remained_datetime".tr( + args: getRemainedTime( + currentSchedule["time"].difference(now) as Duration)), style: titleRegular, // ignore: unnecessary_null_comparison ), diff --git a/lib/pages/settings_page.dart b/lib/pages/settings_page.dart index 1aebaed6..c7ca0c8d 100644 --- a/lib/pages/settings_page.dart +++ b/lib/pages/settings_page.dart @@ -4,6 +4,7 @@ import 'package:otlplus/constants/color.dart'; import 'package:otlplus/constants/text_styles.dart'; import 'package:otlplus/providers/settings_model.dart'; import 'package:otlplus/utils/build_app_bar.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:flutter/foundation.dart' show kDebugMode; @@ -120,12 +121,11 @@ class SettingsPage extends StatelessWidget { "Online Timeplanner with Lectures Plus @ KAIST", style: bodyRegular, ), - InkWell( + IconTextButton( + padding: EdgeInsets.fromLTRB(0, 4, 10, 4), onTap: () => launchUrl(Uri.parse("mailto:$contactEmail")), - child: Text( - contactEmail, - style: bodyRegular.copyWith(color: OTLColor.pinksMain), - ), + text: contactEmail, + textStyle: bodyRegular.copyWith(color: OTLColor.pinksMain), ) ], ), diff --git a/lib/pages/timetable_page.dart b/lib/pages/timetable_page.dart index 43a84165..95fcd4fb 100644 --- a/lib/pages/timetable_page.dart +++ b/lib/pages/timetable_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:otlplus/utils/build_page_route.dart'; import 'package:otlplus/providers/lecture_search_model.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:otlplus/widgets/lecture_search.dart'; import 'package:otlplus/widgets/map_view.dart'; import 'package:provider/provider.dart'; @@ -192,16 +193,20 @@ class _TimetablePageState extends State { args: [lecture.title], ), actions: [ - TextButton( - child: Text("common.cancel".tr()), - onPressed: () { + IconTextButton( + padding: EdgeInsets.all(12), + text: 'common.cancel'.tr(), + color: OTLColor.pinksMain, + onTap: () { result = false; Navigator.pop(context); }, ), - TextButton( - child: Text("common.delete".tr()), - onPressed: () { + IconTextButton( + padding: EdgeInsets.all(12), + text: 'common.delete'.tr(), + color: OTLColor.pinksMain, + onTap: () { result = true; Navigator.pop(context); }, diff --git a/lib/pages/user_page.dart b/lib/pages/user_page.dart index 3392caf4..119f097a 100644 --- a/lib/pages/user_page.dart +++ b/lib/pages/user_page.dart @@ -1,10 +1,10 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:otlplus/constants/text_styles.dart'; import 'package:otlplus/providers/auth_model.dart'; import 'package:otlplus/utils/build_app_bar.dart'; import 'package:otlplus/utils/build_page_route.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/providers/info_model.dart'; @@ -18,26 +18,61 @@ class UserPage extends StatelessWidget { return Scaffold( appBar: buildAppBar(context, 'title.my_information'.tr(), false, true), body: Padding( - padding: const EdgeInsets.all(16.0), + padding: const EdgeInsets.symmetric(vertical: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - _buildContent("user.name", "${user.firstName} ${user.lastName}"), - _buildContent("user.email", user.email), - _buildContent("user.student_id", user.studentId), - _buildContent( - "user.major", - user.majors - .map( - (department) => isEn ? department.nameEn : department.name, - ) - .join(", "), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Column( + children: [ + _buildContent( + "user.name", "${user.firstName} ${user.lastName}"), + _buildContent("user.email", user.email), + _buildContent("user.student_id", user.studentId), + _buildContent( + "user.major", + user.majors + .map( + (department) => + isEn ? department.nameEn : department.name, + ) + .join(", "), + ), + _buildDivider(), + ], + ), + ), + _buildNavigateArrowButton( + context, + 'assets/icons/my_review.svg', + 'user.my_review'.tr(), + () => Navigator.push(context, buildMyReviewPageRoute())), + _buildNavigateArrowButton( + context, + 'assets/icons/liked_review.svg', + 'user.liked_review'.tr(), + () => Navigator.push(context, buildLikedReviewPageRoute())), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: _buildDivider(), + ), + Align( + alignment: Alignment.centerLeft, + child: IconTextButton( + icon: 'assets/icons/logout.svg', + onTap: () { + context.read().logout(); + context.read().logout(); + Navigator.pop(context); + }, + text: 'user.logout'.tr(), + color: OTLColor.pinksMain, + textStyle: bodyBold, + spaceBetween: 8.0, + padding: EdgeInsets.symmetric(horizontal: 16.0), + ), ), - _buildDivider(), - _buildMyReviewButton(context), - _buildLikedReviewButton(context), - _buildDivider(), - _buildLogoutButton(context), ], ), ), @@ -61,71 +96,58 @@ class UserPage extends StatelessWidget { ); } - Widget _buildMyReviewButton(BuildContext context) { - return InkWell( - onTap: () => Navigator.push(context, buildMyReviewPageRoute()), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SvgPicture.asset('assets/icons/my_review.svg', height: 24.0), - const SizedBox(width: 8), - Text( - 'user.my_review'.tr(), - style: bodyBold.copyWith(color: OTLColor.pinksMain), - ), - const Expanded(child: SizedBox()), - Icon(Icons.navigate_next, color: OTLColor.pinksMain), - ], - ), - ), - ); - } - - Widget _buildLikedReviewButton(BuildContext context) { - return InkWell( - onTap: () => Navigator.push(context, buildLikedReviewPageRoute()), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SvgPicture.asset('assets/icons/liked_review.svg', height: 24.0), - const SizedBox(width: 8), - Text( - 'user.liked_review'.tr(), - style: bodyBold.copyWith(color: OTLColor.pinksMain), - ), - const Expanded(child: SizedBox()), - Icon(Icons.navigate_next, color: OTLColor.pinksMain), - ], - ), - ), - ); - } - - Widget _buildLogoutButton(BuildContext context) { - return InkWell( - onTap: () { - context.read().logout(); - context.read().logout(); - Navigator.pop(context); + Widget _buildNavigateArrowButton( + BuildContext context, String icon, String text, VoidCallback onTap) { + return IconTextButtonRaw( + data: { + 'Padding': { + 'padding': EdgeInsets.symmetric(horizontal: 16.0), + 'child': { + 'SizedBox': { + 'height': 36.0, + 'child': { + 'Row': { + 'children': [ + { + 'SvgPicture.asset': { + 'arg': icon, + 'height': 24.0, + 'width': 24.0, + 'color': OTLColor.pinksMain + } + }, + { + 'Padding': { + 'padding': EdgeInsets.symmetric(horizontal: 8.0), + 'child': { + 'Text': { + 'arg': text, + 'style': + bodyBold.copyWith(color: OTLColor.pinksMain), + } + }, + } + }, + {'Spacer': {}}, + { + 'Padding': { + 'padding': EdgeInsets.fromLTRB(16, 6, 0, 6), + 'child': { + 'Icon': { + 'arg': Icons.navigate_next, + 'color': OTLColor.pinksMain, + } + } + } + } + ] + } + } + } + } + } }, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SvgPicture.asset('assets/icons/logout.svg', height: 24.0), - const SizedBox(width: 8), - Text( - 'user.logout'.tr(), - style: bodyBold.copyWith(color: OTLColor.pinksMain), - ), - ], - ), - ), + onTap: onTap, ); } } diff --git a/lib/providers/course_search_model.dart b/lib/providers/course_search_model.dart index 62322512..7e98d9bd 100644 --- a/lib/providers/course_search_model.dart +++ b/lib/providers/course_search_model.dart @@ -166,7 +166,7 @@ class CourseSearchModel extends ChangeNotifier { } else { _courseSearchquery = Text.rich( TextSpan( - style: bodyRegular.copyWith(color: OTLColor.grayA), + style: bodyRegular.copyWith(color: OTLColor.grayA, height: 1.24), children: [ TextSpan( text: _courseSearchText.isEmpty ? '' : '"$_courseSearchText"', diff --git a/lib/providers/lecture_search_model.dart b/lib/providers/lecture_search_model.dart index f1774435..22c03c6c 100644 --- a/lib/providers/lecture_search_model.dart +++ b/lib/providers/lecture_search_model.dart @@ -142,7 +142,7 @@ class LectureSearchModel extends ChangeNotifier { .map((i) => i.label)))).values.expand((i) => i).toList(); _lectureSearchquery = Text.rich( TextSpan( - style: bodyRegular.copyWith(color: OTLColor.grayA), + style: bodyRegular.copyWith(color: OTLColor.grayA, height: 1.24), children: [ TextSpan( text: _lectureSearchText.isEmpty ? '' : '"$_lectureSearchText"', diff --git a/lib/utils/build_app_bar.dart b/lib/utils/build_app_bar.dart index cba0ac02..92483731 100644 --- a/lib/utils/build_app_bar.dart +++ b/lib/utils/build_app_bar.dart @@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/constants/text_styles.dart'; +import 'package:otlplus/utils/responsive_button.dart'; PreferredSizeWidget buildAppBar( BuildContext context, @@ -13,21 +14,23 @@ PreferredSizeWidget buildAppBar( title: appBarPadding(Text(title.tr(), style: titleBold)), leading: isLeading ? appBarPadding( - IconButton( - onPressed: () => Navigator.pop(context), - icon: const Icon(Icons.navigate_before), + IconTextButton( + onTap: () => Navigator.pop(context), + icon: Icons.navigate_before, + padding: EdgeInsets.all(16), ), ) : null, actions: isActions ? [ appBarPadding( - IconButton( - icon: const Icon(Icons.close), - onPressed: () => Navigator.popUntil( + IconTextButton( + icon: Icons.close, + onTap: () => Navigator.popUntil( context, (route) => route.isFirst, ), + padding: EdgeInsets.all(16), ), ), ] diff --git a/lib/utils/responsive_button.dart b/lib/utils/responsive_button.dart new file mode 100644 index 00000000..b5a258e7 --- /dev/null +++ b/lib/utils/responsive_button.dart @@ -0,0 +1,355 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class IconTextButton extends StatefulWidget { + const IconTextButton({ + Key? key, + this.color = const Color(0xFF000000), + this.icon, + this.iconSize = 24, + this.spaceBetween = 0, + this.text, + this.textStyle = const TextStyle(), + this.padding, + this.onTap, + this.tapEffect = 'lighten', + this.tapEffectColorRatio = 0.48, + this.direction = 'row', + }) : assert(icon is IconData || icon is String || icon == null), + assert(icon != null || iconSize == 24), + assert(text != null || textStyle == const TextStyle()), + assert(tapEffect == 'none' || + tapEffect == 'darken' || + tapEffect == 'lighten'), + assert(direction == 'row' || + direction == 'column' || + direction == 'row-reversed' || + direction == 'column-reversed'), + super(key: key); + final Color? color; + final dynamic icon; // IconData or String or null + final double iconSize; + final double spaceBetween; + final String? text; + final TextStyle textStyle; + final EdgeInsetsGeometry? padding; + final VoidCallback? onTap; + final String? tapEffect; + final double tapEffectColorRatio; + final String direction; + + @override + State createState() => _IconTextButtonState(); +} + +class _IconTextButtonState extends State { + @override + Widget build(BuildContext context) { + List> children = [ + if (widget.icon != null) + if (widget.icon is IconData) + { + 'Icon': { + 'arg': widget.icon, + 'size': widget.iconSize, + 'color': widget.color + } + } + else if (widget.icon is String) + { + 'SvgPicture.asset': { + 'arg': widget.icon, + 'height': widget.iconSize, + 'width': widget.iconSize, + 'color': widget.color + } + }, + widget.direction.startsWith('row') + ? { + 'SizedBox': { + 'width': widget.spaceBetween, + } + } + : { + 'SizedBox': { + 'height': widget.spaceBetween, + } + }, + if (widget.text != null) + { + 'Text': { + 'arg': widget.text!, + 'style': widget.textStyle + .copyWith(color: widget.textStyle.color ?? widget.color) + } + } + ]; + Map child = widget.direction.startsWith('row') + ? { + 'Row': { + 'children': widget.direction.endsWith('reversed') + ? children.reversed.toList() + : children + } + } + : { + 'Column': { + 'children': widget.direction.endsWith('reversed') + ? children.reversed.toList() + : children + } + }; + return IconTextButtonRaw( + data: widget.padding != null + ? { + 'Padding': {'padding': widget.padding!, 'child': child} + } + : { + 'Center': {'child': child} + }, + onTap: widget.onTap, + tapEffect: widget.tapEffect, + tapEffectColorRatio: widget.tapEffectColorRatio, + ); + } +} + +Widget renderRawResponsiveWidget(BuildContext context, Map data, + String tapEffect, double tapEffectColorRatio, bool isTapDowned) { + if (data.keys.length != 1) return const Placeholder(); + String widget = data.keys.first; + Map args = data[widget]!; + switch (widget) { + case 'Row': + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: (args['children'] as List>? ?? []) + .map((e) => renderRawResponsiveWidget( + context, e, tapEffect, tapEffectColorRatio, isTapDowned)) + .toList(), + ); + case 'Row-reversed': + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: (args['children'] as List>? ?? []) + .map((e) => renderRawResponsiveWidget( + context, e, tapEffect, tapEffectColorRatio, isTapDowned)) + .toList() + .reversed + .toList(), + ); + case 'Column': + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: (args['children'] as List>? ?? []) + .map((e) => renderRawResponsiveWidget( + context, e, tapEffect, tapEffectColorRatio, isTapDowned)) + .toList(), + ); + case 'Column-reversed': + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: (args['children'] as List>? ?? []) + .map((e) => renderRawResponsiveWidget( + context, e, tapEffect, tapEffectColorRatio, isTapDowned)) + .toList() + .reversed + .toList(), + ); + case 'Icon': + return Icon(args['arg'] ?? null, + size: args['size'] ?? 24.0, + color: Color.lerp( + args['color'] ?? Color(0xFF000000), + tapEffect == 'darken' + ? Color(0xFF000000) + : tapEffect == 'lighten' + ? Color(0xFFFFFFFF) + : args['color'] ?? Color(0xFF000000), + isTapDowned ? tapEffectColorRatio : 0)); + case 'SvgPicture.asset': + return SvgPicture.asset(args['arg'] ?? null, + width: args['width'] ?? 24.0, + height: args['height'] ?? 24.0, + colorFilter: ColorFilter.mode( + Color.lerp( + args['color'] ?? Color(0xFF000000), + tapEffect == 'darken' + ? Color(0xFF000000) + : tapEffect == 'lighten' + ? Color(0xFFFFFFFF) + : args['color'] ?? Color(0xFF000000), + isTapDowned ? tapEffectColorRatio : 0)!, + BlendMode.srcIn)); + case 'SizedBox': + return SizedBox( + width: args['width'], + height: args['height'], + child: args['child'] != null + ? renderRawResponsiveWidget(context, args['child'] ?? {}, + tapEffect, tapEffectColorRatio, isTapDowned) + : null); + case 'Spacer': + return Spacer(flex: args['flex'] ?? 1); + case 'Text': + return Text(args['arg'] ?? '', + style: ((args['style'] ?? TextStyle()) as TextStyle).copyWith( + color: Color.lerp( + (args['style'] ?? TextStyle()).color ?? Color(0xFF000000), + tapEffect == 'darken' + ? Color(0xFF000000) + : tapEffect == 'lighten' + ? Color(0xFFFFFFFF) + : (args['style'] ?? TextStyle()).color ?? + Color(0xFF000000), + isTapDowned ? tapEffectColorRatio : 0))); + case 'Padding': + return Padding( + padding: args['padding'] ?? EdgeInsets.zero, + child: renderRawResponsiveWidget(context, args['child'] ?? {}, + tapEffect, tapEffectColorRatio, isTapDowned)); + case 'Center': + return Center( + child: renderRawResponsiveWidget(context, args['child'] ?? {}, + tapEffect, tapEffectColorRatio, isTapDowned)); + default: + return const Placeholder(); + } +} + +class IconTextButtonRaw extends StatefulWidget { + const IconTextButtonRaw({ + Key? key, + required this.data, + this.onTap, + this.tapEffect = 'lighten', + this.tapEffectColorRatio = 0.48, + }) : super(key: key); + final Map data; + final VoidCallback? onTap; + final String? tapEffect; + final double tapEffectColorRatio; + + @override + State createState() => _IconTextButtonRawState(); +} + +class _IconTextButtonRawState extends State { + bool _isTapDowned = false; + bool _delaying = false; + @override + Widget build(BuildContext context) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTapDown: (_) { + setState(() { + _isTapDowned = true; + }); + _delaying = false; + Future.delayed(const Duration(milliseconds: 128), () { + _delaying = true; + if (_isTapDowned == false) + setState(() { + _isTapDowned = false; + }); + }); + }, + onTapUp: (_) { + if (_isTapDowned && widget.onTap != null) { + widget.onTap!(); + } + if (_delaying) + setState(() { + _isTapDowned = false; + }); + else + _isTapDowned = false; + }, + onTapCancel: () { + setState(() { + _isTapDowned = false; + }); + }, + child: renderRawResponsiveWidget(context, widget.data, + widget.tapEffect!, widget.tapEffectColorRatio, _isTapDowned)); + } +} + +class BackgroundButton extends StatefulWidget { + const BackgroundButton( + {Key? key, + this.color = const Color(0x00000000), + this.onTap, + this.onLongPress, + this.tapEffect = 'darken', + this.tapEffectColorRatio = 0.12, + required this.child}) + : assert(tapEffect == 'none' || + tapEffect == 'darken' || + tapEffect == 'lighten'), + super(key: key); + final Color? color; + final VoidCallback? onTap; + final VoidCallback? onLongPress; + final String? tapEffect; + final double tapEffectColorRatio; + final Widget child; + + @override + State createState() => _BackgroundButtonState(); +} + +class _BackgroundButtonState extends State { + bool _isTapDowned = false; + bool _delaying = false; + @override + Widget build(BuildContext context) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTapDown: (_) { + setState(() { + _isTapDowned = true; + }); + _delaying = false; + Future.delayed(const Duration(milliseconds: 128), () { + _delaying = true; + if (_isTapDowned == false) + setState(() { + _isTapDowned = false; + }); + }); + }, + onTapUp: (_) { + if (_isTapDowned && widget.onTap != null) { + widget.onTap!(); + } + if (_delaying) + setState(() { + _isTapDowned = false; + }); + else + _isTapDowned = false; + }, + onTapCancel: () { + setState(() { + _isTapDowned = false; + }); + }, + onLongPress: widget.onLongPress, + child: ColoredBox( + color: Color.lerp( + widget.color, + widget.tapEffect == 'darken' + ? Color(0xFF000000) + : widget.tapEffect == 'lighten' + ? Color(0xFFFFFFFF) + : widget.color, + _isTapDowned ? widget.tapEffectColorRatio : 0)!, + child: widget.child, + )); + } +} diff --git a/lib/widgets/course_block.dart b/lib/widgets/course_block.dart index 63b299c7..7df17993 100644 --- a/lib/widgets/course_block.dart +++ b/lib/widgets/course_block.dart @@ -4,6 +4,7 @@ import 'package:otlplus/constants/color.dart'; import 'package:otlplus/constants/text_styles.dart'; import 'package:otlplus/extensions/course.dart'; import 'package:otlplus/models/course.dart'; +import 'package:otlplus/utils/responsive_button.dart'; class CourseBlock extends StatelessWidget { final Course course; @@ -15,80 +16,74 @@ class CourseBlock extends StatelessWidget { Widget build(BuildContext context) { final isEn = EasyLocalization.of(context)?.currentLocale == Locale('en'); - return Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4.0), + return ClipRRect( + borderRadius: BorderRadius.circular(4.0), + child: BackgroundButton( color: OTLColor.grayE, - ), - child: Material( - color: Colors.transparent, - child: InkWell( - borderRadius: BorderRadius.circular(4.0), - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10.0, - vertical: 8.0, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text.rich( - TextSpan( - children: [ - TextSpan( - text: isEn ? course.titleEn : course.title, - style: bodyBold, - ), - const TextSpan(text: " "), - TextSpan(text: course.oldCode, style: bodyRegular), - ], - ), - ), - _buildDivider(), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("dictionary.type".tr(), style: labelBold), - const SizedBox(width: 8.0), - Expanded( - child: Text( - "${isEn ? course.department?.nameEn : course.department?.name}, ${isEn ? course.typeEn : course.type}", - style: labelRegular, - ), + onTap: onTap, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10.0, + vertical: 8.0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text.rich( + TextSpan( + children: [ + TextSpan( + text: isEn ? course.titleEn : course.title, + style: bodyBold, ), + const TextSpan(text: " "), + TextSpan(text: course.oldCode, style: bodyRegular), ], ), - const SizedBox(height: 4.0), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("dictionary.professors".tr(), style: labelBold), - const SizedBox(width: 8.0), - Expanded( - child: Text( - isEn ? course.professorsStrEn : course.professorsStr, - style: labelRegular, - ), + ), + _buildDivider(), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("dictionary.type".tr(), style: labelBold), + const SizedBox(width: 8.0), + Expanded( + child: Text( + "${isEn ? course.department?.nameEn : course.department?.name}, ${isEn ? course.typeEn : course.type}", + style: labelRegular, ), - ], - ), - const SizedBox(height: 4.0), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("dictionary.description".tr(), style: labelBold), - const SizedBox(width: 8.0), - Expanded( - child: Text( - course.summary, - style: labelRegular, - ), + ), + ], + ), + const SizedBox(height: 4.0), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("dictionary.professors".tr(), style: labelBold), + const SizedBox(width: 8.0), + Expanded( + child: Text( + isEn ? course.professorsStrEn : course.professorsStr, + style: labelRegular, ), - ], - ), - ], - ), + ), + ], + ), + const SizedBox(height: 4.0), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("dictionary.description".tr(), style: labelBold), + const SizedBox(width: 8.0), + Expanded( + child: Text( + course.summary, + style: labelRegular, + ), + ), + ], + ), + ], ), ), ), diff --git a/lib/widgets/custom_header_delegate.dart b/lib/widgets/custom_header_delegate.dart index 63b897c4..9a0100b1 100644 --- a/lib/widgets/custom_header_delegate.dart +++ b/lib/widgets/custom_header_delegate.dart @@ -4,13 +4,9 @@ class CustomHeaderDelegate extends SliverPersistentHeaderDelegate { final Widget Function(double) builder; final double height; final EdgeInsetsGeometry? padding; - final void Function(double) onTap; CustomHeaderDelegate( - {required this.builder, - required this.height, - this.padding, - required this.onTap}); + {required this.builder, required this.height, this.padding}); @override Widget build( @@ -21,10 +17,7 @@ class CustomHeaderDelegate extends SliverPersistentHeaderDelegate { transform: Matrix4.translationValues(0, -1, 0), child: Material( color: Colors.transparent, - child: InkWell( - onTap: () => onTap(shrinkOffset), - child: builder(shrinkOffset), - ), + child: builder(shrinkOffset), ), ); } diff --git a/lib/widgets/lecture_group_block_row.dart b/lib/widgets/lecture_group_block_row.dart index 64c87609..8b15a20a 100644 --- a/lib/widgets/lecture_group_block_row.dart +++ b/lib/widgets/lecture_group_block_row.dart @@ -1,9 +1,10 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/extensions/lecture.dart'; import 'package:otlplus/models/lecture.dart'; import 'package:otlplus/providers/lecture_search_model.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:provider/provider.dart'; import '../providers/timetable_model.dart'; @@ -90,20 +91,16 @@ class _LectureGroupBlockRowState extends State { mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.center, children: [ - GestureDetector( + IconTextButton( + icon: 'assets/icons/info.svg', + iconSize: 20.0, onTap: widget.onLongPress, - child: Center( - child: SvgPicture.asset('assets/icons/info.svg', - height: 20.0, - width: 20, - colorFilter: ColorFilter.mode( - Color(0xFF000000), BlendMode.srcIn)), - ), + color: Color(0xFF000000), ), SizedBox( width: 6.0, ), - GestureDetector( + IconTextButton( onTap: () { if (alreadyAdded) { _removeLecture(widget.lecture); @@ -111,17 +108,13 @@ class _LectureGroupBlockRowState extends State { _addLecture(widget.lecture); } }, - child: alreadyAdded - ? Icon( - Icons.remove, - size: 24.0, - color: OTLColor.pinksMain, - ) - : SvgPicture.asset('assets/icons/add.svg', - height: 24.0, - width: 24, - colorFilter: ColorFilter.mode( - Color(0xFF000000), BlendMode.srcIn)), + icon: alreadyAdded + ? Icons.remove + : 'assets/icons/add.svg', + iconSize: 24, + color: alreadyAdded + ? OTLColor.pinksMain + : Color(0xFF000000), ) ], ), @@ -149,16 +142,20 @@ class _LectureGroupBlockRowState extends State { content: const Text( "시간이 겹치는 수업이 있습니다. 추가하시면 해당 수업은 삭제됩니다.\n시간표에 추가하시겠습니까?"), actions: [ - TextButton( - child: const Text("취소"), - onPressed: () { + IconTextButton( + padding: EdgeInsets.all(12), + text: 'common.cancel'.tr(), + color: OTLColor.pinksMain, + onTap: () { result = false; Navigator.pop(context); }, ), - TextButton( - child: const Text("추가하기"), - onPressed: () { + IconTextButton( + padding: EdgeInsets.all(12), + text: 'common.add'.tr(), + color: OTLColor.pinksMain, + onTap: () { result = true; Navigator.pop(context); }, diff --git a/lib/widgets/lecture_group_simple_block.dart b/lib/widgets/lecture_group_simple_block.dart index acdd8f12..22930631 100644 --- a/lib/widgets/lecture_group_simple_block.dart +++ b/lib/widgets/lecture_group_simple_block.dart @@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:otlplus/constants/text_styles.dart'; import 'package:otlplus/utils/build_page_route.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/extensions/lecture.dart'; @@ -45,9 +46,16 @@ class LectureGroupSimpleBlock extends StatelessWidget { ? OTLColor.pinksSub : OTLColor.grayE, ), - child: Material( - color: Colors.transparent, - child: InkWell( + child: ClipRRect( + borderRadius: BorderRadius.vertical( + top: (lectures.first == lecture) + ? const Radius.circular(4.0) + : Radius.zero, + bottom: (lectures.last == lecture) + ? const Radius.circular(4.0) + : Radius.zero, + ), + child: BackgroundButton( onTap: () { context .read() @@ -57,14 +65,6 @@ class LectureGroupSimpleBlock extends StatelessWidget { buildLectureDetailPageRoute(), ); }, - borderRadius: BorderRadius.vertical( - top: (lectures.first == lecture) - ? const Radius.circular(4.0) - : Radius.zero, - bottom: (lectures.last == lecture) - ? const Radius.circular(4.0) - : Radius.zero, - ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, diff --git a/lib/widgets/lecture_search.dart b/lib/widgets/lecture_search.dart index 986bb85e..c15e7a54 100644 --- a/lib/widgets/lecture_search.dart +++ b/lib/widgets/lecture_search.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:otlplus/utils/build_page_route.dart'; import 'package:otlplus/pages/lecture_search_page.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/models/lecture.dart'; @@ -32,20 +33,20 @@ class _LectureSearchState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.fromLTRB(16, 12, 16, 12), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(8.0)), - child: GestureDetector( - onTap: () => Navigator.of(context).push(MaterialPageRoute( - builder: (context) => - LectureSearchPage(openKeyboard: false))), - child: ColoredBox( + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 12, 0, 12), + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(8.0)), + child: BackgroundButton( + onTap: () => Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + LectureSearchPage(openKeyboard: false))), color: Color(0xFFF9F0F0), child: Padding( padding: EdgeInsets.all(8.0), @@ -69,20 +70,15 @@ class _LectureSearchState extends State { ), ), ), - ), - ), - )), - SizedBox(width: 11.0), - SizedBox( - width: 24, - height: 24, - child: GestureDetector( - onTap: widget.onClosed, - child: const Icon(Icons.close_outlined, size: 24), - ), - ), - ], - ), + )), + )), + IconTextButton( + padding: EdgeInsets.fromLTRB(8, 12, 16, 12), + icon: Icons.close_outlined, + iconSize: 24, + onTap: widget.onClosed, + ) + ], ), Expanded( child: searchModel.isSearching diff --git a/lib/widgets/lecture_simple_block.dart b/lib/widgets/lecture_simple_block.dart index f704c2a6..acb53e49 100644 --- a/lib/widgets/lecture_simple_block.dart +++ b/lib/widgets/lecture_simple_block.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/constants/text_styles.dart'; import 'package:otlplus/models/lecture.dart'; +import 'package:otlplus/utils/responsive_button.dart'; class LectureSimpleBlock extends StatelessWidget { final Lecture lecture; @@ -22,10 +23,9 @@ class LectureSimpleBlock extends StatelessWidget { borderRadius: BorderRadius.circular(4.0), color: OTLColor.grayE, ), - child: Material( - color: Colors.transparent, - child: InkWell( - borderRadius: BorderRadius.circular(4.0), + child: ClipRRect( + borderRadius: BorderRadius.circular(4.0), + child: BackgroundButton( onTap: onTap, child: Padding( padding: const EdgeInsets.symmetric( diff --git a/lib/widgets/pop_up.dart b/lib/widgets/pop_up.dart index 5ddbf87f..102dba3e 100644 --- a/lib/widgets/pop_up.dart +++ b/lib/widgets/pop_up.dart @@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/constants/text_styles.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -35,7 +36,7 @@ class _PopUpState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - GestureDetector( + IconTextButton( onTap: () { setState(() { _checked = !_checked; @@ -44,27 +45,23 @@ class _PopUpState extends State { ); }); }, - child: Row( - children: [ - Icon( - Icons.check_circle_outline, - color: _checked ? OTLColor.pinksMain : OTLColor.grayA, - ), - const SizedBox(width: 8.0), - Text( - 'popup.dont_show_again'.tr(), - style: bodyRegular.copyWith( - color: _checked ? OTLColor.grayF : OTLColor.grayA, - ), - ), - ], + tapEffect: 'none', + icon: Icons.check_circle_outline, + color: _checked ? OTLColor.pinksMain : OTLColor.grayA, + spaceBetween: 8.0, + text: 'popup.dont_show_again'.tr(), + textStyle: bodyRegular.copyWith( + color: _checked ? OTLColor.grayF : OTLColor.grayA, ), ), - GestureDetector( + IconTextButton( onTap: () async { Navigator.pop(context); }, - child: Icon(Icons.close, color: OTLColor.grayF), + icon: Icons.close, + color: OTLColor.grayF, + tapEffect: 'darken', + tapEffectColorRatio: 0.24, ), ], ), diff --git a/lib/widgets/review_block.dart b/lib/widgets/review_block.dart index 56ef8970..fa2bc8b2 100644 --- a/lib/widgets/review_block.dart +++ b/lib/widgets/review_block.dart @@ -6,6 +6,7 @@ import 'package:otlplus/constants/url.dart'; import 'package:otlplus/dio_provider.dart'; import 'package:otlplus/extensions/review.dart'; import 'package:otlplus/models/review.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:otlplus/widgets/expandable_text.dart'; class ReviewBlock extends StatefulWidget { @@ -35,131 +36,125 @@ class _ReviewBlockState extends State { String content = widget.review.content; final isEn = EasyLocalization.of(context)?.currentLocale == Locale('en'); - return Container( - padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0), - margin: const EdgeInsets.only(bottom: 8.0), - decoration: BoxDecoration( + return Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: ClipRRect( borderRadius: BorderRadius.circular(4.0), - color: OTLColor.grayE, - ), - child: Material( - color: Colors.transparent, - child: InkWell( - borderRadius: BorderRadius.circular(4.0), - onTap: widget.onTap, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text.rich( - TextSpan( - style: bodyRegular, - children: [ - TextSpan( - text: isEn - ? widget.review.lecture.titleEn - : widget.review.lecture.title, - style: bodyBold, - ), - const TextSpan(text: " "), - TextSpan( - text: widget.review.lecture.professors - .map( - (professor) => isEn - ? (professor.nameEn == '' - ? professor.name - : professor.nameEn) - : professor.name, - ) - .join(" ")), - const TextSpan(text: " "), - TextSpan(text: widget.review.lecture.year.toString()), - const TextSpan(text: " "), - TextSpan( - text: [ - "", - "semester.spring".tr(), - "semester.summer".tr(), - "semester.fall".tr(), - "semester.winter".tr(), - ][widget.review.lecture.semester], - ), - ], - ), - ), - ExpandableText( - content.trim(), - maxLines: widget.maxLines, - style: bodyRegular.copyWith(color: OTLColor.gray0), - ), - const SizedBox(height: 6.0), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text.rich( - TextSpan( - style: labelRegular, - children: [ - TextSpan(text: "review.likes".tr()), - const TextSpan(text: " "), - TextSpan(text: _like.toString(), style: labelBold), - const TextSpan(text: " "), - TextSpan(text: "review.grade".tr()), - const TextSpan(text: " "), - TextSpan( - text: widget.review.gradeLetter, style: labelBold), - const TextSpan(text: " "), - TextSpan(text: "review.load".tr()), - const TextSpan(text: " "), - TextSpan( - text: widget.review.loadLetter, style: labelBold), - const TextSpan(text: " "), - TextSpan(text: "review.speech".tr()), - const TextSpan(text: " "), - TextSpan( - text: widget.review.speechLetter, style: labelBold), - ], - ), - ), - const Spacer(), - Material( - color: Colors.transparent, - child: InkWell( - onTap: _liked ? _uploadCancel : _uploadLike, - child: Row( - children: [ - Icon( - _liked ? Icons.favorite : Icons.favorite_border, - color: OTLColor.pinksMain, - size: 16.0, + child: Stack( + alignment: Alignment.bottomRight, + children: [ + BackgroundButton( + color: OTLColor.grayE, + onTap: widget.onTap, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text.rich( + TextSpan( + style: bodyRegular, + children: [ + TextSpan( + text: isEn + ? widget.review.lecture.titleEn + : widget.review.lecture.title, + style: bodyBold, ), - const SizedBox(width: 2.0), - Text( - _liked ? "review.likes".tr() : "review.likes".tr(), - style: labelRegular.copyWith( - color: _liked - ? OTLColor.pinksMain - : OTLColor.pinksMain, - ), + const TextSpan(text: " "), + TextSpan( + text: widget.review.lecture.professors + .map( + (professor) => isEn + ? (professor.nameEn == '' + ? professor.name + : professor.nameEn) + : professor.name, + ) + .join(" ")), + const TextSpan(text: " "), + TextSpan(text: widget.review.lecture.year.toString()), + const TextSpan(text: " "), + TextSpan( + text: [ + "", + "semester.spring".tr(), + "semester.summer".tr(), + "semester.fall".tr(), + "semester.winter".tr(), + ][widget.review.lecture.semester], ), ], ), ), - ), - const SizedBox(width: 6.0), - Material( - color: Colors.transparent, - child: InkWell( - onTap: _report, - child: Text( - "review.report".tr(), - style: labelRegular.copyWith(color: OTLColor.gray5), - ), + ExpandableText( + content.trim(), + maxLines: widget.maxLines, + style: bodyRegular.copyWith(color: OTLColor.gray0), + ), + const SizedBox(height: 6.0), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text.rich( + TextSpan( + style: labelRegular, + children: [ + TextSpan(text: "review.likes".tr()), + const TextSpan(text: " "), + TextSpan( + text: _like.toString(), style: labelBold), + const TextSpan(text: " "), + TextSpan(text: "review.grade".tr()), + const TextSpan(text: " "), + TextSpan( + text: widget.review.gradeLetter, + style: labelBold), + const TextSpan(text: " "), + TextSpan(text: "review.load".tr()), + const TextSpan(text: " "), + TextSpan( + text: widget.review.loadLetter, + style: labelBold), + const TextSpan(text: " "), + TextSpan(text: "review.speech".tr()), + const TextSpan(text: " "), + TextSpan( + text: widget.review.speechLetter, + style: labelBold), + ], + ), + ), + ], ), - ), - ], + ], + ), ), - ], - ), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconTextButton( + color: OTLColor.pinksMain, + iconSize: 12.0, + icon: _liked ? Icons.favorite : Icons.favorite_border, + spaceBetween: 2.0, + text: _liked ? "review.likes".tr() : "review.likes".tr(), + textStyle: labelRegular, + padding: EdgeInsets.fromLTRB(3, 8, 10, 8), + onTap: _liked ? _uploadCancel : _uploadLike, + ), + IconTextButton( + color: OTLColor.gray5, + text: "review.report".tr(), + textStyle: labelRegular, + onTap: _report, + padding: EdgeInsets.fromLTRB(3, 8, 10, 8), + ) + ], + ) + ], ), ), ); diff --git a/lib/widgets/review_write_block.dart b/lib/widgets/review_write_block.dart index 9f68aa9a..adabd6a2 100644 --- a/lib/widgets/review_write_block.dart +++ b/lib/widgets/review_write_block.dart @@ -8,6 +8,7 @@ import 'package:otlplus/constants/url.dart'; import 'package:otlplus/dio_provider.dart'; import 'package:otlplus/models/lecture.dart'; import 'package:otlplus/models/review.dart'; +import 'package:otlplus/utils/responsive_button.dart'; class ReviewWriteBlock extends StatefulWidget { final Lecture lecture; @@ -143,25 +144,17 @@ class _ReviewWriteBlockState extends State { _buildScore("성적"), _buildScore("널널"), _buildScore("강의"), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Material( - color: Colors.transparent, - child: InkWell( - onTap: _canUpload() ? _uploadReview : null, - child: Text( - (widget.existingReview == null) - ? "common.upload".tr() - : "common.edit".tr(), - style: labelRegular.copyWith( - color: - _canUpload() ? OTLColor.pinksMain : OTLColor.grayA, - ), - ), - ), - ), - ], + Align( + alignment: Alignment.bottomRight, + child: IconTextButton( + padding: EdgeInsets.zero, + color: _canUpload() ? OTLColor.pinksMain : OTLColor.grayA, + text: (widget.existingReview == null) + ? "common.upload".tr() + : "common.edit".tr(), + onTap: _canUpload() ? _uploadReview : null, + textStyle: labelRegular, + ), ), ], ), @@ -255,26 +248,22 @@ class _ReviewWriteBlockState extends State { return Padding( padding: const EdgeInsets.only(left: 4.0), child: ClipOval( - child: Container( - width: 24.0, - height: 24.0, + child: BackgroundButton( color: (_scores[type] == score) ? OTLColor.pinksSub : OTLColor.grayD, - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: () { - setState(() { - _scores[type] = (_scores[type] == score) ? 0 : score; - }); - }, - child: Center( - child: Text( - ["?", "F", "D", "C", "B", "A"][score], - style: labelBold.copyWith( - color: _scores[type] == score - ? OTLColor.gray0 - : OTLColor.grayF, - ), + onTap: () { + setState(() { + _scores[type] = (_scores[type] == score) ? 0 : score; + }); + }, + child: SizedBox( + width: 24.0, + height: 24.0, + child: Center( + child: Text( + ["?", "F", "D", "C", "B", "A"][score], + style: labelBold.copyWith( + color: + _scores[type] == score ? OTLColor.gray0 : OTLColor.grayF, ), ), ), diff --git a/lib/widgets/search_filter_panel.dart b/lib/widgets/search_filter_panel.dart index 5df323c5..c4be8a56 100644 --- a/lib/widgets/search_filter_panel.dart +++ b/lib/widgets/search_filter_panel.dart @@ -1,8 +1,11 @@ +import 'dart:math' as math; + +import 'package:easy_localization/easy_localization.dart' as _; import 'package:flutter/material.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/constants/text_styles.dart'; import 'package:otlplus/models/filter.dart'; -import 'dart:math' as math; +import 'package:otlplus/utils/responsive_button.dart'; class SearchFilterPanel extends StatefulWidget { final Map filter; @@ -98,7 +101,7 @@ class _SelectorState extends State { ), Visibility( visible: widget.isMultiSelect, - child: GestureDetector( + child: IconTextButton( onTap: () { if (widget.selectList .every((v) => v.every((w) => w.selected == true))) { @@ -115,21 +118,14 @@ class _SelectorState extends State { }); } }, - child: Text.rich( - TextSpan( - style: bodyRegular.copyWith( - color: OTLColor.pinksMain, - decoration: TextDecoration.underline, - ), - text: widget.selectList.every( - (v) => v.every((w) => w.selected == true), - ) - // unknown error - // ? "common.unselect_all".tr() - // : "common.select_all".tr() - ? "모두 해제" - : "모두 선택", - ), + text: widget.selectList.every( + (v) => v.every((w) => w.selected == true), + ) + ? "common.unselect_all".tr() + : "common.select_all".tr(), + textStyle: bodyRegular.copyWith( + color: OTLColor.pinksMain, + decoration: TextDecoration.underline, ), ), ) @@ -230,28 +226,32 @@ class RadioSelectButton extends StatefulWidget { class _RadioSelectButtonState extends State { @override Widget build(BuildContext context) { - return GestureDetector( - onTap: () => widget.setOption(!widget.option.selected), - child: Container( - height: 32.0, - decoration: BoxDecoration( - color: widget.option.selected ? OTLColor.pinksSub : OTLColor.grayE, - borderRadius: BorderRadius.circular(16.0), - ), - padding: EdgeInsets.symmetric(horizontal: 8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - widget.option.label, - style: labelRegular.copyWith( - color: widget.option.selected ? OTLColor.gray0 : OTLColor.grayA, - ), + return ClipRRect( + borderRadius: BorderRadius.circular(16.0), + child: BackgroundButton( + onTap: () => widget.setOption(!widget.option.selected), + color: widget.option.selected ? OTLColor.pinksSub : OTLColor.grayE, + child: SizedBox( + height: 32.0, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.option.label, + style: labelRegular.copyWith( + color: widget.option.selected + ? OTLColor.gray0 + : OTLColor.grayA, + ), + ), + widget.option.selected + ? Icon(Icons.check, size: 16.0, color: OTLColor.gray0) + : Icon(Icons.add, size: 16.0, color: OTLColor.grayA) + ], ), - widget.option.selected - ? Icon(Icons.check, size: 16.0, color: OTLColor.gray0) - : Icon(Icons.add, size: 16.0, color: OTLColor.grayA) - ], + ), ), ), ); diff --git a/lib/widgets/semester_picker.dart b/lib/widgets/semester_picker.dart index 00865ec3..23045ade 100644 --- a/lib/widgets/semester_picker.dart +++ b/lib/widgets/semester_picker.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/constants/text_styles.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/extensions/semester.dart'; import 'package:otlplus/providers/timetable_model.dart'; @@ -33,52 +34,49 @@ class _SemesterPickerState extends State { } Widget _buildTitle(BuildContext context) { - return InkWell( + return IconTextButton( onTap: widget.onTap, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Text( - context.watch().selectedSemester.title, - style: displayBold.copyWith(height: 1.448), - textAlign: TextAlign.center, - ), - ), + padding: const EdgeInsets.all(8.0), + text: context.watch().selectedSemester.title, + textStyle: displayBold.copyWith(height: 1.448), ); } Widget _buildLeftButton(ThemeData theme) { - return InkWell( - onTap: context.watch().canGoPreviousSemester() - ? () { - context.read().goPreviousSemester(); - widget.onSemesterChanged(); - } - : null, - child: Icon( - Icons.navigate_before_outlined, + return IconTextButton( + onTap: context.watch().canGoPreviousSemester() + ? () { + context.read().goPreviousSemester(); + widget.onSemesterChanged(); + } + : null, + icon: Icons.navigate_before_outlined, + iconSize: 24, color: context.watch().canGoPreviousSemester() ? OTLColor.gray0 : OTLColor.grayA, - size: 24, - ), - ); + padding: const EdgeInsets.all(4.0), + tapEffect: context.watch().canGoNextSemester() + ? 'lighten' + : 'none'); } Widget _buildRightButton(ThemeData theme) { - return InkWell( - onTap: context.watch().canGoNextSemester() - ? () { - context.read().goNextSemester(); - widget.onSemesterChanged(); - } - : null, - child: Icon( - Icons.navigate_next_outlined, + return IconTextButton( + onTap: context.watch().canGoNextSemester() + ? () { + context.read().goNextSemester(); + widget.onSemesterChanged(); + } + : null, + icon: Icons.navigate_next_outlined, + iconSize: 24, color: context.watch().canGoNextSemester() ? OTLColor.gray0 : OTLColor.grayA, - size: 24, - ), - ); + padding: const EdgeInsets.all(4.0), + tapEffect: context.watch().canGoNextSemester() + ? 'lighten' + : 'none'); } } diff --git a/lib/widgets/timetable_block.dart b/lib/widgets/timetable_block.dart index d2233cd0..964f068b 100644 --- a/lib/widgets/timetable_block.dart +++ b/lib/widgets/timetable_block.dart @@ -5,6 +5,7 @@ import 'package:otlplus/constants/text_styles.dart'; import 'package:otlplus/extensions/lecture.dart'; import 'package:otlplus/models/lecture.dart'; import 'package:otlplus/utils/get_text_height.dart'; +import 'package:otlplus/utils/responsive_button.dart'; class TimetableBlock extends StatelessWidget { final Lecture lecture; @@ -83,27 +84,21 @@ class TimetableBlock extends StatelessWidget { )); } - return Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(2.0), + return ClipRRect( + borderRadius: BorderRadius.circular(2.0), + child: BackgroundButton( color: isTemp ? OTLColor.pinksMain : isExamTime ? OTLColor.grayE : OTLColor.blockColors[lecture.course % 16], - ), - child: Material( - color: Colors.transparent, - child: InkWell( - borderRadius: BorderRadius.circular(2.0), - onTap: onTap, - onLongPress: onLongPress, - child: Padding( - padding: const EdgeInsets.all(6.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: contents, - ), + onTap: onTap, + onLongPress: onLongPress, + child: Padding( + padding: const EdgeInsets.all(6.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: contents, ), ), ), diff --git a/pubspec.lock b/pubspec.lock index 8e8812ac..a45d9232 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,34 +5,34 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: a36ec4843dc30ea6bf652bf25e3448db6c5e8bcf4aa55f063a5d1dad216d8214 + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a url: "https://pub.dev" source: hosted - version: "58.0.0" + version: "61.0.0" _flutterfire_internals: dependency: transitive description: name: _flutterfire_internals - sha256: "330d7fcbb72624f5b6d374af8b059b0ef4ba96ba5b8987f874964a1287eb617d" + sha256: "5dce45a06d386358334eb1689108db6455d90ceb0d75848d5f4819283d4ee2b8" url: "https://pub.dev" source: hosted - version: "1.0.18" + version: "1.3.4" analyzer: dependency: transitive description: name: analyzer - sha256: cc4242565347e98424ce9945c819c192ec0838cb9d1f6aa4a97cc96becbc5b27 + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 url: "https://pub.dev" source: hosted - version: "5.10.0" + version: "5.13.0" args: dependency: transitive description: name: args - sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" async: dependency: transitive description: @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" cupertino_icons: dependency: transitive description: @@ -109,10 +109,10 @@ packages: dependency: "direct main" description: name: dio - sha256: f7c26ddb3a10ff033cf1bfbf2c668feb3472327f48385775f7cf25b104f8bfce + sha256: "9d6445da1caf8412070670c03c39ad5b12a78cc8c2361417b220905a2bcbdd2f" url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.3.1" dotted_border: dependency: "direct main" description: @@ -182,66 +182,66 @@ packages: dependency: "direct main" description: name: firebase_analytics - sha256: "48202f083e63ec56e00d27bf4bab5600c73d850134bcda262e3b9634ac841826" + sha256: d7494294bdb56a58ab6c4cfef377693835c43fa3610707346b98892fd7c4b634 url: "https://pub.dev" source: hosted - version: "10.1.6" + version: "10.4.4" firebase_analytics_platform_interface: dependency: transitive description: name: firebase_analytics_platform_interface - sha256: "56bc6da503e4536d5c4d0ab41f25fb9fe58379b4f9bac5af9b091735764a141c" + sha256: bcb32ac53c494bd6deb61dcd02087418ae96d063ce522a4004974a32545c3f2b url: "https://pub.dev" source: hosted - version: "3.3.23" + version: "3.6.4" firebase_analytics_web: dependency: transitive description: name: firebase_analytics_web - sha256: "58f8992a4661dec1671255d31d4755721456aad07ae00b49ae303a8afcbc739c" + sha256: "2f7d8ab97b46a747730f580b5a38b3e5330efb54c16af48613a6f729cc5f57fa" url: "https://pub.dev" source: hosted - version: "0.5.1+14" + version: "0.5.4+4" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: "75f747cafd7cbd6c00b908e3a7aa59fc31593d46ba8165d9ee8a79e69464a394" + sha256: "2e9324f719e90200dc7d3c4f5d2abc26052f9f2b995d3b6626c47a0dfe1c8192" url: "https://pub.dev" source: hosted - version: "2.8.0" + version: "2.15.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: "5615b30c36f55b2777d0533771deda7e5730e769e5d3cb7fda79e9bed86cfa55" + sha256: b63e3be6c96ef5c33bdec1aab23c91eb00696f6452f0519401d640938c94cba2 url: "https://pub.dev" source: hosted - version: "4.5.3" + version: "4.8.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: "0c1cf1f1022d2245ac117443bb95207952ca770281524d2908e323bc063fb8ff" + sha256: "0fd5c4b228de29b55fac38aed0d9e42514b3d3bd47675de52bf7f8fccaf922fa" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.6.0" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - sha256: "5410b6ab2009fc6c181ca82e16525fdcb324c5851933bfbb49246543b1005ebc" + sha256: "3607b46342537f98df18b130b6f5ab25cee6981a3a782e1a7b121d04dfea3caa" url: "https://pub.dev" source: hosted - version: "3.0.17" + version: "3.3.4" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: bf3d3791c51a8448413b5ebd8388fa7db239a3e165db6bbd8ab58ef0e8f61906 + sha256: c63abeb87b18f6e6d4bf6bb3977f15d2d9281a049d93fe098e83e56dcbf7da06 url: "https://pub.dev" source: hosted - version: "3.3.17" + version: "3.6.4" flutter: dependency: "direct main" description: flutter @@ -261,10 +261,10 @@ packages: dependency: "direct main" description: name: flutter_platform_widgets - sha256: "8ffd4a834f73a97dd8f1d73d6fcd067cdbe5e9190fb616e34772f1ffd14a277e" + sha256: "107d5bc9a167b4e268cba44075ee399b6b2c63d44ede28f7e8c983d7fa4b59be" url: "https://pub.dev" source: hosted - version: "3.3.4" + version: "3.3.5" flutter_svg: dependency: "direct main" description: @@ -316,10 +316,10 @@ packages: dependency: transitive description: name: glob - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" http_multi_server: dependency: transitive description: @@ -364,10 +364,10 @@ packages: dependency: transitive description: name: logging - sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" matcher: dependency: transitive description: @@ -460,42 +460,42 @@ packages: dependency: "direct main" description: name: path_provider - sha256: c7edf82217d4b2952b2129a61d3ad60f1075b9299e629e149a8d2e39c2e6aad4 + sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" url: "https://pub.dev" source: hosted - version: "2.0.14" + version: "2.0.15" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "019f18c9c10ae370b08dce1f3e3b73bc9f58e7f087bb5e921f06529438ac0ae7" + sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" url: "https://pub.dev" source: hosted - version: "2.0.24" + version: "2.0.27" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "12eee51abdf4d34c590f043f45073adbb45514a108bd9db4491547a2fd891059" + sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.4" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" + sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 url: "https://pub.dev" source: hosted - version: "2.1.10" + version: "2.1.11" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + sha256: bced5679c7df11190e1ddc35f3222c858f328fff85c3942e46e7f5589bf9eb84 url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.1.0" path_provider_windows: dependency: transitive description: @@ -516,10 +516,10 @@ packages: dependency: transitive description: name: permission_handler_platform_interface - sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84" + sha256: "7c6b1500385dd1d2ca61bb89e2488ca178e274a69144d26bbd65e33eae7c02a9" url: "https://pub.dev" source: hosted - version: "3.9.0" + version: "3.11.3" petitparser: dependency: transitive description: @@ -540,10 +540,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" pool: dependency: transitive description: @@ -572,74 +572,74 @@ packages: dependency: transitive description: name: pub_semver - sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: "78528fd87d0d08ffd3e69551173c026e8eacc7b7079c82eb6a77413957b7e394" + sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1" url: "https://pub.dev" source: hosted - version: "2.0.20" + version: "2.2.0" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: ad423a80fe7b4e48b50d6111b3ea1027af0e959e49d485712e134863d9c1c521 + sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076 url: "https://pub.dev" source: hosted - version: "2.0.17" + version: "2.2.0" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "1e755f8583229f185cfca61b1d80fb2344c9d660e1c69ede5450d8f478fa5310" + sha256: f39696b83e844923b642ce9dd4bd31736c17e697f6731a5adf445b1274cf3cd4 url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.3.2" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: "3a59ed10890a8409ad0faad7bb2957dab4b92b8fbe553257b05d30ed8af2c707" + sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.3.0" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "824bfd02713e37603b2bdade0842e47d56e7db32b1dcdd1cae533fb88e2913fc" + sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.3.0" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: "0dc2633f215a3d4aa3184c9b2c5766f4711e4e5a6b256e62aafee41f89f1bfb8" + sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a" url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.2.0" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "71bcd669bb9cdb6b39f22c4a7728b6d49e934f6cba73157ffa5a54f1eed67436" + sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.3.0" shelf: dependency: transitive description: name: shelf - sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" shelf_packages_handler: dependency: transitive description: @@ -660,10 +660,10 @@ packages: dependency: transitive description: name: shelf_web_socket - sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" sky_engine: dependency: transitive description: flutter @@ -761,74 +761,74 @@ packages: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" url_launcher: dependency: "direct main" description: name: url_launcher - sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e" + sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e" url: "https://pub.dev" source: hosted - version: "6.1.10" + version: "6.1.12" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: dd729390aa936bf1bdf5cd1bc7468ff340263f80a2c4f569416507667de8e3c8 + sha256: "78cb6dea3e93148615109e58e42c35d1ffbf5ef66c44add673d0ab75f12ff3af" url: "https://pub.dev" source: hosted - version: "6.0.26" + version: "6.0.37" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "3dedc66ca3c0bef9e6a93c0999aee102556a450afcc1b7bcfeace7a424927d92" + sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2" url: "https://pub.dev" source: hosted - version: "6.1.3" + version: "6.1.4" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "206fb8334a700ef7754d6a9ed119e7349bc830448098f21a69bf1b4ed038cabc" + sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5" url: "https://pub.dev" source: hosted - version: "3.0.4" + version: "3.0.5" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: "0ef2b4f97942a16523e51256b799e9aa1843da6c60c55eefbfa9dbc2dcb8331a" + sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1" url: "https://pub.dev" source: hosted - version: "3.0.4" + version: "3.0.6" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" + sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa" + sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4 url: "https://pub.dev" source: hosted - version: "2.0.16" + version: "2.0.18" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: a83ba3607a507758669cfafb03f9de09bf6e6280c14d9b9cb18f013e406dcacd + sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.7" vector_graphics: dependency: transitive description: @@ -873,18 +873,18 @@ packages: dependency: transitive description: name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.0" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.4.0" webdriver: dependency: transitive description: @@ -953,10 +953,10 @@ packages: dependency: transitive description: name: xdg_directories - sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.1" xml: dependency: transitive description: @@ -969,10 +969,10 @@ packages: dependency: transitive description: name: yaml - sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" sdks: dart: ">=3.0.0 <4.0.0" - flutter: ">=3.7.0" + flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index 16d49f87..ced32f10 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,7 +41,7 @@ dependencies: firebase_core: ^2.8.0 firebase_analytics: ^10.1.6 firebase_crashlytics: ^3.0.17 - # Forked + # Forked dropdown_button2: git: url: https://github.com/sparcs-kaist/dropdown_button2.git