Skip to content

Commit

Permalink
Make all pages scrollable and add headers (#1069)
Browse files Browse the repository at this point in the history
  • Loading branch information
Process-ing authored Jul 3, 2024
2 parents b1fd137 + 1809d46 commit 5caf6d9
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 142 deletions.
10 changes: 7 additions & 3 deletions uni/lib/view/bug_report/bug_report.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ class BugReportPageView extends StatefulWidget {
class BugReportPageViewState extends SecondaryPageViewState<BugReportPageView> {
@override
Widget getBody(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
child: const BugReportForm(),
return ListView(
children: [
Container(
margin: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
child: const BugReportForm(),
),
],
);
}

Expand Down
2 changes: 1 addition & 1 deletion uni/lib/view/bug_report/widgets/form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class BugReportFormState extends State<BugReportForm> {
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: ListView(
child: Column(
children: [
const Padding(padding: EdgeInsets.only(bottom: 10)),
PageTitle(
Expand Down
54 changes: 35 additions & 19 deletions uni/lib/view/common_widgets/pages_layouts/general/general.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,36 +84,52 @@ abstract class GeneralPageViewState<T extends StatefulWidget> extends State<T> {
);
}

Widget? getHeader(BuildContext context) {
return null;
}

String? getTitle();

Widget getBody(BuildContext context);

Widget refreshState(BuildContext context, Widget child) {
return RefreshIndicator(
key: GlobalKey<RefreshIndicatorState>(),
onRefresh: () => ProfileProvider.fetchOrGetCachedProfilePicture(
Provider.of<SessionProvider>(context, listen: false).state!,
forceRetrieval: true,
).then((value) => onRefresh(context)),
child: Builder(
builder: (context) => GestureDetector(
onHorizontalDragEnd: (dragDetails) {
if (dragDetails.primaryVelocity! > 2) {
Scaffold.of(context).openDrawer();
}
},
child: child,
),
),
Future<DecorationImage> buildProfileDecorationImage(
BuildContext context, {
bool forceRetrieval = false,
}) async {
final sessionProvider =
Provider.of<SessionProvider>(context, listen: false);
await sessionProvider.ensureInitialized(context);
final profilePictureFile =
await ProfileProvider.fetchOrGetCachedProfilePicture(
sessionProvider.state!,
forceRetrieval: forceRetrieval,
);
return getProfileDecorationImage(profilePictureFile);
}

/// Returns the current user image.
///
/// If the image is not found / doesn't exist returns a generic placeholder.
DecorationImage getProfileDecorationImage(File? profilePicture) {
const fallbackPicture = AssetImage('assets/images/profile_placeholder.png');
final image =
profilePicture == null ? fallbackPicture : FileImage(profilePicture);

final result =
DecorationImage(fit: BoxFit.cover, image: image as ImageProvider);
return result;
}

Widget getScaffold(BuildContext context, Widget body) {
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.surface,
bottomNavigationBar: const AppBottomNavbar(),
appBar: getTopNavbar(context),
body: RefreshState(onRefresh: onRefresh, child: body),
bottomNavigationBar: const AppBottomNavbar(),
body: RefreshState(
onRefresh: onRefresh,
header: getHeader(context),
body: body,
),
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,58 @@ import 'package:uni/model/providers/startup/profile_provider.dart';
import 'package:uni/model/providers/startup/session_provider.dart';

class RefreshState extends StatelessWidget {
const RefreshState({required this.onRefresh, required this.child, super.key});
const RefreshState({
required this.onRefresh,
required this.header,
required this.body,
super.key,
});

final Future<void> Function(BuildContext) onRefresh;
final Widget child;
final Widget? header;
final Widget body;

@override
Widget build(BuildContext context) {
return RefreshIndicator(
key: GlobalKey<RefreshIndicatorState>(),
onRefresh: () => ProfileProvider.fetchOrGetCachedProfilePicture(
Provider.of<SessionProvider>(context, listen: false).state!,
forceRetrieval: true,
).then((value) => onRefresh(context)),
child: child,
return Column(
children: [
if (header != null) header!,
Expanded(
child: LayoutBuilder(
builder: (context, viewportConstraints) {
return SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: RefreshIndicator(
key: GlobalKey<RefreshIndicatorState>(),
notificationPredicate: (notification) =>
notification.metrics.axisDirection == AxisDirection.down,
onRefresh: () =>
ProfileProvider.fetchOrGetCachedProfilePicture(
Provider.of<SessionProvider>(context, listen: false).state!,
forceRetrieval: true,
).then((value) => onRefresh(context)),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
maxHeight: viewportConstraints.maxHeight,
),
child: Builder(
builder: (context) => GestureDetector(
onHorizontalDragEnd: (dragDetails) {
if (dragDetails.primaryVelocity! > 2) {
Scaffold.of(context).openDrawer();
}
},
child: body,
),
),
),
),
);
},
),
),
],
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ abstract class SecondaryPageViewState<T extends StatefulWidget>
backgroundColor: Theme.of(context).colorScheme.surface,
appBar: getTopNavbar(context),
bottomNavigationBar: const AppBottomNavbar(),
body: RefreshState(onRefresh: onRefresh, child: body),
body: RefreshState(
onRefresh: onRefresh,
header: getHeader(context),
body: body,
),
);
}

Expand Down
12 changes: 8 additions & 4 deletions uni/lib/view/course_unit_info/course_unit_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,21 @@ class CourseUnitDetailPageViewState
await loadInfo(force: false);
}

@override
Widget? getHeader(BuildContext context) {
return PageTitle(
center: false,
name: widget.courseUnit.name,
);
}

@override
Widget getBody(BuildContext context) {
return DefaultTabController(
length: 3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
PageTitle(
center: false,
name: widget.courseUnit.name,
),
TabBar(
tabs: [
Tab(text: S.of(context).course_info),
Expand Down
53 changes: 40 additions & 13 deletions uni/lib/view/course_units/course_units.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class CourseUnitsPageViewState
String? selectedSemester;

@override
Widget getBody(BuildContext context) {
Widget? getHeader(BuildContext context) {
return LazyConsumer<ProfileProvider, Profile>(
builder: (context, profile) {
final courseUnits = profile.courseUnits;
Expand All @@ -50,11 +50,41 @@ class CourseUnitsPageViewState
}
}

return Column(
children: [
_getFilters(availableYears, availableSemesters),
_getPageView(courseUnits, availableYears, availableSemesters),
],
return _getFilters(availableYears, availableSemesters);
},
hasContent: (profile) => profile.courseUnits.isNotEmpty,
onNullContent: Column(
children: [
_getFilters([], []),
Center(
heightFactor: 10,
child: Text(
S.of(context).no_selected_courses,
style: Theme.of(context).textTheme.titleLarge,
),
),
],
),
);
}

@override
Widget getBody(BuildContext context) {
return LazyConsumer<ProfileProvider, Profile>(
builder: (context, profile) {
final courseUnits = profile.courseUnits;
var availableYears = <String>[];
var availableSemesters = <String>[];

if (courseUnits.isNotEmpty) {
availableYears = _getAvailableYears(courseUnits);
availableSemesters = _getAvailableSemesters(courseUnits);
}

return _getPageView(
courseUnits,
availableYears,
availableSemesters,
);
},
hasContent: (profile) => profile.courseUnits.isNotEmpty,
Expand Down Expand Up @@ -153,13 +183,10 @@ class CourseUnitsPageViewState
),
);
}
return Expanded(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 20),
child: ListView(
shrinkWrap: true,
children: _generateCourseUnitsGridView(courseUnits),
),
return Container(
margin: const EdgeInsets.only(left: 20, right: 20, bottom: 20),
child: ListView(
children: _generateCourseUnitsGridView(courseUnits),
),
);
}
Expand Down
37 changes: 37 additions & 0 deletions uni/lib/view/home/home.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:uni/controller/local_storage/preferences_controller.dart';
import 'package:uni/generated/l10n.dart';
import 'package:uni/utils/favorite_widget_type.dart';
import 'package:uni/view/common_widgets/page_title.dart';
import 'package:uni/view/common_widgets/pages_layouts/general/general.dart';
import 'package:uni/view/common_widgets/pages_layouts/general/widgets/profile_button.dart';
import 'package:uni/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart';
Expand Down Expand Up @@ -45,6 +47,41 @@ class HomePageViewState extends GeneralPageViewState {
PreferencesController.saveFavoriteCards(favorites);
}

@override
Widget? getHeader(BuildContext context) {
return Container(
padding: const EdgeInsets.fromLTRB(20, 10, 20, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
PageTitle(
name: S.of(context).nav_title('area'),
center: false,
pad: false,
),
if (isEditing)
ElevatedButton(
onPressed: () => setState(() {
isEditing = false;
}),
child: Text(
S.of(context).edit_on,
),
)
else
OutlinedButton(
onPressed: () => setState(() {
isEditing = true;
}),
child: Text(
S.of(context).edit_off,
),
),
],
),
);
}

void toggleEditing() {
setState(() {
isEditing = !isEditing;
Expand Down
Loading

0 comments on commit 5caf6d9

Please sign in to comment.