Skip to content

Commit

Permalink
feat: Create Schedule details page with quick-select
Browse files Browse the repository at this point in the history
  • Loading branch information
0niel committed Jan 3, 2024
1 parent f5315a7 commit ef6ccc8
Show file tree
Hide file tree
Showing 13 changed files with 536 additions and 160 deletions.
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ if (keystorePropertiesFile.exists()) {
}

android {
compileSdkVersion 33
compileSdkVersion 34
ndkVersion flutter.ndkVersion

compileOptions {
Expand Down
13 changes: 12 additions & 1 deletion lib/presentation/core/routes/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ import 'package:rtu_mirea_app/presentation/pages/profile/profile_scores_page.dar
import 'package:rtu_mirea_app/presentation/pages/profile/profile_page.dart';
import 'package:rtu_mirea_app/presentation/pages/profile/profile_settings_page.dart';
import 'package:rtu_mirea_app/presentation/widgets/images_view_gallery.dart';
import 'package:rtu_mirea_app/schedule/view/schedule_details_page.dart';
// import 'package:rtu_mirea_app/rating_system_calculator/view/rating_system_calculator_page.dart';
import 'package:rtu_mirea_app/schedule/view/schedule_page.dart';
import 'package:rtu_mirea_app/search/view/search_page.dart';
import 'package:rtu_mirea_app/services/view/view.dart';
import 'package:rtu_mirea_app/stories/stories.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:university_app_server_api/client.dart';

final GlobalKey<NavigatorState> _rootNavigatorKey =
GlobalKey<NavigatorState>(debugLabel: 'root');
Expand Down Expand Up @@ -79,7 +81,16 @@ GoRouter createRouter() => GoRouter(
routes: [
GoRoute(
path: 'search',
builder: (context, state) => const SearchPage(),
builder: (context, state) =>
SearchPage(query: state.extra as String?),
),
GoRoute(
path: 'details',
builder: (context, state) {
return ScheduleDetailsPage(
lesson: state.extra as LessonSchedulePart,
);
},
),
],
),
Expand Down
234 changes: 234 additions & 0 deletions lib/schedule/view/schedule_details_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:latlong2/latlong.dart';
import 'package:rtu_mirea_app/presentation/theme.dart';
import 'package:rtu_mirea_app/presentation/typography.dart';
import 'package:rtu_mirea_app/schedule/schedule.dart';
import 'package:university_app_server_api/client.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_cancellable_tile_provider/flutter_map_cancellable_tile_provider.dart';
import 'package:url_launcher/url_launcher_string.dart';

class ScheduleDetailsPage extends StatefulWidget {
const ScheduleDetailsPage({super.key, required this.lesson});

final LessonSchedulePart lesson;

@override
State<ScheduleDetailsPage> createState() => _ScheduleDetailsPageState();
}

class _ScheduleDetailsPageState extends State<ScheduleDetailsPage> {
final mapTileLayer = TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'ninja.mirea.mireaapp',
tileProvider: CancellableNetworkTileProvider(),
);

@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppTheme.colors.background01,
appBar: AppBar(
title: const Text(
'Предмет',
),
),
body: Padding(
padding: const EdgeInsets.all(24),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _buildPageContent(),
),
),
),
);
}

List<Widget> _buildPageContent() {
List<Widget> content = [];

content.addAll([
_buildLessonTitle(),
const Divider(),
_buildLessonType(),
const Divider(),
..._buildClassroomDetails(),
]);

if (widget.lesson.groups != null && widget.lesson.groups!.isNotEmpty) {
content.addAll([
_buildGroups(),
const Divider(),
]);
}

if (widget.lesson.teachers.isNotEmpty) {
content.addAll([
_buildTeachers(),
]);
}

return content;
}

ListTile _buildLessonTitle() {
return ListTile(
title: Text(
'Название предмета'.toUpperCase(),
),
subtitle: Text(widget.lesson.subject),
);
}

// Build the lesson type
ListTile _buildLessonType() {
return ListTile(
title: Text('Тип занятия'.toUpperCase()),
subtitle: Row(
children: [
Container(
height: 7,
width: 7,
decoration: BoxDecoration(
color: LessonCard.getColorByType(widget.lesson.lessonType),
borderRadius: BorderRadius.circular(7),
),
),
const SizedBox(width: 8),
Text(
LessonCard.getLessonTypeName(widget.lesson.lessonType),
style: AppTextStyle.titleM,
),
],
),
);
}

