Skip to content

Commit

Permalink
feat: added filter for student (and teacher) plans
Browse files Browse the repository at this point in the history
style: updated paddings, so it looks more modern and unified
  • Loading branch information
SachsenspieltCoding committed Oct 10, 2023
1 parent b7ca471 commit bdb671f
Show file tree
Hide file tree
Showing 7 changed files with 342 additions and 19 deletions.
106 changes: 106 additions & 0 deletions lib/api/filter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import 'dart:convert';

import 'package:hive_flutter/hive_flutter.dart';
import 'package:vertretungsapp/api/stundenplan24/models/schedule.dart';

FilterManager filterManager = FilterManager();

class Filter {
final String short;
final ScheduleType type;
final List<String> lessons;
final String schoolnumber;

const Filter(
{required this.short,
required this.type,
required this.lessons,
required this.schoolnumber});

Filter.fromJson(Map<String, dynamic> json)
: short = json["short"],
type = ScheduleType.values[json["type"]],
lessons = List<String>.from(json["lessons"]),
schoolnumber = json["schoolnumber"];

Map<String, dynamic> toJson() => {
"short": short,
"type": type.index,
"lessons": List<dynamic>.from(lessons.map((x) => x)),
"schoolnumber": schoolnumber,
};

void addLesson(String lesson) {
lessons.add(lesson);
}

void removeLesson(String lesson) {
lessons.remove(lesson);
}

bool containsLesson(String lesson) {
return lessons.contains(lesson);
}
}

class FilterManager {
Map<String, List<Filter>> filters = {};
late Box<String> filterBox;

FilterManager();

FilterManager.fromJson(Map<String, dynamic> json)
: filters = json.map((key, value) => MapEntry(key,
List<Filter>.from(value.map((x) => Filter.fromJson(x)).toList())));

Map<String, dynamic> toJson() => filters.map((key, value) =>
MapEntry(key, List<dynamic>.from(value.map((x) => x.toJson()))));

Future<void> initFilterManager() async {
filterBox = await Hive.openBox<String>("filter");
readFromDisk();
return;
}

FilterManager readFromDisk() {
final json = filterBox.get("cache");
filters = json != null && json != "{}"
? FilterManager.fromJson(jsonDecode(json)).filters
: FilterManager().filters;
return this;
}

FilterManager writeToDisk() {
filterBox.put("cache", jsonEncode(toJson()));
return this;
}

void add(Filter filter) {
filters.putIfAbsent(filter.schoolnumber, () => []).add(filter);
writeToDisk();
}

void remove(Filter filter) {
filters.putIfAbsent(filter.schoolnumber, () => []).remove(filter);
writeToDisk();
}

bool contains(String schoolnumber, String short) {
return filters[schoolnumber] != null &&
filters[schoolnumber]!.any((element) => element.short == short);
}

Filter getFilter(String schoolnumber, String short) {
if (!contains(schoolnumber, short)) {
add(_createEmptyFilter(schoolnumber, short, ScheduleType.schoolClass));
}
return filters[schoolnumber]!
.firstWhere((element) => element.short == short);
}

Filter _createEmptyFilter(
String schoolnumber, String short, ScheduleType type) {
return Filter(
short: short, type: type, lessons: [], schoolnumber: schoolnumber);
}
}
7 changes: 6 additions & 1 deletion lib/components/back_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:vertretungsapp/components/icon_button.dart';

class VPBackButton extends StatelessWidget {
const VPBackButton({super.key});
final void Function()? onPressed;

const VPBackButton({super.key, this.onPressed});

@override
Widget build(BuildContext context) {
return VPIconButton(
onPressed: () {
Navigator.pop(context);
if (onPressed != null) {
onPressed!();
}
},
icon: const FaIcon(FontAwesomeIcons.circleArrowLeft, size: 30));
}
Expand Down
14 changes: 12 additions & 2 deletions lib/components/plan/filter_button.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:vertretungsapp/components/icon_button.dart';
import 'package:vertretungsapp/pages/filter.dart';

class VPFilterButton extends StatelessWidget {
const VPFilterButton({super.key});
final FilterPageData data;

const VPFilterButton({super.key, required this.data});

@override
Widget build(BuildContext context) {
return VPIconButton(onPressed: () {}, icon: const FaIcon(FontAwesomeIcons.filter, size: 30));
return VPIconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => FilterPage(data: data)),
);
},
icon: const FaIcon(FontAwesomeIcons.filter, size: 30));
}
}
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:vertretungsapp/api/cache.dart';
import 'package:vertretungsapp/api/filter.dart';
import 'package:vertretungsapp/api/stundenplan24/models/schedule.dart';
import 'package:vertretungsapp/pages/overview.dart';
import 'package:vertretungsapp/pages/settings.dart';
Expand All @@ -13,6 +14,7 @@ import 'pages/home.dart';
void main() async {
await Hive.initFlutter();
await cache.initCache();
await filterManager.initFilterManager();
runApp(const Main());
}

