diff --git a/lib/home.dart b/lib/home.dart index 44f2e559..d8abbc7a 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -1,19 +1,7 @@ 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/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/widgets/responsive_button.dart'; +import 'package:otlplus/utils/navigator.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; import 'package:otlplus/widgets/pop_up.dart'; -import 'package:otlplus/widgets/semester_picker.dart'; -import 'package:provider/provider.dart'; -import 'package:otlplus/constants/color.dart'; import 'package:otlplus/pages/dictionary_page.dart'; import 'package:otlplus/pages/main_page.dart'; import 'package:otlplus/pages/review_page.dart'; @@ -47,7 +35,7 @@ class _OTLHomeState extends State with SingleTickerProviderStateMixin { WidgetsBinding.instance.addPostFrameCallback( (_) async { if ((await SharedPreferences.getInstance()).getBool('popup') ?? true) { - await showDialog( + await OTLNavigator.pushDialog( context: context, builder: (context) => PopUp(), ); @@ -58,12 +46,10 @@ class _OTLHomeState extends State with SingleTickerProviderStateMixin { @override Widget build(BuildContext context) { - return Scaffold( - appBar: _buildAppBar(), - backgroundColor: - _currentIndex == 0 ? const Color(0xFF9B4810) : OTLColor.pinksLight, + return OTLScaffold( + // extendBodyBehindAppBar: _currentIndex == 0, bottomNavigationBar: _buildBottomNavigationBar(), - body: GestureDetector( + child: GestureDetector( onTap: () { FocusScope.of(context).unfocus(); }, @@ -73,153 +59,6 @@ class _OTLHomeState extends State with SingleTickerProviderStateMixin { ); } - PreferredSizeWidget _buildHomeAppBar() { - return PreferredSize( - preferredSize: Size.fromHeight(5), - child: AppBar( - flexibleSpace: - SafeArea(child: Container(color: OTLColor.pinksMain, height: 5.0)), - backgroundColor: OTLColor.pinksLight, - foregroundColor: OTLColor.pinksMain, - elevation: 0.0, - automaticallyImplyLeading: false, - ), - ); - } - - PreferredSizeWidget _buildTimeTableAppBar() { - return PreferredSize( - preferredSize: Size.fromHeight(kToolbarHeight + 5), - child: SafeArea( - child: Container( - color: OTLColor.pinksLight, - child: Column( - children: [ - Container( - color: OTLColor.pinksMain, - height: 5, - ), - Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: const EdgeInsets.only(left: 16), - child: SemesterPicker( - onSemesterChanged: () { - context - .read() - .setSelectedLecture(null); - context.read().lectureClear(); - }, - ), - ), - TimetableModeControl( - dropdownIndex: - context.watch().selectedMode, - onTap: (mode) => - context.read().setMode(mode), - ), - ], - ), - ) - ], - ), - ), - ), - ); - } - - PreferredSizeWidget _buildDictionaryAppBar() { - return AppBar( - title: appBarPadding(ClipRRect( - borderRadius: BorderRadius.circular(8.0), - child: BackgroundButton( - tapEffectColorRatio: 0.04, - onTap: () => Navigator.push(context, buildCourseSearchPageRoute()), - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), - child: Row( - 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: context.watch().courseSearchquery, - ), - ], - ), - ), - color: OTLColor.grayF, - ), - )), - flexibleSpace: - SafeArea(child: Container(color: OTLColor.pinksMain, height: 5.0)), - toolbarHeight: kToolbarHeight + 5.0, - backgroundColor: OTLColor.pinksLight, - foregroundColor: OTLColor.gray0, - elevation: 0.0, - centerTitle: true, - automaticallyImplyLeading: false, - ); - } - - PreferredSizeWidget _buildReviewAppBar() { - return PreferredSize( - preferredSize: Size.fromHeight(kToolbarHeight + 5), - child: SafeArea( - child: Container( - color: OTLColor.pinksLight, - child: Column( - children: [ - Container( - color: OTLColor.pinksMain, - height: 5, - ), - Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - ReviewModeControl( - selectedMode: - context.watch().selectedMode, - ), - Visibility( - visible: - context.watch().selectedMode == 0, - child: Padding( - padding: const EdgeInsets.only(right: 16.0), - child: HallOfFameControl(), - ), - ) - ], - ), - ) - ], - ), - ), - ), - ); - } - - PreferredSizeWidget _buildAppBar() { - switch (_currentIndex) { - case 0: - return _buildHomeAppBar(); - case 1: - return _buildTimeTableAppBar(); - case 2: - return _buildDictionaryAppBar(); - case 3: - return _buildReviewAppBar(); - default: - return _buildHomeAppBar(); - } - } - Widget _buildStack(BuildContext context, BoxConstraints constraints) { final layerTop = constraints.biggest.height; final layerAnimation = RelativeRectTween( diff --git a/lib/pages/course_detail_page.dart b/lib/pages/course_detail_page.dart index ca8ba819..a4136721 100644 --- a/lib/pages/course_detail_page.dart +++ b/lib/pages/course_detail_page.dart @@ -2,8 +2,8 @@ import 'package:easy_localization/easy_localization.dart'; 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/widgets/responsive_button.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/extensions/course.dart'; @@ -19,6 +19,7 @@ import 'package:otlplus/widgets/review_block.dart'; import 'package:otlplus/widgets/review_write_block.dart'; class CourseDetailPage extends StatelessWidget { + CourseDetailPage({Key? key}) : super(key: key); static String route = 'course_detail_page'; final _scrollController = ScrollController(); @@ -29,17 +30,15 @@ class CourseDetailPage extends StatelessWidget { context.watch(); final isEn = EasyLocalization.of(context)?.currentLocale == Locale('en'); - return Scaffold( - appBar: buildAppBar( - context, - courseDetailModel.hasData - ? (isEn - ? courseDetailModel.course.titleEn - : courseDetailModel.course.title) - : '', - true, - false, - ), + return OTLScaffold( + child: OTLLayout( + middle: Text( + courseDetailModel.hasData + ? (isEn + ? courseDetailModel.course.titleEn + : courseDetailModel.course.title) + : '', + style: titleBold), body: Card( shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16.0)), @@ -50,7 +49,7 @@ class CourseDetailPage extends StatelessWidget { child: const CircularProgressIndicator(), ), ), - ); + )); } Widget _buildBody(BuildContext context) { @@ -323,10 +322,9 @@ class CourseDetailPage extends StatelessWidget { ), ); return LectureGroupSimpleBlock( - lectures: filteredLectures, - semester: semester, - filter: selectedFilter, - ); + lectures: filteredLectures, + semester: semester, + filter: selectedFilter); }, ).toList(), ), diff --git a/lib/pages/course_search_page.dart b/lib/pages/course_search_page.dart index db2bc883..29893372 100644 --- a/lib/pages/course_search_page.dart +++ b/lib/pages/course_search_page.dart @@ -3,15 +3,15 @@ import 'package:flutter/material.dart'; 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/widgets/responsive_button.dart'; +import 'package:otlplus/utils/navigator.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; import 'package:otlplus/widgets/search_filter_panel.dart'; import 'package:otlplus/widgets/search_textfield.dart'; import 'package:provider/provider.dart'; class CourseSearchPage extends StatefulWidget { final bool openKeyboard; - const CourseSearchPage({Key? key, this.openKeyboard = false}) + const CourseSearchPage({Key? key, this.openKeyboard = true}) : super(key: key); @override @@ -42,112 +42,91 @@ class _CourseSearchPageState extends State { super.dispose(); } - PreferredSizeWidget _buildAppBar(BuildContext context) { - return AppBar( - title: appBarPadding( - Row( - children: [ - IconTextButton( - onTap: () => Navigator.pop(context), - icon: Icons.navigate_before, - padding: EdgeInsets.fromLTRB(0, 16, 16, 16), - ), - Expanded( - child: SearchTextfield( - autoFocus: - _searchTextController.text == '' && widget.openKeyboard, - backgroundColor: OTLColor.grayF, - textController: _searchTextController, - focusNode: _focusNode, - ), - ), - ], - ), - ), - flexibleSpace: - SafeArea(child: Container(color: OTLColor.pinksMain, height: 5.0)), - toolbarHeight: kToolbarHeight + 5.0, - backgroundColor: OTLColor.pinksLight, - foregroundColor: OTLColor.gray0, - elevation: 0.0, - centerTitle: true, - automaticallyImplyLeading: false, - ); - } - @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: OTLColor.pinksLight, - appBar: _buildAppBar(context), - body: Padding( - padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 16.0), - child: Column( - children: [ - Expanded( - child: Column( - children: [ - Expanded( - child: SearchFilterPanel( - filter: context.watch().courseFilter, - setFilter: context - .read() - .setCourseFilterSelected, + return OTLScaffold( + resizeToAvoidBottomInset: true, + child: OTLLayout( + middle: Padding( + padding: EdgeInsets.only(right: 16.0), + child: SearchTextfield( + autoFocus: _searchTextController.text == '' && widget.openKeyboard, + backgroundColor: OTLColor.grayF, + textController: _searchTextController, + focusNode: _focusNode, + ), + ), + body: SafeArea( + top: false, + minimum: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 16.0), + child: Column( + children: [ + Expanded( + child: Column( + children: [ + Expanded( + child: SearchFilterPanel( + filter: context.watch().courseFilter, + setFilter: context + .read() + .setCourseFilterSelected, + ), ), - ), - const SizedBox(height: 16.0), - ], + const SizedBox(height: 16.0), + ], + ), ), - ), - Row( - children: [ - Expanded( - child: FilledButton( - onPressed: () { - _searchTextController.clear(); - context.read().resetCourseFilter(); - }, - child: Text( - "common.reset_all".tr(), - style: bodyBold.copyWith(color: OTLColor.pinksMain), - ), - style: ButtonStyle( - backgroundColor: MaterialStatePropertyAll(OTLColor.grayF), - shape: MaterialStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), + Row( + children: [ + Expanded( + child: FilledButton( + onPressed: () { + _searchTextController.clear(); + context.read().resetCourseFilter(); + }, + child: Text( + "common.reset_all".tr(), + style: bodyBold.copyWith(color: OTLColor.pinksMain), + ), + style: ButtonStyle( + backgroundColor: + MaterialStatePropertyAll(OTLColor.grayF), + shape: MaterialStatePropertyAll( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), ), ), ), ), - ), - SizedBox( - width: 12, - ), - Expanded( - child: FilledButton( - style: ButtonStyle( - shape: MaterialStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), + SizedBox( + width: 12, + ), + Expanded( + child: FilledButton( + style: ButtonStyle( + shape: MaterialStatePropertyAll( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), ), ), + onPressed: () async { + if (await context + .read() + .courseSearch()) { + OTLNavigator.pop(context, result: true); + } else { + _focusNode.requestFocus(); + } + }, + child: Text("common.search".tr(), style: bodyBold), ), - onPressed: () async { - if (await context - .read() - .courseSearch()) { - Navigator.of(context).pop(true); - } else { - _focusNode.requestFocus(); - } - }, - child: Text("common.search".tr(), style: bodyBold), ), - ), - ], - ), - ], + ], + ), + ], + ), ), ), ); diff --git a/lib/pages/dictionary_page.dart b/lib/pages/dictionary_page.dart index 38bd17cf..41f63f62 100644 --- a/lib/pages/dictionary_page.dart +++ b/lib/pages/dictionary_page.dart @@ -1,11 +1,16 @@ 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/constants/text_styles.dart'; -import 'package:otlplus/utils/build_page_route.dart'; +import 'package:otlplus/pages/course_detail_page.dart'; +import 'package:otlplus/pages/course_search_page.dart'; +import 'package:otlplus/utils/navigator.dart'; import 'package:otlplus/providers/course_detail_model.dart'; import 'package:otlplus/providers/course_search_model.dart'; import 'package:otlplus/widgets/course_block.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; +import 'package:otlplus/widgets/responsive_button.dart'; import 'package:provider/provider.dart'; class DictionaryPage extends StatefulWidget { @@ -24,47 +29,77 @@ class _DictionaryPageState extends State { Widget build(BuildContext context) { final searchModel = context.watch(); - return Container( - color: OTLColor.grayF, - child: Builder( - builder: (context) { - if (searchModel.isSearching) { - return const Center(child: CircularProgressIndicator()); - } else if (searchModel.courses == null) { - return Center(child: _buildCopyRight()); - } else if (searchModel.courses!.isEmpty) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, + return OTLLayout( + middle: Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0), + child: ClipRRect( + borderRadius: BorderRadius.circular(8.0), + child: BackgroundButton( + tapEffectColorRatio: 0.04, + onTap: () => OTLNavigator.push(context, CourseSearchPage()), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text( - "common.no_result".tr(), - style: bodyRegular.copyWith(color: OTLColor.grayA), + 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: context.watch().courseSearchquery, ), ], ), - ); - } else { - return Scrollbar( - controller: _scrollController, - child: ListView.separated( + ), + color: OTLColor.grayF, + ), + ), + ), + body: ColoredBox( + color: OTLColor.grayF, + child: Builder( + builder: (context) { + if (searchModel.isSearching) { + return const Center(child: CircularProgressIndicator()); + } else if (searchModel.courses == null) { + return Center(child: _buildCopyRight()); + } else if (searchModel.courses!.isEmpty) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "common.no_result".tr(), + style: bodyRegular.copyWith(color: OTLColor.grayA), + ), + ], + ), + ); + } else { + return Scrollbar( controller: _scrollController, - padding: EdgeInsets.all(12.0), - itemCount: searchModel.courses?.length ?? 0, - itemBuilder: (context, index) => CourseBlock( - course: searchModel.courses![index], - onTap: () { - context - .read() - .loadCourse(searchModel.courses![index].id); - Navigator.push(context, buildCourseDetailPageRoute()); - }, + child: ListView.separated( + controller: _scrollController, + padding: EdgeInsets.all(12.0), + itemCount: searchModel.courses?.length ?? 0, + itemBuilder: (context, index) => CourseBlock( + course: searchModel.courses![index], + onTap: () { + context + .read() + .loadCourse(searchModel.courses![index].id); + OTLNavigator.push(context, CourseDetailPage()); + }, + ), + separatorBuilder: (context, index) => SizedBox(height: 8.0), ), - separatorBuilder: (context, index) => SizedBox(height: 8.0), - ), - ); - } - }, + ); + } + }, + ), ), ); } diff --git a/lib/pages/lecture_detail_page.dart b/lib/pages/lecture_detail_page.dart index 65d097ef..6c3487e0 100644 --- a/lib/pages/lecture_detail_page.dart +++ b/lib/pages/lecture_detail_page.dart @@ -3,9 +3,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_web_browser/flutter_web_browser.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/build_page_route.dart'; +import 'package:otlplus/pages/course_detail_page.dart'; import 'package:otlplus/widgets/responsive_button.dart'; +import 'package:otlplus/utils/navigator.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/extensions/lecture.dart'; @@ -19,8 +20,11 @@ import 'package:otlplus/widgets/review_block.dart'; import 'package:otlplus/widgets/review_write_block.dart'; class LectureDetailPage extends StatelessWidget { - static String route = 'lecture_detail_page'; + LectureDetailPage({Key? key, this.fromCourseDetailPage = false}) + : super(key: key); + static String route = 'lecture_detail_page'; + final bool fromCourseDetailPage; final _scrollController = ScrollController(); String _getSyllabusUrl(Lecture lecture) { @@ -39,27 +43,26 @@ class LectureDetailPage extends StatelessWidget { context.watch(); final isEn = EasyLocalization.of(context)?.currentLocale == Locale('en'); - return Scaffold( - appBar: buildAppBar( - context, - lectureDetailModel.hasData - ? (isEn - ? lectureDetailModel.lecture.titleEn - : lectureDetailModel.lecture.title) - : '', - true, - false, - ), - body: Card( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(16.0)), + return OTLScaffold( + child: OTLLayout( + middle: Text( + lectureDetailModel.hasData + ? (isEn + ? lectureDetailModel.lecture.titleEn + : lectureDetailModel.lecture.title) + : '', + style: titleBold), + body: Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(16.0)), + ), + child: + context.select((model) => model.hasData) + ? _buildBody(context) + : Center( + child: const CircularProgressIndicator(), + ), ), - child: - context.select((model) => model.hasData) - ? _buildBody(context) - : Center( - child: const CircularProgressIndicator(), - ), ), ); } @@ -106,7 +109,7 @@ class LectureDetailPage extends StatelessWidget { onOverlap: (lectures) async { bool result = false; - await showDialog( + await OTLNavigator.pushDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( @@ -120,7 +123,7 @@ class LectureDetailPage extends StatelessWidget { color: OTLColor.pinksMain, onTap: () { result = false; - Navigator.pop(context); + OTLNavigator.pop(context); }, ), IconTextButton( @@ -129,7 +132,7 @@ class LectureDetailPage extends StatelessWidget { color: OTLColor.pinksMain, onTap: () { result = true; - Navigator.pop(context); + OTLNavigator.pop(context); }, ), ], @@ -153,15 +156,21 @@ class LectureDetailPage extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - IconTextButton( - onTap: () { - context.read().loadCourse(lecture.course); - Navigator.push(context, buildCourseDetailPageRoute()); - }, - text: "dictionary.dictionary".tr(), - textStyle: bodyRegular.copyWith(color: OTLColor.pinksMain), + Padding( + padding: EdgeInsets.only(right: 8.0), + child: IconTextButton( + onTap: () { + if (fromCourseDetailPage) { + OTLNavigator.pop(context); + } else { + context.read().loadCourse(lecture.course); + OTLNavigator.push(context, CourseDetailPage()); + } + }, + text: "dictionary.dictionary".tr(), + textStyle: bodyRegular.copyWith(color: OTLColor.pinksMain), + ), ), - const SizedBox(width: 8.0), IconTextButton( onTap: () => FlutterWebBrowser.openWebPage( url: _getSyllabusUrl(lecture), diff --git a/lib/pages/lecture_search_page.dart b/lib/pages/lecture_search_page.dart index 4b0e3c7f..af24d977 100644 --- a/lib/pages/lecture_search_page.dart +++ b/lib/pages/lecture_search_page.dart @@ -4,15 +4,15 @@ import 'package:otlplus/constants/color.dart'; 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/widgets/responsive_button.dart'; +import 'package:otlplus/utils/navigator.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; import 'package:otlplus/widgets/search_filter_panel.dart'; import 'package:otlplus/widgets/search_textfield.dart'; import 'package:provider/provider.dart'; class LectureSearchPage extends StatefulWidget { final bool openKeyboard; - const LectureSearchPage({Key? key, this.openKeyboard = false}) + const LectureSearchPage({Key? key, this.openKeyboard = true}) : super(key: key); @override @@ -43,112 +43,93 @@ class _LectureSearchPageState extends State { super.dispose(); } - PreferredSizeWidget _buildAppBar(BuildContext context) { - return AppBar( - title: appBarPadding( - Row( - children: [ - IconTextButton( - onTap: () => Navigator.pop(context), - icon: Icons.navigate_before, - padding: EdgeInsets.fromLTRB(0, 16, 16, 16), - ), - Expanded( - child: SearchTextfield( - autoFocus: widget.openKeyboard, - backgroundColor: OTLColor.grayF, - textController: _searchTextController, - focusNode: _focusNode, - ), - ), - ], - ), - ), - flexibleSpace: - SafeArea(child: Container(color: OTLColor.pinksMain, height: 5.0)), - toolbarHeight: kToolbarHeight + 5.0, - backgroundColor: OTLColor.pinksLight, - foregroundColor: OTLColor.gray0, - elevation: 0.0, - centerTitle: true, - automaticallyImplyLeading: false, - ); - } - @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: OTLColor.pinksLight, - appBar: _buildAppBar(context), - body: Padding( - padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 16.0), - child: Column( - children: [ - Flexible( - child: Column( - children: [ - Flexible( - child: SearchFilterPanel( - filter: context.watch().lectureFilter, - setFilter: context - .read() - .setLectureFilterSelected, + return OTLScaffold( + resizeToAvoidBottomInset: true, + child: OTLLayout( + middle: Padding( + padding: EdgeInsets.only(right: 16.0), + child: SearchTextfield( + autoFocus: _searchTextController.text == '' && widget.openKeyboard, + backgroundColor: OTLColor.grayF, + textController: _searchTextController, + focusNode: _focusNode, + ), + ), + body: SafeArea( + top: false, + minimum: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 16.0), + child: Column( + children: [ + Flexible( + child: Column( + children: [ + Flexible( + child: SearchFilterPanel( + filter: + context.watch().lectureFilter, + setFilter: context + .read() + .setLectureFilterSelected, + ), ), - ), - const SizedBox(height: 16.0), - ], + const SizedBox(height: 16.0), + ], + ), ), - ), - Row( - children: [ - Expanded( - child: FilledButton( - onPressed: () { - _searchTextController.clear(); - context.read().resetLectureFilter(); - }, - child: Text( - "common.reset_all".tr(), - style: bodyBold.copyWith(color: OTLColor.pinksMain), - ), - style: ButtonStyle( - backgroundColor: MaterialStatePropertyAll(OTLColor.grayF), - shape: MaterialStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), + Row( + children: [ + Expanded( + child: FilledButton( + onPressed: () { + _searchTextController.clear(); + context.read().resetLectureFilter(); + }, + child: Text( + "common.reset_all".tr(), + style: bodyBold.copyWith(color: OTLColor.pinksMain), + ), + style: ButtonStyle( + backgroundColor: + MaterialStatePropertyAll(OTLColor.grayF), + shape: MaterialStatePropertyAll( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), ), ), ), ), - ), - SizedBox( - width: 12, - ), - Expanded( - child: FilledButton( - style: ButtonStyle( - shape: MaterialStatePropertyAll( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), + SizedBox( + width: 12, + ), + Expanded( + child: FilledButton( + style: ButtonStyle( + shape: MaterialStatePropertyAll( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), ), ), + onPressed: () async { + if (await context + .read() + .lectureSearch( + context.read().selectedSemester, + )) + OTLNavigator.pop(context); + else + _focusNode.requestFocus(); + }, + child: Text("common.search".tr(), style: bodyBold), ), - onPressed: () async { - if (await context - .read() - .lectureSearch( - context.read().selectedSemester, - )) - Navigator.of(context).pop(); - else - _focusNode.requestFocus(); - }, - child: Text("common.search".tr(), style: bodyBold), ), - ), - ], - ), - ], + ], + ), + ], + ), ), ), ); diff --git a/lib/pages/liked_review_page.dart b/lib/pages/liked_review_page.dart index 114453d1..10309a42 100644 --- a/lib/pages/liked_review_page.dart +++ b/lib/pages/liked_review_page.dart @@ -1,10 +1,12 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:otlplus/constants/text_styles.dart'; +import 'package:otlplus/pages/course_detail_page.dart'; import 'package:otlplus/providers/course_detail_model.dart'; import 'package:otlplus/providers/info_model.dart'; import 'package:otlplus/providers/liked_review_model.dart'; -import 'package:otlplus/utils/build_app_bar.dart'; -import 'package:otlplus/utils/build_page_route.dart'; +import 'package:otlplus/utils/navigator.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; import 'package:otlplus/widgets/review_block.dart'; import 'package:provider/provider.dart'; @@ -20,81 +22,84 @@ class LikedReviewPage extends StatelessWidget { final user = context.watch().user; final reviews = context.watch().likedReviews(user); - return Scaffold( - appBar: buildAppBar(context, 'user.liked_review'.tr(), true, true), - body: Container( - constraints: const BoxConstraints.expand(), - child: Card( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(16.0)), - ), - child: NotificationListener( - onNotification: (scrollNotification) { - final likedReviewModel = context.read(); + return OTLScaffold( + child: OTLLayout( + middle: Text('user.liked_review'.tr(), style: titleBold), + body: Container( + constraints: const BoxConstraints.expand(), + child: Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(16.0)), + ), + child: NotificationListener( + onNotification: (scrollNotification) { + final likedReviewModel = context.read(); - if (!likedReviewModel.isLoading && - scrollNotification.metrics.pixels == - scrollNotification.metrics.maxScrollExtent) { - likedReviewModel.loadLikedReviews(user); - } - return true; - }, - child: Padding( - padding: const EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 0.0), - child: Column( - children: [ - Expanded( - child: RefreshIndicator( - onRefresh: () async { - await context.read().clear(user); - }, - child: Scrollbar( - controller: _scrollController, - child: CustomScrollView( + if (!likedReviewModel.isLoading && + scrollNotification.metrics.pixels == + scrollNotification.metrics.maxScrollExtent) { + likedReviewModel.loadLikedReviews(user); + } + return true; + }, + child: Padding( + padding: const EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 0.0), + child: Column( + children: [ + Expanded( + child: RefreshIndicator( + onRefresh: () async { + await context.read().clear(user); + }, + child: Scrollbar( controller: _scrollController, - slivers: [ - SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - return ReviewBlock( - review: reviews[index], - onTap: () async { - context - .read() - .loadCourse(reviews[index].course.id); - Navigator.push( - context, - buildCourseDetailPageRoute(), - ); - }, - ); - }, - childCount: reviews.length, + child: CustomScrollView( + controller: _scrollController, + slivers: [ + SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return ReviewBlock( + review: reviews[index], + onTap: () async { + context + .read() + .loadCourse( + reviews[index].course.id); + OTLNavigator.push( + context, CourseDetailPage(), + transition: OTLNavigatorTransition + .rightLeft); + }, + ); + }, + childCount: reviews.length, + ), ), - ), - SliverList( - delegate: SliverChildListDelegate([ - Padding( - padding: const EdgeInsets.only( - top: 4.0, bottom: 12.0), - child: const Center( - child: SizedBox( - width: 24, - height: 24, - child: CircularProgressIndicator( - color: Colors.black12, - strokeWidth: 2, + SliverList( + delegate: SliverChildListDelegate([ + Padding( + padding: const EdgeInsets.only( + top: 4.0, bottom: 12.0), + child: const Center( + child: SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + color: Colors.black12, + strokeWidth: 2, + ), ), ), - ), - ) - ])) - ], + ) + ])) + ], + ), ), ), ), - ), - ], + ], + ), ), ), ), diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index 3b871c02..d3b5e756 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/widgets/responsive_button.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/url.dart'; import 'package:otlplus/providers/auth_model.dart'; @@ -53,11 +54,8 @@ class _LoginPageState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - toolbarHeight: 0, - ), - body: Material( + return OTLScaffold( + child: Material( child: Stack( children: [ Center( diff --git a/lib/pages/main_page.dart b/lib/pages/main_page.dart index 859530f8..88eee7ab 100644 --- a/lib/pages/main_page.dart +++ b/lib/pages/main_page.dart @@ -1,9 +1,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:otlplus/constants/text_styles.dart'; +import 'package:otlplus/pages/course_search_page.dart'; +import 'package:otlplus/pages/people_page.dart'; +import 'package:otlplus/pages/privacy_page.dart'; +import 'package:otlplus/pages/settings_page.dart'; +import 'package:otlplus/pages/user_page.dart'; import 'package:otlplus/providers/course_search_model.dart'; -import 'package:otlplus/utils/build_page_route.dart'; import 'package:otlplus/widgets/responsive_button.dart'; +import 'package:otlplus/utils/navigator.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/models/semester.dart'; @@ -35,174 +41,193 @@ class _MainPageState extends State { semester.beginning.isBefore(now) && semester.end.isAfter(now), orElse: () => infoModel.semesters.last, ); - return Stack( - alignment: Alignment.topCenter, - children: [ - Image.asset( - "assets/images/bg.4556cdee.jpg", - fit: BoxFit.cover, - color: const Color(0xFF9B4810).withOpacity(0.1), - colorBlendMode: BlendMode.srcATop, + return OTLLayout( + extendBodyBehindAppBar: true, + leading: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Image.asset( + "assets/images/logo.png", + height: 27.0, ), - Container( - constraints: const BoxConstraints.expand(), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - height: MediaQuery.of(context).size.width / 1296 * 865 - 16, - width: MediaQuery.of(context).size.width, - child: Stack( - alignment: Alignment.topCenter, - children: [ - 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: [ - IconTextButton( - onTap: () => Navigator.push( - context, buildUserPageRoute()), - icon: 'assets/icons/person.svg', - iconSize: 24, - color: OTLColor.pinksMain, - tapEffect: ButtonTapEffect.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: ButtonTapEffect.darken, - padding: - EdgeInsets.fromLTRB(8.0, 16.0, 16.0, 16.0), - ), - ], - ) - ], - ), - ), - Center( - child: Padding( - 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: ButtonTapEffect.darken, - color: OTLColor.grayF, - 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), - ), + ), + trailing: Row( + children: [ + IconTextButton( + onTap: () => OTLNavigator.push(context, UserPage(), + transition: OTLNavigatorTransition.downUp), + icon: 'assets/icons/person.svg', + iconSize: 24, + color: OTLColor.pinksMain, + tapEffect: ButtonTapEffect.darken, + padding: EdgeInsets.fromLTRB(16.0, 16.0, 8.0, 16.0), + ), + IconTextButton( + onTap: () => OTLNavigator.push(context, SettingsPage(), + transition: OTLNavigatorTransition.downUp), + icon: 'assets/icons/gear.svg', + iconSize: 24, + color: OTLColor.pinksMain, + tapEffect: ButtonTapEffect.darken, + padding: EdgeInsets.fromLTRB(8.0, 16.0, 16.0, 16.0), + ), + ], + ), + body: Stack( + alignment: Alignment.topCenter, + children: [ + Image.asset( + "assets/images/bg.4556cdee.jpg", + fit: BoxFit.cover, + color: const Color(0xFF9B4810).withOpacity(0.1), + colorBlendMode: BlendMode.srcATop, + ), + Container( + constraints: const BoxConstraints.expand(), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: MediaQuery.of(context).size.width / 1296 * 865 - 16, + width: MediaQuery.of(context).size.width, + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: ClipRRect( + borderRadius: BorderRadius.circular(8.0), + child: BackgroundButton( + tapEffectColorRatio: 0.04, + onTap: () { + context + .read() + .resetCourseFilter(); + OTLNavigator.push(context, CourseSearchPage()) + .then((e) { + if (e == true) { + widget.changeIndex(2); + } + }); + }, + tapEffect: ButtonTapEffect.darken, + color: OTLColor.grayF, + 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), ), - ], - ), + ), + ], ), ), - )), - ), - ], + ), + )), + ), ), - ), - Flexible( - child: ClipRRect( - borderRadius: - BorderRadius.vertical(top: Radius.circular(16.0)), - child: Container( - 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), - ], - ) - ], + Flexible( + child: ClipRRect( + borderRadius: + BorderRadius.vertical(top: Radius.circular(16.0)), + child: Container( + 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), + ], + ) + ], + ), ), ), ), - ), - ], - )), + ], + ) + + // 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), + // ], + // ) + // ], + // ), + // ), + // ), + // ), + ), + ), ), - ), - ], + ], + ), ), - ), - ], + ], + ), ); } @@ -216,7 +241,8 @@ class _MainPageState extends State { children: [ IconTextButton( onTap: () { - Navigator.push(context, buildPrivacyPageRoute()); + OTLNavigator.push(context, PrivacyPage(), + transition: OTLNavigatorTransition.downUp); }, text: 'title.privacy'.tr(), textStyle: labelRegular.copyWith(color: OTLColor.gray75), @@ -225,7 +251,8 @@ class _MainPageState extends State { ), IconTextButton( onTap: () { - Navigator.push(context, buildPeoplePageRoute()); + OTLNavigator.push(context, PeoplePage(), + transition: OTLNavigatorTransition.downUp); }, text: 'title.credit'.tr(), textStyle: labelRegular.copyWith(color: OTLColor.gray75), diff --git a/lib/pages/my_review_page.dart b/lib/pages/my_review_page.dart index 0a88511e..5be8a967 100644 --- a/lib/pages/my_review_page.dart +++ b/lib/pages/my_review_page.dart @@ -1,14 +1,16 @@ 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/models/lecture.dart'; import 'package:otlplus/models/semester.dart'; import 'package:otlplus/models/user.dart'; +import 'package:otlplus/pages/lecture_detail_page.dart'; import 'package:otlplus/providers/info_model.dart'; import 'package:otlplus/providers/lecture_detail_model.dart'; -import 'package:otlplus/utils/build_app_bar.dart'; -import 'package:otlplus/utils/build_page_route.dart'; +import 'package:otlplus/utils/navigator.dart'; import 'package:otlplus/widgets/lecture_simple_block.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; import 'package:provider/provider.dart'; class MyReviewPage extends StatelessWidget { @@ -30,14 +32,11 @@ class MyReviewPage extends StatelessWidget { ..sort((a, b) => ((a.year != b.year) ? (b.year - a.year) : (b.semester - a.semester))); - return Scaffold( - appBar: buildAppBar(context, 'user.my_review'.tr(), true, true), - body: Container( - constraints: const BoxConstraints.expand(), - child: Card( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(16.0)), - ), + return OTLScaffold( + child: OTLLayout( + middle: Text('user.my_review'.tr(), style: titleBold), + body: ColoredBox( + color: OTLColor.grayF, child: Padding( padding: const EdgeInsets.all(16.0), child: SingleChildScrollView( @@ -121,7 +120,7 @@ class MyReviewPage extends StatelessWidget { hasReview: user.reviews.any((review) => review.lecture.id == lecture.id), onTap: () { context.read().loadLecture(lecture.id, false); - Navigator.push(context, buildLectureDetailPageRoute()); + OTLNavigator.push(context, LectureDetailPage()); }, ); } diff --git a/lib/pages/people_page.dart b/lib/pages/people_page.dart index 2e38e0a5..3f236fcc 100644 --- a/lib/pages/people_page.dart +++ b/lib/pages/people_page.dart @@ -3,30 +3,35 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/constants/text_styles.dart'; -import 'package:otlplus/utils/build_app_bar.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; class PeoplePage extends StatelessWidget { const PeoplePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return Scaffold( - appBar: buildAppBar(context, 'title.credit'.tr(), false, true), - body: Padding( - padding: EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - _buildContainer('2023.03 ~'), - ..._get202303(), - const SizedBox(height: 32.0), - _buildContainer('2020.03 ~ 2023.02'), - const SizedBox(height: 12.0), - Text( - '준비 중입니다.', - style: bodyRegular.copyWith(color: OTLColor.grayA), + return OTLScaffold( + child: OTLLayout( + middle: Text('title.credit'.tr(), style: titleBold), + body: ColoredBox( + color: OTLColor.grayF, + child: Padding( + padding: EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _buildContainer('2023.03 ~'), + ..._get202303(), + const SizedBox(height: 32.0), + _buildContainer('2020.03 ~ 2023.02'), + const SizedBox(height: 12.0), + Text( + '준비 중입니다.', + style: bodyRegular.copyWith(color: OTLColor.grayA), + ), + ], ), - ], + ), ), ), ); diff --git a/lib/pages/privacy_page.dart b/lib/pages/privacy_page.dart index dc6347dd..0fe3fd1a 100644 --- a/lib/pages/privacy_page.dart +++ b/lib/pages/privacy_page.dart @@ -1,42 +1,49 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:otlplus/constants/color.dart'; import 'package:otlplus/constants/privacy.dart'; import 'package:otlplus/constants/text_styles.dart'; -import 'package:otlplus/utils/build_app_bar.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; class PrivacyPage extends StatelessWidget { const PrivacyPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return Scaffold( - appBar: buildAppBar(context, 'title.privacy'.tr(), false, true), - body: Padding( - padding: EdgeInsets.all(16.0), - child: ListView( - children: [ - Text.rich( - TextSpan( - style: bodyRegular, - children: [ + return OTLScaffold( + child: OTLLayout( + middle: Text('title.privacy'.tr(), style: titleBold), + body: ColoredBox( + color: OTLColor.grayF, + child: Padding( + padding: EdgeInsets.all(16.0), + child: ListView( + children: [ + Text.rich( TextSpan( - text: privacyText0, - children: List.generate( - 12, - (index) => TextSpan( - children: [ - TextSpan(text: '\n'), - TextSpan(text: privacyTitles[index], style: bodyBold), - TextSpan(text: '\n'), - TextSpan(text: privacyTexts[index]), - ], + style: bodyRegular, + children: [ + TextSpan( + text: privacyText0, + children: List.generate( + 12, + (index) => TextSpan( + children: [ + TextSpan(text: '\n'), + TextSpan( + text: privacyTitles[index], style: bodyBold), + TextSpan(text: '\n'), + TextSpan(text: privacyTexts[index]), + ], + ), + ), ), - ), + ], ), - ], - ), - ) - ], + ) + ], + ), + ), ), ), ); diff --git a/lib/pages/review_page.dart b/lib/pages/review_page.dart index 68f51b6f..22dbb880 100644 --- a/lib/pages/review_page.dart +++ b/lib/pages/review_page.dart @@ -1,6 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:otlplus/pages/course_detail_page.dart'; import 'package:otlplus/providers/hall_of_fame_model.dart'; -import 'package:otlplus/utils/build_page_route.dart'; +import 'package:otlplus/utils/navigator.dart'; +import 'package:otlplus/widgets/hall_of_fame_control.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; +import 'package:otlplus/widgets/review_mode_control.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/providers/course_detail_model.dart'; import 'package:otlplus/providers/review_model.dart'; @@ -20,43 +24,55 @@ class _ReviewPageState extends State { final latestReviews = context.watch().reviews; final hallOfFames = context.watch().hallOfFames(); - return Card( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(16.0)), + return OTLLayout( + leading: ReviewModeControl( + selectedMode: context.watch().selectedMode, ), - child: NotificationListener( - onNotification: (scrollNotification) { - if (_selectedMode == 1) { - final reviewModel = context.read(); + trailing: Visibility( + visible: context.watch().selectedMode == 0, + child: Padding( + padding: const EdgeInsets.only(right: 16.0), + child: HallOfFameControl(), + ), + ), + body: Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(16.0)), + ), + child: NotificationListener( + onNotification: (scrollNotification) { + if (_selectedMode == 1) { + final reviewModel = context.read(); - if (!reviewModel.isLoading && - scrollNotification.metrics.pixels == - scrollNotification.metrics.maxScrollExtent) { - reviewModel.loadReviews(); - } + if (!reviewModel.isLoading && + scrollNotification.metrics.pixels == + scrollNotification.metrics.maxScrollExtent) { + reviewModel.loadReviews(); + } - return true; - } else { - final hallOfFameModel = context.read(); + return true; + } else { + final hallOfFameModel = context.read(); - if (!hallOfFameModel.isLoading && - scrollNotification.metrics.pixels == - scrollNotification.metrics.maxScrollExtent) { - hallOfFameModel.loadHallOfFames(); - } + if (!hallOfFameModel.isLoading && + scrollNotification.metrics.pixels == + scrollNotification.metrics.maxScrollExtent) { + hallOfFameModel.loadHallOfFames(); + } - return true; - } - }, - child: Padding( - padding: const EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 0.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - _selectedMode == 1 - ? _buildLatestReviews(latestReviews) - : _buildHallOfFames(hallOfFames), - ], + return true; + } + }, + child: Padding( + padding: const EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 0.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _selectedMode == 1 + ? _buildLatestReviews(latestReviews) + : _buildHallOfFames(hallOfFames), + ], + ), ), ), ), @@ -84,7 +100,7 @@ class _ReviewPageState extends State { context .read() .loadCourse(latestReviews[index].course.id); - Navigator.push(context, buildCourseDetailPageRoute()); + OTLNavigator.push(context, CourseDetailPage()); }, ); }, @@ -135,7 +151,7 @@ class _ReviewPageState extends State { context .read() .loadCourse(hallOfFames[index].course.id); - Navigator.push(context, buildCourseDetailPageRoute()); + OTLNavigator.push(context, CourseDetailPage()); }, ); }, diff --git a/lib/pages/settings_page.dart b/lib/pages/settings_page.dart index 20d1b83b..85b0b22c 100644 --- a/lib/pages/settings_page.dart +++ b/lib/pages/settings_page.dart @@ -3,8 +3,9 @@ import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; 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/widgets/responsive_button.dart'; +import 'package:otlplus/utils/navigator.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:flutter/foundation.dart' show kDebugMode; @@ -15,121 +16,127 @@ class SettingsPage extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - appBar: buildAppBar(context, 'title.settings'.tr(), false, true), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - ListTile( - title: Text( - "settings.send_error_log".tr(), - style: bodyBold, - ), - subtitle: Text( - "settings.send_error_log_desc".tr(), - style: bodyRegular, - ), - trailing: PlatformSwitch( - value: context.watch().getSendCrashlytics(), - onChanged: (value) => - context.read().setSendCrashlytics(value), - ), - ), - Visibility( - visible: context.watch().getSendCrashlytics(), - child: ListTile( - title: Text( - "settings.send_anonymously".tr(), - style: bodyBold, - ), - subtitle: Text( - "settings.send_anonymously_desc".tr(), - style: bodyRegular, - ), - trailing: PlatformSwitch( - value: context - .watch() - .getSendCrashlyticsAnonymously(), - onChanged: (value) => context - .read() - .setSendCrashlyticsAnonymously(value), - ), - ), - ), - Visibility( - visible: kDebugMode, - child: ListTile( - title: Text( - "settings.throw_test".tr(), - style: bodyBold, + return OTLScaffold( + child: OTLLayout( + middle: Text('title.settings'.tr(), style: titleBold), + body: ColoredBox( + color: OTLColor.grayF, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + ListTile( + title: Text( + "settings.send_error_log".tr(), + style: bodyBold, + ), + subtitle: Text( + "settings.send_error_log_desc".tr(), + style: bodyRegular, + ), + trailing: PlatformSwitch( + value: context.watch().getSendCrashlytics(), + onChanged: (value) => + context.read().setSendCrashlytics(value), + ), ), - subtitle: Text( - "settings.throw_test_desc".tr(), - style: bodyRegular, + Visibility( + visible: context.watch().getSendCrashlytics(), + child: ListTile( + title: Text( + "settings.send_anonymously".tr(), + style: bodyBold, + ), + subtitle: Text( + "settings.send_anonymously_desc".tr(), + style: bodyRegular, + ), + trailing: PlatformSwitch( + value: context + .watch() + .getSendCrashlyticsAnonymously(), + onChanged: (value) => context + .read() + .setSendCrashlyticsAnonymously(value), + ), + ), ), - onTap: () => throw Exception(), - ), - ), - ListTile( - title: Text("settings.reset_all".tr(), style: bodyBold), - onTap: () { - showDialog( - context: context, - builder: (context) => AlertDialog( + Visibility( + visible: kDebugMode, + child: ListTile( title: Text( - 'common.alert'.tr(), - style: titleRegular, + "settings.throw_test".tr(), + style: bodyBold, ), - content: Text( - 'settings.reset_all_desc'.tr(), + subtitle: Text( + "settings.throw_test_desc".tr(), style: bodyRegular, ), - actions: [ - TextButton( - child: Text( - "common.cancel".tr(), - style: bodyRegular, + onTap: () => throw Exception(), + ), + ), + ListTile( + title: Text("settings.reset_all".tr(), style: bodyBold), + onTap: () { + OTLNavigator.pushDialog( + context: context, + builder: (context) => AlertDialog( + title: Text( + 'common.alert'.tr(), + style: titleRegular, ), - onPressed: () { - Navigator.pop(context); - }, - ), - TextButton( - child: Text( - "common.delete".tr(), + content: Text( + 'settings.reset_all_desc'.tr(), style: bodyRegular, ), - onPressed: () { - context - .read() - .clearAllValues() - .then((_) => Navigator.pop(context)); - }, + actions: [ + TextButton( + child: Text( + "common.cancel".tr(), + style: bodyRegular, + ), + onPressed: () { + OTLNavigator.pop(context); + }, + ), + TextButton( + child: Text( + "common.delete".tr(), + style: bodyRegular, + ), + onPressed: () { + context + .read() + .clearAllValues() + .then((_) => OTLNavigator.pop(context)); + }, + ), + ], ), - ], - ), - ); - }, - ), - AboutListTile( - applicationName: "", - applicationIcon: - Image.asset("assets/images/logo.png", height: 48.0), - aboutBoxChildren: [ - Text( - "Online Timeplanner with Lectures Plus @ KAIST", - style: bodyRegular, + ); + }, + ), + AboutListTile( + applicationName: "", + applicationIcon: + Image.asset("assets/images/logo.png", height: 48.0), + aboutBoxChildren: [ + Text( + "Online Timeplanner with Lectures Plus @ KAIST", + style: bodyRegular, + ), + IconTextButton( + padding: EdgeInsets.fromLTRB(0, 4, 10, 4), + onTap: () => launchUrl(Uri.parse("mailto:$contactEmail")), + text: contactEmail, + textStyle: + bodyRegular.copyWith(color: OTLColor.pinksMain), + ) + ], ), - IconTextButton( - padding: EdgeInsets.fromLTRB(0, 4, 10, 4), - onTap: () => launchUrl(Uri.parse("mailto:$contactEmail")), - text: contactEmail, - textStyle: bodyRegular.copyWith(color: OTLColor.pinksMain), - ) ], ), - ], + ), ), ), ); diff --git a/lib/pages/timetable_page.dart b/lib/pages/timetable_page.dart index 060de3da..a58318e3 100644 --- a/lib/pages/timetable_page.dart +++ b/lib/pages/timetable_page.dart @@ -1,10 +1,15 @@ import 'package:flutter/material.dart'; -import 'package:otlplus/utils/build_page_route.dart'; +import 'package:otlplus/pages/lecture_detail_page.dart'; +import 'package:otlplus/pages/lecture_search_page.dart'; +import 'package:otlplus/utils/navigator.dart'; import 'package:otlplus/providers/lecture_search_model.dart'; import 'package:otlplus/widgets/delete_dialog.dart'; import 'package:otlplus/widgets/responsive_button.dart'; import 'package:otlplus/widgets/lecture_search.dart'; import 'package:otlplus/widgets/map_view.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; +import 'package:otlplus/widgets/semester_picker.dart'; +import 'package:otlplus/widgets/timetable_mode_control.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/models/lecture.dart'; @@ -46,80 +51,95 @@ class _TimetablePageState extends State { Scrollable.ensureVisible(_selectedKey.currentContext!); }); - return Column( - children: [ - Expanded( - child: ColoredBox( - color: OTLColor.grayF, - child: Column( - children: [ - SizedBox( - height: 60, - child: Row( - children: [ - Expanded( - child: Container( - color: OTLColor.pinksLight, + return OTLLayout( + leading: Padding( + padding: const EdgeInsets.only(left: 16), + child: SemesterPicker( + onSemesterChanged: () { + context.read().setSelectedLecture(null); + context.read().lectureClear(); + }, + ), + ), + trailing: TimetableModeControl( + dropdownIndex: context.watch().selectedMode, + onTap: (mode) => context.read().setMode(mode), + ), + body: Column( + children: [ + Expanded( + child: ColoredBox( + color: OTLColor.grayF, + child: Column( + children: [ + SizedBox( + height: 60, + child: Row( + children: [ + Expanded( child: Container( - padding: const EdgeInsets.symmetric(vertical: 16), - decoration: BoxDecoration( - color: OTLColor.grayF, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16)), + color: OTLColor.pinksLight, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 16), + decoration: BoxDecoration( + color: OTLColor.grayF, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16)), + ), + child: _buildTimetableTabs(context), ), - child: _buildTimetableTabs(context), ), ), - ), - if (mode == 0) - GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - Navigator.push( - context, buildLectureSearchPageRoute()); - }, - child: Padding( - padding: const EdgeInsets.fromLTRB(12, 18, 16, 18), - child: Icon( - Icons.search, - size: 24, - color: OTLColor.pinksMain, + if (mode == 0) + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + OTLNavigator.push(context, LectureSearchPage()); + }, + child: Padding( + padding: + const EdgeInsets.fromLTRB(12, 18, 16, 18), + child: Icon( + Icons.search, + size: 24, + color: OTLColor.pinksMain, + ), ), - ), - ) - else - const SizedBox(width: 16), - ], + ) + else + const SizedBox(width: 16), + ], + ), ), - ), - Expanded( - child: () { - switch (mode) { - case 0: - case 1: - return _buildTimetableMode( - context, lectures, mode == 1); - default: - return MapView(lectures: lectures); - } - }(), - ) - ], + Expanded( + child: () { + switch (mode) { + case 0: + case 1: + return _buildTimetableMode( + context, lectures, mode == 1); + default: + return MapView(lectures: lectures); + } + }(), + ) + ], + ), ), ), - ), - Visibility( - visible: context.watch().resultOpened, - child: Expanded( - child: LectureSearch( - onClosed: () async { - context.read().resetLectureFilter(); - return true; - }, + Visibility( + visible: context.watch().resultOpened, + child: Expanded( + child: LectureSearch( + onClosed: () async { + context.read().resetLectureFilter(); + return true; + }, + ), ), ), - ), - ], + ], + ), ); } @@ -176,13 +196,13 @@ class _TimetablePageState extends State { isExamTime: isExamTime, onTap: () { context.read().loadLecture(lecture.id, true); - Navigator.push(context, buildLectureDetailPageRoute()); + OTLNavigator.push(context, LectureDetailPage()); }, onLongPress: isSelected ? null : () async { bool result = false; - await showDialog( + await OTLNavigator.pushDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( @@ -197,7 +217,7 @@ class _TimetablePageState extends State { color: OTLColor.pinksMain, onTap: () { result = false; - Navigator.pop(context); + OTLNavigator.pop(context); }, ), IconTextButton( @@ -206,7 +226,7 @@ class _TimetablePageState extends State { color: OTLColor.pinksMain, onTap: () { result = true; - Navigator.pop(context); + OTLNavigator.pop(context); }, ), ], diff --git a/lib/pages/user_page.dart b/lib/pages/user_page.dart index e74be39c..e2819ff9 100644 --- a/lib/pages/user_page.dart +++ b/lib/pages/user_page.dart @@ -3,11 +3,13 @@ import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:otlplus/constants/text_styles.dart'; +import 'package:otlplus/pages/liked_review_page.dart'; +import 'package:otlplus/pages/my_review_page.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/navigator.dart'; import 'package:otlplus/widgets/delete_dialog.dart'; import 'package:otlplus/widgets/responsive_button.dart'; +import 'package:otlplus/widgets/otl_scaffold.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/providers/info_model.dart'; @@ -18,80 +20,85 @@ class UserPage extends StatelessWidget { final user = context.watch().user; final isEn = EasyLocalization.of(context)?.currentLocale == Locale('en'); - return Scaffold( - appBar: buildAppBar(context, 'title.my_information'.tr(), false, true), - body: Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - 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(", "), + return OTLScaffold( + child: OTLLayout( + middle: Text('title.my_information'.tr(), style: titleBold), + body: ColoredBox( + color: Colors.white, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + 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(), + ], ), - _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(), - ), - _buildAccount( - 'assets/icons/logout.svg', - () { - context.read().logout(); - context.read().logout(); - Navigator.pop(context); - }, - 'user.logout'.tr(), + ), + _buildNavigateArrowButton( + context, + 'assets/icons/my_review.svg', + 'user.my_review'.tr(), + () => OTLNavigator.push(context, MyReviewPage())), + _buildNavigateArrowButton( + context, + 'assets/icons/liked_review.svg', + 'user.liked_review'.tr(), + () => OTLNavigator.push(context, LikedReviewPage())), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: _buildDivider(), + ), + _buildAccount( + 'assets/icons/logout.svg', + () { + context.read().logout(); + context.read().logout(); + Navigator.pop(context); + }, + 'user.logout'.tr(), + ), + if (Platform.isIOS) + _buildAccount( + Icons.highlight_off, + () async { + showGeneralDialog( + context: context, + barrierColor: Colors.black.withOpacity(0.2), + barrierDismissible: true, + barrierLabel: MaterialLocalizations.of(context) + .modalBarrierDismissLabel, + pageBuilder: (context, _, __) => DeleteDialog( + text: 'user.ask_delete_account'.tr(), + onDelete: () { + context.read().logout(); + context.read().deleteAccount(); + Navigator.pop(context); + }, + ), + ); + }, + 'user.delete_account'.tr(), + ), + ], ), - if (Platform.isIOS) - _buildAccount( - Icons.highlight_off, - () async { - showGeneralDialog( - context: context, - barrierColor: Colors.black.withOpacity(0.2), - barrierDismissible: true, - barrierLabel: MaterialLocalizations.of(context) - .modalBarrierDismissLabel, - pageBuilder: (context, _, __) => DeleteDialog( - text: 'user.ask_delete_account'.tr(), - onDelete: () { - context.read().logout(); - context.read().deleteAccount(); - Navigator.pop(context); - }, - ), - ); - }, - 'user.delete_account'.tr(), - ), - ], + ), ), ), ); diff --git a/lib/utils/build_app_bar.dart b/lib/utils/build_app_bar.dart deleted file mode 100644 index adfc294d..00000000 --- a/lib/utils/build_app_bar.dart +++ /dev/null @@ -1,51 +0,0 @@ -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/widgets/responsive_button.dart'; - -PreferredSizeWidget buildAppBar( - BuildContext context, - String title, - bool isLeading, - bool isActions, -) { - return AppBar( - title: appBarPadding(Text(title.tr(), style: titleBold)), - leading: isLeading - ? appBarPadding( - IconTextButton( - onTap: () => Navigator.pop(context), - icon: Icons.navigate_before, - padding: EdgeInsets.all(16), - ), - ) - : null, - actions: isActions - ? [ - appBarPadding( - IconTextButton( - icon: Icons.close, - onTap: () => Navigator.popUntil( - context, - (route) => route.isFirst, - ), - padding: EdgeInsets.all(16), - ), - ), - ] - : null, - flexibleSpace: - SafeArea(child: Container(color: OTLColor.pinksMain, height: 5.0)), - toolbarHeight: kToolbarHeight + 5.0, - backgroundColor: OTLColor.pinksLight, - foregroundColor: OTLColor.gray0, - elevation: 0.0, - centerTitle: true, - automaticallyImplyLeading: false, - ); -} - -Widget appBarPadding(Widget widget) { - return Padding(padding: const EdgeInsets.only(top: 5.0), child: widget); -} diff --git a/lib/utils/build_page_route.dart b/lib/utils/build_page_route.dart deleted file mode 100644 index 5bcdf2c8..00000000 --- a/lib/utils/build_page_route.dart +++ /dev/null @@ -1,191 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:otlplus/pages/course_detail_page.dart'; -import 'package:otlplus/pages/course_search_page.dart'; -import 'package:otlplus/pages/lecture_detail_page.dart'; -import 'package:otlplus/pages/lecture_search_page.dart'; -import 'package:otlplus/pages/liked_review_page.dart'; -import 'package:otlplus/pages/my_review_page.dart'; -import 'package:otlplus/pages/people_page.dart'; -import 'package:otlplus/pages/privacy_page.dart'; -import 'package:otlplus/pages/settings_page.dart'; -import 'package:otlplus/pages/user_page.dart'; - -Route buildCourseDetailPageRoute() { - return PageRouteBuilder( - pageBuilder: (_, animation, __) => CourseDetailPage(), - transitionsBuilder: (_, animation, __, child) { - const begin = Offset(1.0, 0.0); - const end = Offset.zero; - final curveTween = CurveTween(curve: Curves.ease); - final tween = Tween(begin: begin, end: end).chain(curveTween); - final offsetAnimation = animation.drive(tween); - - return SlideTransition( - position: offsetAnimation, - child: child, - ); - }, - ); -} - -Route buildLectureDetailPageRoute() { - return PageRouteBuilder( - pageBuilder: (_, animation, __) => LectureDetailPage(), - transitionsBuilder: (_, animation, __, child) { - const begin = Offset(1.0, 0.0); - const end = Offset.zero; - final curveTween = CurveTween(curve: Curves.ease); - final tween = Tween(begin: begin, end: end).chain(curveTween); - final offsetAnimation = animation.drive(tween); - - return SlideTransition( - position: offsetAnimation, - child: child, - ); - }, - ); -} - -Route buildUserPageRoute() { - return PageRouteBuilder( - pageBuilder: (_, animation, __) => UserPage(), - transitionsBuilder: (_, animation, __, child) { - const begin = Offset(0.0, 1.0); - const end = Offset.zero; - final curveTween = CurveTween(curve: Curves.ease); - final tween = Tween(begin: begin, end: end).chain(curveTween); - final offsetAnimation = animation.drive(tween); - - return SlideTransition( - position: offsetAnimation, - child: child, - ); - }, - ); -} - -Route buildSettingsPageRoute() { - return PageRouteBuilder( - pageBuilder: (_, animation, __) => SettingsPage(), - transitionsBuilder: (_, animation, __, child) { - const begin = Offset(0.0, 1.0); - const end = Offset.zero; - final curveTween = CurveTween(curve: Curves.ease); - final tween = Tween(begin: begin, end: end).chain(curveTween); - final offsetAnimation = animation.drive(tween); - - return SlideTransition( - position: offsetAnimation, - child: child, - ); - }, - ); -} - -Route buildMyReviewPageRoute() { - return PageRouteBuilder( - pageBuilder: (_, animation, __) => MyReviewPage(), - transitionsBuilder: (_, animation, __, child) { - const begin = Offset(1.0, 0.0); - const end = Offset.zero; - final curveTween = CurveTween(curve: Curves.ease); - final tween = Tween(begin: begin, end: end).chain(curveTween); - final offsetAnimation = animation.drive(tween); - - return SlideTransition( - position: offsetAnimation, - child: child, - ); - }, - ); -} - -Route buildLikedReviewPageRoute() { - return PageRouteBuilder( - pageBuilder: (_, animation, __) => LikedReviewPage(), - transitionsBuilder: (_, animation, __, child) { - const begin = Offset(1.0, 0.0); - const end = Offset.zero; - final curveTween = CurveTween(curve: Curves.ease); - final tween = Tween(begin: begin, end: end).chain(curveTween); - final offsetAnimation = animation.drive(tween); - - return SlideTransition( - position: offsetAnimation, - child: child, - ); - }, - ); -} - -Route buildCourseSearchPageRoute() { - return PageRouteBuilder( - pageBuilder: (_, animation, __) => CourseSearchPage(openKeyboard: true), - transitionsBuilder: (_, animation, __, child) { - const begin = Offset(1.0, 0.0); - const end = Offset.zero; - final curveTween = CurveTween(curve: Curves.ease); - final tween = Tween(begin: begin, end: end).chain(curveTween); - final offsetAnimation = animation.drive(tween); - - return SlideTransition( - position: offsetAnimation, - child: child, - ); - }, - ); -} - -Route buildLectureSearchPageRoute() { - return PageRouteBuilder( - pageBuilder: (_, animation, __) => LectureSearchPage(openKeyboard: true), - transitionsBuilder: (_, animation, __, child) { - const begin = Offset(1.0, 0.0); - const end = Offset.zero; - final curveTween = CurveTween(curve: Curves.ease); - final tween = Tween(begin: begin, end: end).chain(curveTween); - final offsetAnimation = animation.drive(tween); - - return SlideTransition( - position: offsetAnimation, - child: child, - ); - }, - ); -} - -Route buildPrivacyPageRoute() { - return PageRouteBuilder( - pageBuilder: (_, animation, __) => PrivacyPage(), - transitionsBuilder: (_, animation, __, child) { - const begin = Offset(0.0, 1.0); - const end = Offset.zero; - final curveTween = CurveTween(curve: Curves.ease); - final tween = Tween(begin: begin, end: end).chain(curveTween); - final offsetAnimation = animation.drive(tween); - - return SlideTransition( - position: offsetAnimation, - child: child, - ); - }, - ); -} - -Route buildPeoplePageRoute() { - return PageRouteBuilder( - pageBuilder: (_, animation, __) => PeoplePage(), - transitionsBuilder: (_, animation, __, child) { - const begin = Offset(0.0, 1.0); - const end = Offset.zero; - final curveTween = CurveTween(curve: Curves.ease); - final tween = Tween(begin: begin, end: end).chain(curveTween); - final offsetAnimation = animation.drive(tween); - - return SlideTransition( - position: offsetAnimation, - child: child, - ); - }, - ); -} diff --git a/lib/utils/get_text_height.dart b/lib/utils/get_text_height.dart index afe401d1..c029bff2 100644 --- a/lib/utils/get_text_height.dart +++ b/lib/utils/get_text_height.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -double getTextHeight( +Size getTextSize( BuildContext context, { required String text, required TextStyle style, @@ -10,8 +10,8 @@ double getTextHeight( text: TextSpan(text: text, style: style), textDirection: TextDirection.ltr, textScaleFactor: MediaQuery.of(context).textScaleFactor, - )..layout(maxWidth: maxWidth); - return textPainter.size.height; + )..layout(minWidth: 0, maxWidth: maxWidth); + return textPainter.size; } double singleHeight(BuildContext context, TextStyle style) { diff --git a/lib/utils/navigator.dart b/lib/utils/navigator.dart new file mode 100644 index 00000000..6940caed --- /dev/null +++ b/lib/utils/navigator.dart @@ -0,0 +1,183 @@ +import 'package:flutter/material.dart'; + +enum OTLNavigatorTransition { rightLeft, downUp, immediate } + +enum _TransitionHistory { rightLeft, downUp, immediate, dialog } + +class OTLNavigator { + static List _rightleftTransitionHistory = []; + static List _downupTransitionHistory = []; + static List<_TransitionHistory> _history = []; + + static void _removeLastHistory() { + switch (_history.removeLast()) { + case _TransitionHistory.rightLeft: + _rightleftTransitionHistory.removeLast(); + break; + case _TransitionHistory.downUp: + case _TransitionHistory.immediate: + _downupTransitionHistory.removeLast(); + break; + case _TransitionHistory.dialog: + break; + } + } + + static void _removeLastUntil( + bool Function(_TransitionHistory mode) predicate) { + while (!predicate(_history.last)) { + _removeLastHistory(); + } + _removeLastHistory(); + } + + static Future push(BuildContext context, Widget page, + {OTLNavigatorTransition transition = OTLNavigatorTransition.rightLeft}) { + Route _route; + switch (transition) { + case OTLNavigatorTransition.rightLeft: + _route = buildRightLeftPageRoute(page); + _rightleftTransitionHistory.add(_route); + _history.add(_TransitionHistory.rightLeft); + break; + case OTLNavigatorTransition.downUp: + _route = buildDownUpPageRoute(page); + _downupTransitionHistory.add(_route); + _history.add(_TransitionHistory.downUp); + break; + case OTLNavigatorTransition.immediate: + _route = buildImmediatePageRoute(page); + _downupTransitionHistory.add(_route); + _history.add(_TransitionHistory.immediate); + break; + } + return Navigator.of(context).push(_route); + } + + static Future pushRoot( + BuildContext context, Widget page, + {OTLNavigatorTransition transition = OTLNavigatorTransition.immediate}) { + _rightleftTransitionHistory.clear(); + _downupTransitionHistory.clear(); + _history.clear(); + Route _route; + switch (transition) { + case OTLNavigatorTransition.rightLeft: + _route = buildRightLeftPageRoute(page); + break; + case OTLNavigatorTransition.downUp: + _route = buildDownUpPageRoute(page); + break; + case OTLNavigatorTransition.immediate: + _route = buildImmediatePageRoute(page); + break; + } + return Navigator.of(context) + .pushAndRemoveUntil(_route, (Route route) => false); + } + + static void pop(BuildContext context, + {OTLNavigatorTransition? until, T? result}) { + NavigatorState navigator = Navigator.of(context); + assert(_history.isNotEmpty); + if (until == null) { + _removeLastHistory(); + return navigator.pop(result); + } else if (until == OTLNavigatorTransition.rightLeft) { + assert(_rightleftTransitionHistory.isNotEmpty); + navigator.popUntil((Route route) => + _rightleftTransitionHistory.last == route || route.isFirst); + _removeLastUntil((mode) => mode == _TransitionHistory.rightLeft); + return navigator.pop(result); + } else { + assert(_downupTransitionHistory.isNotEmpty); + navigator.popUntil((Route route) => + _downupTransitionHistory.last == route || route.isFirst); + _removeLastUntil((mode) => (mode == _TransitionHistory.downUp || + mode == _TransitionHistory.immediate)); + return navigator.pop(result); + } + } + + static bool get canPop => _history.isNotEmpty; + + static bool get canPopRightLeft => _rightleftTransitionHistory.isNotEmpty; + static bool get canPopDownUp => _downupTransitionHistory.isNotEmpty; + + static Future pushDialog({ + required BuildContext context, + required WidgetBuilder builder, + bool barrierDismissible = true, + Color? barrierColor = Colors.black54, + String? barrierLabel, + bool useSafeArea = true, + bool useRootNavigator = true, + RouteSettings? routeSettings, + Offset? anchorPoint, + TraversalEdgeBehavior? traversalEdgeBehavior, + }) { + _history.add(_TransitionHistory.dialog); + return showDialog( + context: context, + builder: builder, + barrierDismissible: barrierDismissible, + barrierColor: barrierColor, + barrierLabel: barrierLabel, + useSafeArea: useSafeArea, + useRootNavigator: useRootNavigator, + routeSettings: routeSettings, + anchorPoint: anchorPoint, + traversalEdgeBehavior: traversalEdgeBehavior, + ); + } +} + +Route buildRightLeftPageRoute(Widget page) { + return PageRouteBuilder( + pageBuilder: (_, animation, __) => page, + transitionsBuilder: (_, animation, __, child) { + const begin = Offset(1.0, 0.0); + const end = Offset.zero; + final curveTween = CurveTween(curve: Curves.ease); + final tween = Tween(begin: begin, end: end).chain(curveTween); + final offsetAnimation = animation.drive(tween); + + return SlideTransition( + position: offsetAnimation, + child: child, + ); + }, + ); +} + +Route buildDownUpPageRoute(Widget page) { + return PageRouteBuilder( + pageBuilder: (_, animation, __) => page, + transitionsBuilder: (_, animation, __, child) { + const begin = Offset(0.0, 1.0); + const end = Offset.zero; + final curveTween = CurveTween(curve: Curves.ease); + final tween = Tween(begin: begin, end: end).chain(curveTween); + final offsetAnimation = animation.drive(tween); + + return SlideTransition( + position: offsetAnimation, + child: SafeArea( + top: animation.value != 1, + right: false, + left: false, + bottom: false, + child: Align(alignment: Alignment.bottomCenter, child: child), + ), + ); + }, + ); +} + +Route buildImmediatePageRoute(Widget page) { + return PageRouteBuilder( + pageBuilder: (_, animation, __) => page, + transitionDuration: Duration.zero, + reverseTransitionDuration: Duration.zero, + ); +} diff --git a/lib/widgets/base_layout.dart b/lib/widgets/base_layout.dart deleted file mode 100644 index d8d71ad5..00000000 --- a/lib/widgets/base_layout.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:otlplus/constants/color.dart'; - -class BaseLayout extends StatefulWidget { - final Widget body; - final Widget? leading; - final Widget? middle; - final Widget? trailing; - final bool enableBackButton; - final VoidCallback? onBack; - final Color sheetBackgroundColor; - - const BaseLayout({ - Key? key, - required this.body, - this.leading, - this.middle, - this.trailing, - this.enableBackButton = false, - this.onBack, - this.sheetBackgroundColor = Colors.white, - }) : super(key: key); - - @override - State createState() => _BaseLayoutState(); -} - -//NavigationToolbar -class _BaseLayoutState extends State { - @override - Widget build(BuildContext context) { - final NavigatorState? navigator = Navigator.maybeOf(context); - return ColoredBox( - color: OTLColor.pinksLight, - child: Column( - children: [ - SizedBox( - height: kToolbarHeight, - child: NavigationToolbar( - centerMiddle: true, - leading: Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - if (navigator != null && - navigator.canPop() && - widget.enableBackButton) - _BackButton(onBack: widget.onBack), - if (widget.leading != null) widget.leading!, - ], - ), - middle: widget.middle, - trailing: widget.trailing, - )), - Expanded( - child: ClipRRect( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), topRight: Radius.circular(16)), - child: ColoredBox( - color: widget.sheetBackgroundColor, - child: SizedBox(width: double.infinity, child: widget.body), - ), - )) - ], - ), - ); - } -} - -class _BackButton extends StatelessWidget { - final Function()? onBack; - const _BackButton({Key? key, this.onBack}) : super(key: key); - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () { - onBack?.call(); - Navigator.maybePop(context); - }, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: SvgPicture.asset('assets/icons/back.svg', - height: 24.0, - width: 24.0, - colorFilter: ColorFilter.mode(Color(0xFF000000), BlendMode.srcIn)), - ), - ); - } -} diff --git a/lib/widgets/lecture_group_block_row.dart b/lib/widgets/lecture_group_block_row.dart index dfa9215b..b5e3bfa1 100644 --- a/lib/widgets/lecture_group_block_row.dart +++ b/lib/widgets/lecture_group_block_row.dart @@ -5,6 +5,7 @@ import 'package:otlplus/extensions/lecture.dart'; import 'package:otlplus/models/lecture.dart'; import 'package:otlplus/providers/lecture_search_model.dart'; import 'package:otlplus/widgets/responsive_button.dart'; +import 'package:otlplus/utils/navigator.dart'; import 'package:provider/provider.dart'; import '../providers/timetable_model.dart'; @@ -134,7 +135,7 @@ class _LectureGroupBlockRowState extends State { lecture: lec, onOverlap: (lectures) async { bool result = false; - await showDialog( + await OTLNavigator.pushDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( @@ -148,7 +149,7 @@ class _LectureGroupBlockRowState extends State { color: OTLColor.pinksMain, onTap: () { result = false; - Navigator.pop(context); + OTLNavigator.pop(context); }, ), IconTextButton( @@ -157,7 +158,7 @@ class _LectureGroupBlockRowState extends State { color: OTLColor.pinksMain, onTap: () { result = true; - Navigator.pop(context); + OTLNavigator.pop(context); }, ), ], diff --git a/lib/widgets/lecture_group_simple_block.dart b/lib/widgets/lecture_group_simple_block.dart index ad27a192..2165efa9 100644 --- a/lib/widgets/lecture_group_simple_block.dart +++ b/lib/widgets/lecture_group_simple_block.dart @@ -1,8 +1,9 @@ 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/widgets/responsive_button.dart'; +import 'package:otlplus/pages/lecture_detail_page.dart'; +import 'package:otlplus/utils/navigator.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/extensions/lecture.dart'; @@ -60,10 +61,8 @@ class LectureGroupSimpleBlock extends StatelessWidget { context .read() .loadLecture(lecture.id, false); - Navigator.push( - context, - buildLectureDetailPageRoute(), - ); + OTLNavigator.push(context, + LectureDetailPage(fromCourseDetailPage: true)); }, child: Padding( padding: const EdgeInsets.symmetric( diff --git a/lib/widgets/lecture_search.dart b/lib/widgets/lecture_search.dart index 718ffa9c..0f0f1cec 100644 --- a/lib/widgets/lecture_search.dart +++ b/lib/widgets/lecture_search.dart @@ -1,6 +1,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_detail_page.dart'; +import 'package:otlplus/utils/navigator.dart'; import 'package:otlplus/pages/lecture_search_page.dart'; import 'package:otlplus/widgets/responsive_button.dart'; import 'package:provider/provider.dart'; @@ -43,10 +44,8 @@ class _LectureSearchState extends State { child: ClipRRect( borderRadius: BorderRadius.all(Radius.circular(8.0)), child: BackgroundButton( - onTap: () => Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => - LectureSearchPage(openKeyboard: false))), + onTap: () => + OTLNavigator.push(context, LectureSearchPage()), color: Color(0xFFF9F0F0), child: Padding( padding: EdgeInsets.all(8.0), @@ -109,7 +108,7 @@ class _LectureSearchState extends State { context .read() .loadLecture(lecture.id, true); - Navigator.push(context, buildLectureDetailPageRoute()); + OTLNavigator.push(context, LectureDetailPage()); }, ), separatorBuilder: (context, index) => SizedBox(height: 8)); diff --git a/lib/widgets/map_view.dart b/lib/widgets/map_view.dart index 4a719ce1..70b32673 100644 --- a/lib/widgets/map_view.dart +++ b/lib/widgets/map_view.dart @@ -294,10 +294,11 @@ class _MapViewState extends State { ? classtime.classroom : classtime.classroomEn : classtime.roomName; - final isMultiLine = (getTextHeight(context, - text: location, - style: labelRegular, - maxWidth: 143) ~/ + final isMultiLine = (getTextSize(context, + text: location, + style: labelRegular, + maxWidth: 143) + .height ~/ singleHeight(context, labelRegular)) > 1; diff --git a/lib/widgets/otl_scaffold.dart b/lib/widgets/otl_scaffold.dart new file mode 100644 index 00000000..0996c9c1 --- /dev/null +++ b/lib/widgets/otl_scaffold.dart @@ -0,0 +1,207 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:otlplus/constants/color.dart'; +import 'package:otlplus/utils/navigator.dart'; +import 'package:otlplus/widgets/responsive_button.dart'; + +class OTLScaffold extends StatelessWidget { + // final Widget body; + // final Widget? leading; + // final Widget? middle; + // final Widget? trailing; + // final Widget? bottomNavigationBar; + // final Function()? onBack; + // final Color sheetBackgroundColor; + // final bool resizeToAvoidBottomInset; + // final bool disableBackButton; + + OTLScaffold({ + Key? key, + required this.child, + this.floatingActionButton, + this.floatingActionButtonLocation, + this.floatingActionButtonAnimator, + this.persistentFooterButtons, + this.persistentFooterAlignment = AlignmentDirectional.centerEnd, + this.drawer, + this.onDrawerChanged, + this.endDrawer, + this.onEndDrawerChanged, + this.bottomNavigationBar, + this.bottomSheet, + this.backgroundColor = OTLColor.pinksLight, + this.resizeToAvoidBottomInset = false, + this.primary = true, + this.drawerDragStartBehavior = DragStartBehavior.start, + this.extendBody = false, + this.drawerScrimColor, + this.drawerEdgeDragWidth, + this.drawerEnableOpenDragGesture = true, + this.endDrawerEnableOpenDragGesture = true, + this.restorationId, + }) : super(key: key) { + if (backgroundColor != null && backgroundColor!.computeLuminance() < 0.5) + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light); + else + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark); + } + final Widget child; + final bool extendBody; + final Widget? floatingActionButton; + final FloatingActionButtonLocation? floatingActionButtonLocation; + final FloatingActionButtonAnimator? floatingActionButtonAnimator; + final List? persistentFooterButtons; + final AlignmentDirectional persistentFooterAlignment; + final Widget? drawer; + final DrawerCallback? onDrawerChanged; + final Widget? endDrawer; + final DrawerCallback? onEndDrawerChanged; + final Color? drawerScrimColor; + final Color? backgroundColor; + final Widget? bottomNavigationBar; + final Widget? bottomSheet; + final bool? resizeToAvoidBottomInset; + final bool primary; + final DragStartBehavior drawerDragStartBehavior; + final double? drawerEdgeDragWidth; + final bool drawerEnableOpenDragGesture; + final bool endDrawerEnableOpenDragGesture; + final String? restorationId; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + preferredSize: Size.fromHeight(5.0), + child: SafeArea( + top: true, + bottom: false, + left: false, + right: false, + child: SizedBox( + height: 5, + child: ColoredBox(color: OTLColor.pinksMain), + ), + ), + ), + body: child, + floatingActionButton: floatingActionButton, + floatingActionButtonLocation: floatingActionButtonLocation, + floatingActionButtonAnimator: floatingActionButtonAnimator, + persistentFooterButtons: persistentFooterButtons, + persistentFooterAlignment: persistentFooterAlignment, + drawer: drawer, + onDrawerChanged: onDrawerChanged, + endDrawer: endDrawer, + onEndDrawerChanged: onEndDrawerChanged, + bottomNavigationBar: bottomNavigationBar, + bottomSheet: bottomSheet, + backgroundColor: backgroundColor, + resizeToAvoidBottomInset: resizeToAvoidBottomInset, + primary: primary, + drawerDragStartBehavior: drawerDragStartBehavior, + extendBody: extendBody, + drawerScrimColor: drawerScrimColor, + drawerEdgeDragWidth: drawerEdgeDragWidth, + drawerEnableOpenDragGesture: drawerEnableOpenDragGesture, + endDrawerEnableOpenDragGesture: endDrawerEnableOpenDragGesture, + restorationId: restorationId, + ); + } +} + +class OTLLayout extends StatefulWidget { + const OTLLayout({ + Key? key, + this.middle, + this.leading, + this.trailing, + this.extendBodyBehindAppBar = false, + required this.body, + }) : super(key: key); + final Widget? middle; + final Widget? leading; + final Widget? trailing; + final bool extendBodyBehindAppBar; + final Widget body; + + @override + State createState() => _OTLLayoutState(); +} + +class _OTLLayoutState extends State { + final bool canPopRightLeft = OTLNavigator.canPopRightLeft; + final bool canPopDownUp = OTLNavigator.canPopDownUp; + + @override + Widget build(BuildContext context) { + final ScaffoldState? scaffold = Scaffold.maybeOf(context); + final bool hasDrawer = scaffold?.hasDrawer ?? false; + final bool hasEndDrawer = scaffold?.hasEndDrawer ?? false; + + return Stack( + fit: StackFit.expand, + alignment: Alignment.topCenter, + children: [ + Positioned.fill( + top: widget.extendBodyBehindAppBar ? 0 : kToolbarHeight, + child: widget.body, + ), + Positioned( + top: 0, + left: 0, + right: 0, + child: SizedBox( + height: kToolbarHeight, + child: NavigationToolbar( + leading: (widget.leading != null || + canPopRightLeft || + hasDrawer) + ? Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (canPopRightLeft) + IconTextButton( + icon: Icons.navigate_before, + onTap: () => OTLNavigator.pop(context, + until: OTLNavigatorTransition.rightLeft), + padding: EdgeInsets.all(16)), + if (hasDrawer) + IconTextButton( + icon: Icons.menu, + onTap: () => Scaffold.of(context).openDrawer(), + padding: EdgeInsets.all(16)), + if (widget.leading != null) widget.leading!, + ], + ) + : null, + middle: widget.middle, + trailing: + (widget.trailing != null || canPopDownUp || hasEndDrawer) + ? Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.trailing != null) widget.trailing!, + if (hasEndDrawer) + IconTextButton( + icon: Icons.menu, + onTap: () => + Scaffold.of(context).openEndDrawer(), + padding: EdgeInsets.all(16)), + if (canPopDownUp) + IconTextButton( + icon: Icons.close, + onTap: () => OTLNavigator.pop(context, + until: OTLNavigatorTransition.downUp), + padding: EdgeInsets.all(16)), + ], + ) + : null, + middleSpacing: 0, + )), + ) + ], + ); + } +} diff --git a/lib/widgets/pop_up.dart b/lib/widgets/pop_up.dart index bc22202a..223fb214 100644 --- a/lib/widgets/pop_up.dart +++ b/lib/widgets/pop_up.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/widgets/responsive_button.dart'; +import 'package:otlplus/utils/navigator.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -56,7 +57,7 @@ class _PopUpState extends State { ), IconTextButton( onTap: () async { - Navigator.pop(context); + OTLNavigator.pop(context); }, icon: Icons.close, color: OTLColor.grayF, diff --git a/lib/widgets/review_block.dart b/lib/widgets/review_block.dart index d162f6ed..01694436 100644 --- a/lib/widgets/review_block.dart +++ b/lib/widgets/review_block.dart @@ -7,6 +7,7 @@ import 'package:otlplus/dio_provider.dart'; import 'package:otlplus/extensions/review.dart'; import 'package:otlplus/models/review.dart'; import 'package:otlplus/widgets/responsive_button.dart'; +import 'package:otlplus/utils/navigator.dart'; import 'package:otlplus/widgets/expandable_text.dart'; class ReviewBlock extends StatefulWidget { @@ -185,7 +186,7 @@ class _ReviewBlockState extends State { } void _report() { - showDialog( + OTLNavigator.pushDialog( context: context, builder: (_) => AlertDialog( title: Text('안내'), @@ -195,7 +196,7 @@ class _ReviewBlockState extends State { new TextButton( child: new Text("확인"), onPressed: () { - Navigator.pop(context); + OTLNavigator.pop(context); }, ), ], diff --git a/lib/widgets/search_filter_panel.dart b/lib/widgets/search_filter_panel.dart index 69651652..21754b6a 100644 --- a/lib/widgets/search_filter_panel.dart +++ b/lib/widgets/search_filter_panel.dart @@ -5,6 +5,7 @@ import 'package:otlplus/constants/color.dart'; import 'package:otlplus/constants/text_styles.dart'; import 'package:otlplus/models/filter.dart'; import 'package:otlplus/widgets/responsive_button.dart'; +import 'package:otlplus/utils/get_text_height.dart'; class SearchFilterPanel extends StatefulWidget { final Map filter; @@ -293,16 +294,6 @@ class SilderSelection extends StatefulWidget { } class _SilderSelectionState extends State { - double _textWidth(String text, TextStyle style) { - final TextPainter textPainter = TextPainter( - text: TextSpan(text: text, style: style), - maxLines: 1, - textDirection: TextDirection.rtl, - textScaleFactor: MediaQuery.of(context).textScaleFactor) - ..layout(minWidth: 0, maxWidth: double.infinity); - return textPainter.size.width; - } - double _value = 0; TextStyle labelTextStyle = TextStyle( @@ -325,11 +316,15 @@ class _SilderSelectionState extends State { padding: EdgeInsets.only(left: 2, right: 10), child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { - double leftPadding = _textWidth( - widget.selectList.reversed.first.first.label, labelTextStyle) / + double leftPadding = getTextSize(context, + text: widget.selectList.reversed.first.first.label, + style: labelTextStyle) + .width / 2; - double rightPadding = _textWidth( - widget.selectList.reversed.last.first.label, labelTextStyle) / + double rightPadding = getTextSize(context, + text: widget.selectList.reversed.last.first.label, + style: labelTextStyle) + .width / 2; double divisionWidth = (constraints.maxWidth - leftPadding - rightPadding) / divisions; @@ -395,18 +390,23 @@ class _SilderSelectionState extends State { ) : Spacer( flex: (divisionWidth - - (_textWidth( - widget.selectList.reversed - .elementAt(index ~/ 2) - .first - .label, - labelTextStyle) + - _textWidth( - widget.selectList.reversed - .elementAt((index ~/ 2) + 1) - .first - .label, - labelTextStyle)) / + (getTextSize(context, + text: widget + .selectList.reversed + .elementAt(index ~/ 2) + .first + .label, + style: labelTextStyle) + .width + + getTextSize(context, + text: widget + .selectList.reversed + .elementAt( + (index ~/ 2) + 1) + .first + .label, + style: labelTextStyle) + .width) / 2) .toInt(), )), diff --git a/lib/widgets/search_textfield.dart b/lib/widgets/search_textfield.dart index f221b697..cfc46d98 100644 --- a/lib/widgets/search_textfield.dart +++ b/lib/widgets/search_textfield.dart @@ -24,36 +24,38 @@ class SearchTextfield extends StatefulWidget { class _SearchTextfieldState extends State { @override Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration( + return ClipRRect( + borderRadius: BorderRadius.circular(8.0), + child: ColoredBox( color: OTLColor.grayF, - borderRadius: BorderRadius.circular(8.0), - ), - padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), - child: Row( - 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: TextField( - autofocus: widget.autoFocus ?? false, - controller: widget.textController, - focusNode: widget.focusNode, - onSubmitted: (value) { - widget.focusNode?.unfocus(); - }, - style: bodyRegular, - decoration: InputDecoration( - hintText: "common.search_hint".tr(), - hintStyle: evenBodyRegular.copyWith(color: OTLColor.grayA), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), + child: Row( + 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: TextField( + autofocus: widget.autoFocus ?? false, + controller: widget.textController, + focusNode: widget.focusNode, + onSubmitted: (value) { + widget.focusNode?.unfocus(); + }, + style: bodyRegular, + decoration: InputDecoration( + hintText: "common.search_hint".tr(), + hintStyle: evenBodyRegular.copyWith(color: OTLColor.grayA), + ), + ), ), - ), + ], ), - ], + ), ), ); } diff --git a/lib/widgets/semester_picker.dart b/lib/widgets/semester_picker.dart index 2563ea7d..de1ac66e 100644 --- a/lib/widgets/semester_picker.dart +++ b/lib/widgets/semester_picker.dart @@ -9,10 +9,8 @@ import 'package:otlplus/providers/timetable_model.dart'; class SemesterPicker extends StatefulWidget { final bool isExamTime; final Function() onSemesterChanged; - final VoidCallback? onTap; - SemesterPicker( - {this.isExamTime = false, required this.onSemesterChanged, this.onTap}); + SemesterPicker({this.isExamTime = false, required this.onSemesterChanged}); @override _SemesterPickerState createState() => _SemesterPickerState(); @@ -34,11 +32,10 @@ class _SemesterPickerState extends State { } Widget _buildTitle(BuildContext context) { - return IconTextButton( - onTap: widget.onTap, + return Padding( padding: const EdgeInsets.all(8.0), - text: context.watch().selectedSemester.title, - textStyle: displayBold.copyWith(height: 1.448), + child: Text(context.watch().selectedSemester.title, + style: displayBold.copyWith(height: 1.448)), ); } diff --git a/lib/widgets/timetable_block.dart b/lib/widgets/timetable_block.dart index c08c609c..55df053c 100644 --- a/lib/widgets/timetable_block.dart +++ b/lib/widgets/timetable_block.dart @@ -65,8 +65,9 @@ class TimetableBlock extends StatelessWidget { if (showClassroom) { maxLines = (validHeight - - getTextHeight(context, - text: title, style: labelRegular, maxWidth: 54)) ~/ + getTextSize(context, + text: title, style: labelRegular, maxWidth: 54) + .height) ~/ lineHeight; contents.add(Expanded(