// Build the classroom details
List<Widget> _buildClassroomDetails() {
return widget.lesson.classrooms.map((classroom) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListTile(
title: Text('Аудитория'.toUpperCase()),
subtitle: Text(classroom.name),
trailing: const Icon(
Icons.chevron_right_sharp,
size: 24,
),
onTap: () => context.go('/schedule/search', extra: classroom.name),
),
const Divider(),
if (classroom.campus != null &&
classroom.campus?.latitude != null) ...[
ListTile(
title: Text('Кампус'.toUpperCase()),
subtitle: Text(classroom.campus!.name),
),
_buildClassroomMap(classroom),
const Divider(
height: 32,
),
],
],
);
}).toList();
}

// Build the map for the classroom
SizedBox _buildClassroomMap(Classroom classroom) {
return SizedBox(
height: 200,
child: FlutterMap(
options: MapOptions(
initialZoom: 15,
interactionOptions: const InteractionOptions(
flags: InteractiveFlag.none,
),
initialCenter: LatLng(
classroom.campus!.latitude!,
classroom.campus!.longitude!,
),
crs: const Epsg3857(),
onTap: (tapPosition, point) {
launchUrlString(
'https://yandex.ru/maps/?ll=${classroom.campus!.longitude},${classroom.campus!.latitude}&z=15&mode=whatshere&whatshere%5Bpoint%5D=${classroom.campus!.longitude},${classroom.campus!.latitude}&whatshere%5Bzoom%5D=15&whatshere%5Bdirection%5D=down&whatshere%5Bviewport%5D=%5B${classroom.campus!.longitude! - 0.0001},${classroom.campus!.latitude! - 0.0001}%2C${classroom.campus!.longitude! + 0.0001},${classroom.campus!.latitude! + 0.0001}%5D&basemap=map');
},
),
children: [
mapTileLayer,
CircleLayer(
circles: [
CircleMarker(
point: LatLng(
classroom.campus!.latitude!,
classroom.campus!.longitude!,
),
color: AppTheme.colors.primary,
borderColor: AppTheme.colors.colorful01,
radius: 5,
),
],
),
],
),
);
}

// Build the groups
ListTile _buildGroups() {
return ListTile(
title: Text('Группы'.toUpperCase()),
subtitle: Wrap(
spacing: 8,
runSpacing: 4,
children: widget.lesson.groups?.map((group) {
return InputChip(
label: Text(group),
onPressed: () => context.go('/schedule/search', extra: group),
);
}).toList() ??
[],
),
);
}

// Build the teachers
ListTile _buildTeachers() {
return ListTile(
title: Text('Преподаватели'.toUpperCase()),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: widget.lesson.teachers.map((teacher) {
return ListTile(
title: Text(
teacher.name,
style: AppTextStyle.titleM.copyWith(
color: AppTheme.colors.active,
),
),
subtitle: teacher.post != null
? Text(
teacher.post!,
style: AppTextStyle.captionS.copyWith(
color: AppTheme.colors.deactive,
),
)
: null,
dense: true,
contentPadding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
trailing: const Icon(
Icons.chevron_right_sharp,
size: 24,
),
onTap: () => context.go('/schedule/search', extra: teacher.name),
);
}).toList(),
),
);
}
}
19 changes: 17 additions & 2 deletions lib/schedule/view/schedule_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:go_router/go_router.dart';
import 'package:rtu_mirea_app/presentation/theme.dart';
import 'package:rtu_mirea_app/presentation/widgets/bottom_modal_sheet.dart';
import 'package:rtu_mirea_app/schedule/bloc/schedule_bloc.dart';
import 'package:rtu_mirea_app/schedule/models/models.dart';
import 'package:rtu_mirea_app/stories/view/stories_view.dart';
import 'package:university_app_server_api/client.dart';

Expand Down Expand Up @@ -35,6 +36,20 @@ class _SchedulePageState extends State<SchedulePage> {
super.dispose();
}

String get _getAppBarTitle {
final schedule = context.read<ScheduleBloc>().state.selectedSchedule;

if (schedule is SelectedGroupSchedule) {
return 'Расписание ${schedule.group.name}';
} else if (schedule is SelectedTeacherSchedule) {
return 'Расписание ${schedule.teacher.name}';
} else if (schedule is SelectedClassroomSchedule) {
return 'Расписание ${schedule.classroom.name}';
} else {
return 'Расписание';
}
}

@override
Widget build(BuildContext context) {
return BlocBuilder<ScheduleBloc, ScheduleState>(
Expand All @@ -59,8 +74,8 @@ class _SchedulePageState extends State<SchedulePage> {
headerSliverBuilder: (_, __) => [
SliverAppBar(
pinned: false,
title: const Text(
'Расписание',
title: Text(
_getAppBarTitle,
),
actions: [
SizedBox(
Expand Down
1 change: 1 addition & 0 deletions lib/schedule/view/view.dart
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export 'schedule_page.dart';
export 'schedule_details_page.dart';
Loading

0 comments on commit ef6ccc8

Please sign in to comment.