Expand Down
137 changes: 137 additions & 0 deletions lib/pages/filter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:vertretungsapp/api/filter.dart';
import 'package:vertretungsapp/api/stundenplan24/models/lesson.dart';
import 'package:vertretungsapp/api/stundenplan24/models/plan.dart';
import 'package:vertretungsapp/components/back_button.dart';

class FilterPageData {
final Plan plan;
final String short;
final List<Lesson> lessons;
final Filter filter;
final void Function(Filter) onSave;

const FilterPageData(
{required this.plan,
required this.short,
required this.lessons,
required this.filter,
required this.onSave});
}

class FilterPage extends StatelessWidget {
final FilterPageData data;

const FilterPage({super.key, required this.data});

@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.fromLTRB(8, 30, 8, 0),
child: Column(
children: [
Row(
children: [
VPBackButton(onPressed: () {
data.onSave(data.filter);
}),
],
),
Center(
child: RichText(
text: TextSpan(
text: 'Filter für ',
style: Theme.of(context).textTheme.displayMedium,
children: <TextSpan>[
TextSpan(
text: data.short,
style: TextStyle(
color: Theme.of(context).colorScheme.primary)),
],
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 4, 0, 4),
child: GridView.count(
crossAxisCount: 3,
mainAxisSpacing: 8,
crossAxisSpacing: 16,
childAspectRatio: 1.8,
children: data.lessons
.map((e) => _Item(
lesson: e,
filter: data.filter,
))
.toList()),
))
],
),
));
}
}

class _Item extends StatefulWidget {
final Lesson lesson;
final Filter filter;

const _Item({Key? key, required this.lesson, required this.filter})
: super(key: key);

@override
State<_Item> createState() => _ItemState();
}

class _ItemState extends State<_Item> {
bool selected = true;

@override
void initState() {
super.initState();
selected = !widget.filter.containsLesson(widget.lesson.id);
}

@override
Widget build(BuildContext context) {
return MaterialButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
side: BorderSide(
color: selected == true
? Theme.of(context).colorScheme.primary
: Colors.transparent,
width: 0.7)),
color: Theme.of(context).colorScheme.surface,
onPressed: () {
setState(() {
selected = !selected;
if (selected == false) {
widget.filter.addLesson(widget.lesson.id);
} else {
widget.filter.removeLesson(widget.lesson.id);
}
});
},
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(widget.lesson.subject),
Text(widget.lesson.teacher,
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(color: Theme.of(context).colorScheme.tertiary)),
Text(widget.lesson.id,
style: Theme.of(context)
.textTheme
.labelSmall!
.copyWith(color: Theme.of(context).colorScheme.tertiary)),
],
)));
}
}
20 changes: 11 additions & 9 deletions lib/pages/overview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,17 @@ class _OverviewPageState extends State<OverviewPage> {
break;
}
return Expanded(
child: GridView.count(
crossAxisCount: 3,
mainAxisSpacing: 16,
crossAxisSpacing: 16,
childAspectRatio: 2,
children: items
.map((e) =>
_Item(type: widget.type, schedule: e))
.toList()));
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 4, 0, 4),
child: GridView.count(
crossAxisCount: 3,
mainAxisSpacing: 16,
crossAxisSpacing: 16,
childAspectRatio: 2,
children: items
.map((e) => _Item(type: widget.type, schedule: e))
.toList()),
));
} else if (snapshot.hasError) {
return const Text("Error");
} else {
Expand Down
Loading

0 comments on commit bdb671f

Please sign in to comment.