==0.6.5==

- added A LOT, TLTD
- TODO:
  - update README
  - update license & copyright info
  - cleanup code
  - merge code parts to server side
  - add more settings
  - fix 11/12
  - add more timetable options
  - add timetable additional info
  - muuuuuuuch more...
This commit is contained in:
Denys Konovalov 2021-09-14 20:55:58 +02:00
parent 75e8c9ea2c
commit bae624dd67
19 changed files with 1207 additions and 762 deletions

@ -1,414 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'Settings.dart';
import 'networking.dart';
import 'Login.dart';
class Dashboard extends StatefulWidget {
const Dashboard({
Key? key,
this.restorationId
}): super(key: key);
final String? restorationId;
@override
State<StatefulWidget> createState() => _DashboardState();
}
Future<String> getSettingsString(String key) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? value = await prefs.getString(key);
if(value == null || value.isEmpty) {
value = "";
}
print(value);
return value;
}
Widget buildSettingsString(String key, TextStyle style) {
return FutureBuilder(
future: getSettingsString(key),
builder: (context, snapshot) {
if(snapshot.hasData) {
return Text(snapshot.data as String, style: style);
} else {
return(Center(child: CircularProgressIndicator()));
}
}
);
}
class _DashboardState extends State<Dashboard> with RestorationMixin {
final RestorableInt _currentIndex = RestorableInt(0);
@override
Widget build(BuildContext context) {
final drawerElements = ListView(
children: [
UserAccountsDrawerHeader(
accountName: buildSettingsString('name', TextStyle()),
accountEmail: buildSettingsString('user', TextStyle()),
currentAccountPicture: const CircularProgressIndicator(backgroundColor: Colors.black,),
),
ListTile(
title: Text("Einstellungen"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Settings()),
);
},
leading: Icon(CupertinoIcons.settings),
),
ListTile(
title: Text("Abmelden"),
onTap: () async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('api_key', "");
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => Login()),
);
},
leading: Icon(Icons.exit_to_app_outlined),
),
AboutListTile(
child: Text("Info"),
icon: Icon(CupertinoIcons.info),
applicationVersion: "0.5.0-alpha1",
applicationIcon: Image.asset("assets/images/meincantor_r.png", height: 64, width: 64),
applicationName: "MeinCantor",
aboutBoxChildren: [
Text("MeinCantor ist die Schulplatform für Schüler des Georg-Cantor-Gymnasiums in Halle (Saale)."),
Divider(),
Text("Copyright © 2021 Denys Konovalov")
],
// applicationIcon: Image.,
),
],
);
var bottomNavBarItems = [
BottomNavigationBarItem(
icon: const Icon(CupertinoIcons.home),
label: "Startseite",
),
BottomNavigationBarItem(icon: const Icon(CupertinoIcons.rectangle_grid_1x2), label: "Vertretungsplan"),
];
return Scaffold(
appBar: AppBar(
title: Text("GCG.MeinCantor"),
centerTitle: true,
),
body: _DashboardBottomNavView(
key: UniqueKey(),
item: bottomNavBarItems[_currentIndex.value]
),
drawer: Drawer(
child: drawerElements,
),
bottomNavigationBar: BottomNavigationBar(
showUnselectedLabels: false,
items: bottomNavBarItems,
currentIndex: _currentIndex.value,
onTap: (index) {
setState(() {
_currentIndex.value = index;
});
//print(index);
},
),
);
}
@override
String? get restorationId => widget.restorationId;
@override
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
registerForRestoration(_currentIndex, 'bottom_navigation_tab_index');
}
}
class _DashboardBottomNavView extends StatelessWidget {
_DashboardBottomNavView({
Key? key,
required this.item
}) : super(key:key);
final BottomNavigationBarItem item;
@override
Widget build(BuildContext context) {
return materialCard(item.label);
}
Widget materialCard(String? label) {
if (label == "Startseite") {
var view = SingleChildScrollView(
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: EdgeInsets.fromLTRB(20, 30, 20, 5),
child: Text(
'Hallo,',
style: GoogleFonts.robotoSlab(
fontSize: 20,
),
),
),
],
),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: EdgeInsets.fromLTRB(20, 5, 20, 20),
child: buildSettingsString("name", GoogleFonts.robotoSlab(
fontSize: 28,
fontWeight: FontWeight.w800,
),
),
)
],
),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: EdgeInsets.fromLTRB(20, 10, 0, 10),
child: Text(
'Deine nächste Unterrichtsstunde:',
style: GoogleFonts.robotoSlab(
fontSize: 20,
fontWeight: FontWeight.w100,
),
),
)
],
),
Padding(
padding: EdgeInsets.fromLTRB(20, 20, 20, 10),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
color: Colors.red,
child: Column(
children: [ListTile(
title: Text('3' + '.' + ' ' + 'Deutsch', style: TextStyle(color: Colors.white)),
subtitle: Row(
children: [Icon(CupertinoIcons.person, color: Colors.white), SizedBox(width: 5), Text("Herr Jünemann", style: TextStyle(color: Colors.white)), Spacer(), Icon(CupertinoIcons.home, color: Colors.white), SizedBox(width: 5), Text("106", style: TextStyle(color: Colors.white))]
),
leading:
Icon(CupertinoIcons.time, color: Colors.white)
)],
)
),
),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Padding(
padding: EdgeInsets.fromLTRB(20, 20, 20, 10),
child: Column(
children: [
Card(
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding:
EdgeInsets.fromLTRB(20, 20, 10, 10),
child: Icon(
CupertinoIcons.news,
color: Color(0xFFFFBC3B),
size: 48,
),
)
],
),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Align(
alignment: Alignment(0, 0),
child: Padding(
padding:
EdgeInsets.fromLTRB(15, 50, 0, 15),
child: Text(
'Schülerzeitung',
),
),
),
],
)
],
),
),
Card(
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding:
EdgeInsets.fromLTRB(20, 20, 10, 10),
child: Icon(
CupertinoIcons.book,
color: Color(0xFFFFBC3B),
size: 48,
),
)
],
),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: EdgeInsets.fromLTRB(15, 50, 0, 15),
child: Text(
'Schulbibliothek',
),
)
],
)
],
),
),
Card(
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding:
EdgeInsets.fromLTRB(20, 20, 10, 10),
child: Icon(
CupertinoIcons.device_laptop,
color: Color(0xFFFFBC3B),
size: 48,
),
)
],
),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: EdgeInsets.fromLTRB(15, 50, 0, 15),
child: Text(
'Schulcomputer',
),
)
],
)
],
),
),
Card(
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding:
EdgeInsets.fromLTRB(20, 20, 10, 10),
child: Icon(
CupertinoIcons.house_alt,
color: Color(0xFFFFBC3B),
size: 48,
),
)
],
),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: EdgeInsets.fromLTRB(15, 50, 0, 15),
child: Text(
'Raumübersicht',
),
)
],
)
],
),
)
],
),
),
)
]
),
],
)
);
return view;
} else if (label == "Vertretungsplan") {
return LayoutBuilder(
builder: (context, constraints) {
double widgetWidth = constraints.maxWidth;
double widgetHeight = constraints.maxHeight;
var factor;
if (widgetWidth <= 600) {
factor = 1;
} else if (widgetWidth <= 1400) {
factor = 2;
} else if (widgetWidth <= 2000) {
factor = 3;
}
// print(screenType);
return Center(
child: Container(
constraints: BoxConstraints(
// minHeight: 500, //minimum height
// minWidth: 300, // minimum width
//maximum height set to 100% of vertical height
maxWidth: MediaQuery.of(context).size.width / factor,
//maximum width set to 100% of width
),
child: buildClassTimetable(),
),
);
}
);
} else {
return Text("Such page does not exist.");
}
}
}

@ -1,38 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'networking.dart';
class Settings extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Einstellungen"),
),
body: ListView(
padding: EdgeInsets.fromLTRB(20, 20, 20, 20),
children: [
Padding(
padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
child: TextField(
decoration: InputDecoration(
icon: Icon(CupertinoIcons.lock),
border: OutlineInputBorder(),
labelText: 'API Key (POST /login)',
),
onSubmitted: (String value) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String api_key = value;
print('Set new API key to $api_key');
await prefs.setString('api_key', api_key);
}
)
),
Divider(),
buildClassesChooser()
],
)
);
}
}

@ -1,174 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
class ClassTimetableBuilder {
final ListView view;
ClassTimetableBuilder({required this.view});
factory ClassTimetableBuilder.buildView(Map<String, dynamic> json) {
List<Widget> children = [];
ClassTimetable.fromJson(json).timetable.forEach((element) {
List<Widget> cardChildren = [];
cardChildren.add(ListTile(
title: Text(element.count.toString() + '.' + ' ' + element.name,
style: TextStyle(color: element.fontColor)),
subtitle: Row(
children: [
Icon(CupertinoIcons.person, color: element.fontColor),
SizedBox(width: 5),
Text(element.teacher,
style: TextStyle(color: element.fontColor)),
Spacer(),
Icon(CupertinoIcons.home, color: element.fontColor),
SizedBox(width: 5),
Text(element.room, style: TextStyle(color: element.fontColor))
]
),
leading:
Icon(CupertinoIcons.time, color: element.fontColor)
));
if (element.info != '') {
cardChildren.add(ListTile(title: Text(
element.info, style: TextStyle(color: element.fontColor))));
}
Card card = Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
color: element.color,
child: Column(
children: cardChildren,
)
);
children.add(card);
});
return ClassTimetableBuilder(
view: ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: children
)
);
}
}
class ClassTimetable {
final List timetable;
ClassTimetable({required this.timetable});
factory ClassTimetable.fromJson(Map<String, dynamic> json){
print(json);
List<TimetableLesson> lessons = [];
json['courses'].forEach((value){
var color;
var name;
var teacher;
var room;
dynamic teachers = {'Poli':'Herr Polity', 'Zura':'Frau Zuralski', 'Enzi': 'Frau Enzian', 'Bütt':'Frau Büttner', 'Brod':'Herr Brode', 'Rink':'Frau Rinke', 'Schk':'Frau Schmidt', 'Rudo':'Frau Rudolph', 'Kipp':'Frau Kipping', 'Bach':'Frau Bachran', 'Bad':'Herr Bader', 'Prei':'Frau Preiß', 'Scha':'Frau Schapitz', '&nbsp;':'', 'Link':'Herr Linke', 'Stei':'Herr Stein', 'Tupp':'Frau Tuppack', 'Hoff':'Frau Hoffman', 'Knol':'Frau Knoll', 'Bet':'Frau Bethin', 'Schu':'Frau Schulz', 'Seid':'Frau Seidel', 'Krug':'Frau Krug', 'Laer':'Frau Langer', 'Youn':'Frau Younso', 'Härt':'Frau Härtig', 'Bros':'Frau Brosig', 'Ber':'Frau Bernhardt', 'Stüb':'Frau Stüber', 'Bor':'Frau Borchert', 'Dubb':'Frau Dubberstein', 'Tren':'Frau Trentsch', 'Meit':'Herr Meitzner', 'Stol':'Frau Stolpe', 'Jac':'Frau Jacob', 'Jüne':'Herr Jünemann', 'Bert':'Frau Berthelmann', 'Felk':'Frau Felke', 'Kimm':'Herr Kimmel', 'PM1': 'Pädagosische(r) Mitarbeiter(in) 1', 'Schet':'Herr Schetler', 'Mani':'Herr Manigk', 'Segg':'Frau Seggern', 'Opel':'Frau Opel-Fritzler'};
if(value['Fa'].runtimeType != String){
name = value['Fa']['#text'];
}
else {
name = value['Fa'];
}
if(value['Le'].runtimeType != String){
teacher = value['Le']['#text'];
}
else {
teacher = value['Le'];
}
print(value['Ra']);
if(value['Ra'].runtimeType != String && value['Ra'].runtimeType != int){
if(value['Ra']['#text'] == '&nbsp;') {
room = '';
} else {
room = value['Ra']['#text'];
}
} else if(value['Ra'] == '&nbsp;') {
room = '';
} else {
room = value['Ra'];
}
var fontColor;
if(name == '---') {
fontColor = Colors.red;
} else {
fontColor = Colors.white;
}
var info;
if(value['If'].runtimeType != String) {
info = '';
} else {
info = value['If'];
}
if(name == 'Mat'){
color = Colors.indigo;
}
else if(name == 'Deu'){
color = Colors.red;
}
else if(name == 'Kun'){
color = Colors.deepPurple;
}
else if(name == 'Bio'){
color = Colors.green;
}
else if(name == 'Geo'){
color = Colors.brown;
}
else if(name == 'Lat'){
color = Colors.teal;
}
else if(name == 'Che'){
color = Colors.lightGreen;
}
else if(name == 'Eng'){
color = Colors.amber;
}
else if(name == 'Phy'){
color = Colors.cyan;
}
else if(name == 'Inf'){
color = Colors.tealAccent[400];
}
else if(name == 'Mus'){
color = Colors.deepOrange;
}
else if(name == 'Lme'){
color = Colors.amber[700];
}
else if(name == 'Ges'){
color = Colors.grey;
}
else if(name == 'Spa'){
color = Colors.redAccent;
}
else if(name == 'Frz'){
color = Colors.amberAccent[700];
}
else if(name == '---') {
color = Colors.white;
}
else {
color = Colors.grey[700];
}
dynamic names = {'Bio':'Biologie', 'Mat':'Mathematik', 'Kun':'Kunst', 'Mus':'Musik', 'Geo':'Geographie', 'Ges':'Geschichte', 'Che':'Chemie', 'Lat':'Latein', 'Inf':'Informatik', 'Eng':'Englisch', 'Frz':'Französisch', 'Phy':'Physik', '---':'---', 'Spo':'Sport', 'Deu':'Deutsch', 'Lme':'Lernmethoden', 'Eth':'Ethik', 'EvR':'Evangelische Religion', 'Spa':'Spanisch', 'Soz':'Sozialkunde', 'Ast':'Astronomie'};
dynamic colors = {'Bio':Colors.green, 'Mat':Colors.blue};
lessons.add(TimetableLesson(value['St'], names[name].toString(), teachers[teacher].toString(), room.toString(), value['If'].toString(), color, fontColor, info));
});
return ClassTimetable(
timetable: lessons
);
}
}
class TimetableLesson {
final int count;
final String name;
final String teacher;
final String room;
final String comment;
final Color color;
final Color fontColor;
final String info;
const TimetableLesson(this.count, this.name, this.teacher, this.room, this.comment, this.color, this.fontColor, this.info);
}

516
lib/dashboard.dart Normal file

@ -0,0 +1,516 @@
//import 'package:MeinCantor/timetable.dart';
//import 'package:MeinCantor/main.dart';
import 'package:MeinCantor/raumuebersicht.dart';
import 'package:MeinCantor/schulbibliothek.dart';
import 'package:MeinCantor/schulcomputer.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'settings.dart';
import 'networking.dart';
import 'login.dart';
import 'schuelerzeitung.dart';
class Dashboard extends StatefulWidget {
const Dashboard({Key? key, this.restorationId}) : super(key: key);
final String? restorationId;
@override
State<StatefulWidget> createState() => _DashboardState();
}
Future<String> getSettingsString(String key) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? value = prefs.getString(key);
if (value == null || value.isEmpty) {
value = "";
}
print(value);
return value;
}
Widget buildSettingsString(String key, TextStyle style) {
return FutureBuilder(
future: getSettingsString(key),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data as String, style: style);
} else {
return (const Center(child: CircularProgressIndicator()));
}
});
}
class _DashboardState extends State<Dashboard> with RestorationMixin {
final RestorableInt _currentIndex = RestorableInt(0);
@override
Widget build(BuildContext context) {
final drawerElements = ListView(
children: [
UserAccountsDrawerHeader(
accountName: buildSettingsString('name', const TextStyle()),
accountEmail: buildSettingsString('user', const TextStyle()),
currentAccountPicture: const CircularProgressIndicator(
backgroundColor: Colors.black,
),
),
ListTile(
title: const Text("Einstellungen"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Settings()),
);
},
leading: const Icon(CupertinoIcons.settings),
),
ListTile(
title: const Text("Abmelden"),
onTap: () async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('api_key', "");
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => Login()),
);
},
leading: const Icon(Icons.exit_to_app_outlined),
),
AboutListTile(
child: const Text("Info"),
icon: const Icon(CupertinoIcons.info),
applicationVersion: "0.6.5-alpha",
applicationIcon: Image.asset("assets/images/meincantor_r.png",
height: 64, width: 64),
applicationName: "MeinCantor",
aboutBoxChildren: const [
Text(
"MeinCantor ist die Schulplatform für Schüler des Georg-Cantor-Gymnasiums in Halle (Saale)."),
Divider(),
Text("Copyright © 2021 Denys Konovalov")
],
// applicationIcon: Image.,
),
],
);
var bottomNavBarItems = [
const BottomNavigationBarItem(
icon: Icon(CupertinoIcons.home),
label: "Startseite",
),
const BottomNavigationBarItem(
icon: Icon(CupertinoIcons.rectangle_grid_1x2),
label: "Vertretungsplan"),
];
return Scaffold(
appBar: AppBar(
title: const Text("GCG.MeinCantor"),
centerTitle: true,
),
body: _DashboardBottomNavView(
key: UniqueKey(), item: bottomNavBarItems[_currentIndex.value]),
drawer: Drawer(
child: drawerElements,
),
bottomNavigationBar: BottomNavigationBar(
showUnselectedLabels: false,
items: bottomNavBarItems,
currentIndex: _currentIndex.value,
onTap: (index) {
setState(() {
_currentIndex.value = index;
});
//print(index);
},
),
);
}
@override
String? get restorationId => widget.restorationId;
@override
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
registerForRestoration(_currentIndex, 'bottom_navigation_tab_index');
}
}
class _DashboardBottomNavView extends StatelessWidget {
const _DashboardBottomNavView({Key? key, required this.item})
: super(key: key);
final BottomNavigationBarItem item;
@override
Widget build(BuildContext context) {
if (item.label == "Startseite") {
double _timeOfDayToDouble(TimeOfDay tod) => tod.hour + tod.minute / 60.0;
var lessonCount;
if (_timeOfDayToDouble(TimeOfDay.now()) <=
_timeOfDayToDouble(const TimeOfDay(hour: 7, minute: 30))) {
lessonCount = 1;
} else if (_timeOfDayToDouble(TimeOfDay.now()) >
_timeOfDayToDouble(const TimeOfDay(hour: 7, minute: 30)) &&
_timeOfDayToDouble(TimeOfDay.now()) <=
_timeOfDayToDouble(const TimeOfDay(hour: 8, minute: 20))) {
lessonCount = 2;
} else if (_timeOfDayToDouble(TimeOfDay.now()) >
_timeOfDayToDouble(const TimeOfDay(hour: 8, minute: 20)) &&
_timeOfDayToDouble(TimeOfDay.now()) <=
_timeOfDayToDouble(const TimeOfDay(hour: 9, minute: 25))) {
lessonCount = 3;
} else if (_timeOfDayToDouble(TimeOfDay.now()) >
_timeOfDayToDouble(const TimeOfDay(hour: 9, minute: 25)) &&
_timeOfDayToDouble(TimeOfDay.now()) <=
_timeOfDayToDouble(const TimeOfDay(hour: 10, minute: 15))) {
lessonCount = 4;
} else if (_timeOfDayToDouble(TimeOfDay.now()) >
_timeOfDayToDouble(const TimeOfDay(hour: 10, minute: 15)) &&
_timeOfDayToDouble(TimeOfDay.now()) <=
_timeOfDayToDouble(const TimeOfDay(hour: 11, minute: 30))) {
lessonCount = 5;
} else if (_timeOfDayToDouble(TimeOfDay.now()) >
_timeOfDayToDouble(const TimeOfDay(hour: 11, minute: 30)) &&
_timeOfDayToDouble(TimeOfDay.now()) <=
_timeOfDayToDouble(const TimeOfDay(hour: 12, minute: 20))) {
lessonCount = 6;
} else if (_timeOfDayToDouble(TimeOfDay.now()) >
_timeOfDayToDouble(const TimeOfDay(hour: 12, minute: 20)) &&
_timeOfDayToDouble(TimeOfDay.now()) <=
_timeOfDayToDouble(const TimeOfDay(hour: 13, minute: 30))) {
lessonCount = 7;
} else if (_timeOfDayToDouble(TimeOfDay.now()) >
_timeOfDayToDouble(const TimeOfDay(hour: 13, minute: 30)) &&
_timeOfDayToDouble(TimeOfDay.now()) <=
_timeOfDayToDouble(const TimeOfDay(hour: 14, minute: 20))) {
lessonCount = 8;
} else if (_timeOfDayToDouble(TimeOfDay.now()) >
_timeOfDayToDouble(const TimeOfDay(hour: 14, minute: 20)) &&
_timeOfDayToDouble(TimeOfDay.now()) <=
_timeOfDayToDouble(const TimeOfDay(hour: 15, minute: 10))) {
lessonCount = 9;
} else {
lessonCount = -1;
}
/*
Widget timetable;
if (_timeOfDayToDouble(TimeOfDay.now()) >
_timeOfDayToDouble(const TimeOfDay(hour: , minute: 55))) {
timetable = buildClassTimetable();
} else {
timetable = buildTodayClassTimetable();
}
*/
print(lessonCount);
var view = SingleChildScrollView(
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(20, 30, 20, 5),
child: Text(
'Hallo,',
style: GoogleFonts.robotoSlab(
fontSize: 20,
),
),
),
],
),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(20, 5, 20, 20),
child: buildSettingsString(
"name",
GoogleFonts.robotoSlab(
fontSize: 28,
fontWeight: FontWeight.w800,
),
),
)
],
),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(20, 10, 0, 10),
child: Text(
'Deine nächste Unterrichtsstunde:',
style: GoogleFonts.robotoSlab(
fontSize: 20,
fontWeight: FontWeight.w100,
),
),
)
],
),
Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 10),
child: buildTodayClassTimetableLesson(lessonCount),
),
Row(mainAxisSize: MainAxisSize.max, children: [
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 10),
child: Column(
children: [
GestureDetector(
onTap: () async {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SZ()),
);
},
child: Card(
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: const [
Padding(
padding: EdgeInsets.fromLTRB(20, 20, 10, 10),
child: Icon(
CupertinoIcons.news,
color: Color(0xFFFFBC3B),
size: 48,
),
)
],
),
Row(
mainAxisSize: MainAxisSize.max,
children: const [
Align(
alignment: Alignment(0, 0),
child: Padding(
padding: EdgeInsets.fromLTRB(15, 50, 0, 15),
child: Text(
'Schülerzeitung',
),
),
),
],
)
],
),
),
),
GestureDetector(
onTap: () async {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SB()),
);
},
child: Card(
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: const [
Padding(
padding: EdgeInsets.fromLTRB(20, 20, 10, 10),
child: Icon(
CupertinoIcons.book,
color: Color(0xFFFFBC3B),
size: 48,
),
)
],
),
Row(
mainAxisSize: MainAxisSize.max,
children: const [
Padding(
padding: EdgeInsets.fromLTRB(15, 50, 0, 15),
child: Text(
'Schulbibliothek',
),
)
],
)
],
),
),
),
GestureDetector(
onTap: () async {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SC()),
);
},
child: Card(
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: const [
Padding(
padding: EdgeInsets.fromLTRB(20, 20, 10, 10),
child: Icon(
CupertinoIcons.device_laptop,
color: Color(0xFFFFBC3B),
size: 48,
),
)
],
),
Row(
mainAxisSize: MainAxisSize.max,
children: const [
Padding(
padding: EdgeInsets.fromLTRB(15, 50, 0, 15),
child: Text(
'Schulcomputer',
),
)
],
)
],
),
),
),
GestureDetector(
onTap: () async {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const RoomOverview()),
);
},
child: Card(
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: const [
Padding(
padding: EdgeInsets.fromLTRB(20, 20, 10, 10),
child: Icon(
CupertinoIcons.house_alt,
color: Color(0xFFFFBC3B),
size: 48,
),
)
],
),
Row(
mainAxisSize: MainAxisSize.max,
children: const [
Padding(
padding: EdgeInsets.fromLTRB(15, 50, 0, 15),
child: Text(
'Raumübersicht',
),
)
],
)
],
),
),
),
],
),
),
)
]),
],
));
return view;
} else if (item.label == "Vertretungsplan") {
return LayoutBuilder(builder: (context, constraints) {
double widgetWidth = constraints.maxWidth;
// double widgetHeight = constraints.maxHeight;
var factor;
if (widgetWidth <= 600) {
factor = 1;
} else if (widgetWidth <= 1400) {
factor = 2;
} else if (widgetWidth <= 2000) {
factor = 3;
}
// print(screenType);
return Center(
child: Container(
constraints: BoxConstraints(
// minHeight: 500, //minimum height
// minWidth: 300, // minimum width
//maximum height set to 100% of vertical height
maxWidth: MediaQuery.of(context).size.width / factor,
//maximum width set to 100% of width
),
child: DefaultTabController(
initialIndex: 0,
length: 2,
child: Scaffold(
appBar: AppBar(
elevation: 0,
title: const TabBar(
tabs: <Widget>[
Tab(
text: "Heute",
icon: Icon(CupertinoIcons.calendar_today),
),
Tab(
text: "Neuster Plan",
icon: Icon(CupertinoIcons.calendar),
),
/*Tab(
text: "Archiv",
icon: Icon(CupertinoIcons.archivebox),
),*/
],
),
),
body: TabBarView(
children: <Widget>[
buildTodayClassTimetable(),
buildClassTimetable(),
/*
Center(
child: Text("It's sunny here"),
),
*/
],
),
),
),
),
);
});
} else {
return const Text("Such page does not exist.");
}
// return materialCard(item.label);
}
}

@ -2,6 +2,7 @@
// Generated file. Do not edit. // Generated file. Do not edit.
// //
// ignore_for_file: directives_ordering
// ignore_for_file: lines_longer_than_80_chars // ignore_for_file: lines_longer_than_80_chars
import 'package:fluttertoast/fluttertoast_web.dart'; import 'package:fluttertoast/fluttertoast_web.dart';

@ -2,15 +2,15 @@ import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:http/http.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'networking.dart'; import 'networking.dart';
import 'Dashboard.dart'; import 'dashboard.dart';
Future<bool> checkKey() async { Future<bool> checkKey() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
String? api_key = await prefs.getString('api_key'); String? apiKey = prefs.getString('api_key');
return api_key != null && api_key.isNotEmpty; return apiKey != null && apiKey.isNotEmpty;
} }
class Login extends StatelessWidget { class Login extends StatelessWidget {
@ -37,12 +37,12 @@ class Login extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text("Anmelden"), title: const Text("Anmelden"),
), ),
body: Center( body: Center(
child: SingleChildScrollView( child: SingleChildScrollView(
child: Padding( child: Padding(
padding: EdgeInsets.fromLTRB(20, 20, 20, 20), padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
child: Container( child: Container(
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: maxWidth:
@ -53,23 +53,27 @@ class Login extends StatelessWidget {
children: [ children: [
Image.asset("assets/images/meincantor_r.png", Image.asset("assets/images/meincantor_r.png",
height: 192, width: 192), height: 192, width: 192),
Divider(), const Divider(),
AutofillGroup( AutofillGroup(
child: Column( child: Column(
children: [ children: [
TextField( TextField(
autofillHints: [AutofillHints.username], autofillHints: const [
decoration: InputDecoration( AutofillHints.username
],
decoration: const InputDecoration(
icon: Icon(CupertinoIcons.person), icon: Icon(CupertinoIcons.person),
border: OutlineInputBorder(), border: OutlineInputBorder(),
labelText: 'Benutzername', labelText: 'Benutzername',
), ),
controller: userController, controller: userController,
), ),
Divider(), const Divider(),
TextField( TextField(
autofillHints: [AutofillHints.password], autofillHints: const [
decoration: InputDecoration( AutofillHints.password
],
decoration: const InputDecoration(
icon: Icon(CupertinoIcons.lock), icon: Icon(CupertinoIcons.lock),
border: OutlineInputBorder(), border: OutlineInputBorder(),
labelText: 'Passwort', labelText: 'Passwort',
@ -79,9 +83,9 @@ class Login extends StatelessWidget {
), ),
], ],
)), )),
Divider(), const Divider(),
TextField( TextField(
decoration: InputDecoration( decoration: const InputDecoration(
icon: Icon(CupertinoIcons.lock), icon: Icon(CupertinoIcons.lock),
border: OutlineInputBorder(), border: OutlineInputBorder(),
labelText: '2F2-Code (OTP) [falls aktiviert]', labelText: '2F2-Code (OTP) [falls aktiviert]',
@ -89,37 +93,55 @@ class Login extends StatelessWidget {
obscureText: true, obscureText: true,
controller: otpController, controller: otpController,
), ),
Divider(), const Divider(),
TextField( TextField(
decoration: InputDecoration( decoration: const InputDecoration(
icon: Icon(CupertinoIcons.device_laptop), icon: Icon(CupertinoIcons.device_laptop),
border: OutlineInputBorder(), border: OutlineInputBorder(),
labelText: 'Gerätebezeichnung', labelText: 'Gerätebezeichnung',
), ),
controller: devIdController, controller: devIdController,
), ),
Divider(), const Divider(),
OutlinedButton( OutlinedButton(
onPressed: () async { onPressed: () async {
SharedPreferences prefs = SharedPreferences prefs =
await SharedPreferences.getInstance(); await SharedPreferences.getInstance();
String api_key = await getToken( Response loginResponse = await getToken(
userController.text, userController.text,
passwordController.text, passwordController.text,
otpController.text, otpController.text,
devIdController.text); devIdController.text);
print('Set new API key to $api_key'); if (loginResponse.statusCode == 200) {
await prefs.setString('api_key', api_key); String apiKey = jsonDecode(utf8.decode(
loginResponse.bodyBytes))['token'];
print('Set new API key to $apiKey');
await prefs.setString('api_key', apiKey);
dynamic userinfo = jsonDecode( dynamic userinfo = jsonDecode(
await getUserInfo( await getUserInfo(
userController.text, userController.text,
passwordController.text, passwordController.text,
otpController.text, otpController.text,
devIdController.text)); devIdController.text));
await prefs.setString( await prefs.setString('user',
'user', userinfo['preferred_username']); userinfo['preferred_username']);
await prefs.setString( await prefs.setString(
'name', userinfo['name']); 'name', userinfo['name']);
await prefs.setString(
'class_num',
userinfo['groups'][0]
.replaceAll("_", "/"));
} else if (loginResponse.statusCode ==
401) {
String text = loginResponse.body;
final snackBar = SnackBar(
content: Text('Fehler: $text'));
// Find the ScaffoldMessenger in the widget tree
// and use it to show a SnackBar.
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
}
if (prefs.getString('api_key') != null && if (prefs.getString('api_key') != null &&
prefs prefs
.getString('api_key')! .getString('api_key')!
@ -127,18 +149,15 @@ class Login extends StatelessWidget {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => Dashboard()), builder: (context) =>
const Dashboard()),
); );
} }
}, },
child: Text("Anmelden")) child: const Text("Anmelden"))
], ],
), ),
) )))));
)
)
)
);
}, },
); );
} }

@ -1,13 +1,15 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'Dashboard.dart'; import 'dashboard.dart';
import 'Login.dart'; import 'login.dart';
import 'dart:math'; import 'dart:math';
void main() => runApp(App()); void main() => runApp(const App());
class App extends StatelessWidget { class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
MaterialColor generateMaterialColor(Color color) { MaterialColor generateMaterialColor(Color color) {
return MaterialColor(color.value, { return MaterialColor(color.value, {
50: tintColor(color, 0.5), 50: tintColor(color, 0.5),
@ -38,47 +40,40 @@ class App extends StatelessWidget {
theme: ThemeData( theme: ThemeData(
primaryColor: Palette.primary, primaryColor: Palette.primary,
colorScheme: ColorScheme.fromSwatch( colorScheme: ColorScheme.fromSwatch(
primarySwatch: generateMaterialColor(Palette.accent), primarySwatch: generateMaterialColor(Palette.primary),
).copyWith( ).copyWith(
secondary: generateMaterialColor(Palette.accent), secondary: generateMaterialColor(Palette.accent),
), ),
), ),
darkTheme: ThemeData.from(colorScheme: ColorScheme.dark(primary: Palette.accent)), darkTheme: ThemeData.from(
colorScheme: const ColorScheme.dark(primary: Palette.accent)),
title: "GCG.MeinCantor", title: "GCG.MeinCantor",
home: buildHomePage(), home: buildHomePage(),
/*
routes: <String, WidgetBuilder>{
"/": (_) => Dashboard(),
"/login": (_) => Login()
},
*/
); );
} }
} }
Future<bool> apiKeyEmpty() async { Future<bool> apiKeyEmpty() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
String? api_key = await prefs.getString("api_key"); String? apiKey = prefs.getString("api_key");
return api_key == null || api_key.isEmpty; return apiKey == null || apiKey.isEmpty;
} }
Widget buildHomePage() { Widget buildHomePage() {
return FutureBuilder( return FutureBuilder(
future: apiKeyEmpty(), future: apiKeyEmpty(),
builder: (context, snapshot) { builder: (context, snapshot) {
if(snapshot.data == true) { if (snapshot.data == true) {
return Login(); return Login();
} else if(snapshot.data == false) { } else if (snapshot.data == false) {
return Dashboard(); return const Dashboard();
} else { } else {
return Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} }
} });
);
} }
class Palette { class Palette {
static const Color primary = Color(0xFF1A1A37); static const Color primary = Color(0xFF1A1A37);
static const Color accent = Color(0xFFFFBC3B); static const Color accent = Color(0xFFFFBC3B);
} }

@ -1,39 +1,21 @@
import 'dart:convert'; import 'dart:convert';
import 'main.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'timetable.dart';
import 'Timetable.dart'; import 'login.dart';
import 'Login.dart'; import 'main.dart';
Future<String> getToken( Future<http.Response> getToken(
String user, String password, String otp, String devId) async { String user, String password, String otp, String devId) async {
var uri = Uri.https("mein.cantorgymnasium.de", "/login"); var uri = Uri.https("mein.cantorgymnasium.de", "/login");
String body = String body =
'{"user":"$user", "password": "$password", "otp": "$otp", "devid": "$devId"}'; '{"user":"$user", "password": "$password", "otp": "$otp", "devid": "$devId"}';
print(uri); print(uri);
final response = await http.post(uri, body: body); final response = await http.post(uri, body: body);
return (response);
if (response.statusCode == 200) {
return jsonDecode(utf8.decode(response.bodyBytes))['token'];
} else if(response.statusCode == 401) {
var body = response.body;
Fluttertoast.showToast(
msg: "Fehler: $body",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0
);
throw Exception('Failed to log in');
} else {
throw Exception('Undefined error');
}
} }
Future<String> getUserInfo( Future<String> getUserInfo(
@ -53,18 +35,36 @@ Future<String> getUserInfo(
Future<http.Response> fetchClassTimetable() async { Future<http.Response> fetchClassTimetable() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
var class_num; String classNum;
print(prefs.getString('class_num')); print(prefs.getString('class_num'));
if (prefs.getString('class_num') != null) { if (prefs.getString('class_num') != null) {
class_num = prefs.getString('class_num')!; classNum = prefs.getString('class_num')!.replaceAll("/", "_");
} else { } else {
class_num = '07_1'; classNum = '05_1';
} }
var api_key = prefs.getString('api_key'); var apiKey = prefs.getString('api_key');
var uri = Uri.https("mein.cantorgymnasium.de", "/api/timetable/$class_num"); var uri = Uri.https("mein.cantorgymnasium.de", "/api/timetable/$classNum");
var headers = {"x-api-key": "$api_key"}; var headers = {"x-api-key": "$apiKey"};
print(uri); print(uri);
final response = http.get(uri, headers: headers); final response = http.get(uri, headers: headers).onError((error, stackTrace) { return(http.Response("", 404)); } );
return response;
}
Future<http.Response> fetchTodayClassTimetable() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String classNum;
print(prefs.getString('class_num'));
if (prefs.getString('class_num') != null) {
classNum = prefs.getString('class_num')!.replaceAll("/", "_");
} else {
classNum = '05_1';
}
var apiKey = prefs.getString('api_key');
var uri =
Uri.https("mein.cantorgymnasium.de", "/api/timetable/$classNum/today");
var headers = {"x-api-key": "$apiKey"};
print(uri);
final response = http.get(uri, headers: headers).onError((error, stackTrace) { return(http.Response("", 404)); } );
return response; return response;
} }
@ -79,15 +79,148 @@ Widget buildClassTimetable() {
jsonDecode(utf8.decode(snapshot.data!.bodyBytes))) jsonDecode(utf8.decode(snapshot.data!.bodyBytes)))
.view; .view;
return timetableView; return timetableView;
} } else if (statusCode == 400) {
else if(statusCode == 400) { Navigator.pushReplacement(
Navigator.push(context, MaterialPageRoute(builder: (context) => Login())); context, MaterialPageRoute(builder: (context) => Login()));
} else if (statusCode == 500) {
return const Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Center(child: Text("Es konnte kein Vertretungsplan gefunden werden.")),
);
} else if (statusCode == 404) {
return const Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Center(child: Text("Keine Verbindung mit dem MeinCantor-Server möglich. Bitte prüfe deine Internetverbindung und deine DNS-Einstellungen oder wende dich an den MeinCantor-Support.")),
);
} }
return Center(child: Text('Error $statusCode')); return Center(child: Text('Error $statusCode'));
} else if (snapshot.hasError) { } else if (snapshot.hasError) {
return Text('$snapshot.error'); return Text('$snapshot.error');
} else { } else {
return Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
}
},
);
}
Widget buildTodayClassTimetable() {
return FutureBuilder<http.Response>(
future: fetchTodayClassTimetable(),
builder: (context, snapshot) {
if (snapshot.hasData) {
int statusCode = snapshot.data!.statusCode;
if (statusCode == 200) {
ListView timetableView = ClassTimetableBuilder.buildView(
jsonDecode(utf8.decode(snapshot.data!.bodyBytes)))
.view;
return timetableView;
} else if (statusCode == 400) {
Navigator.push(
context, MaterialPageRoute(builder: (context) => Login()));
} else if (statusCode == 500) {
return const Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Center(child: Text("Es konnte kein Vertretungsplan für heute gefunden werden.")),
);
} else if (statusCode == 404) {
return const Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Center(child: Text("Keine Verbindung mit dem MeinCantor-Server möglich. Bitte prüfe deine Internetverbindung und deine DNS-Einstellungen oder wende dich an den MeinCantor-Support.")),
);
}
return Center(child: Text('Error $statusCode'));
} else if (snapshot.hasError) {
return Text('$snapshot.error');
} else {
return const Center(child: CircularProgressIndicator());
}
},
);
}
Widget buildClassTimetableLesson(int count) {
return FutureBuilder<http.Response>(
future: fetchClassTimetable(),
builder: (context, snapshot) {
if (snapshot.hasData) {
int statusCode = snapshot.data!.statusCode;
if (statusCode == 200) {
List<Widget> lessons = LessonsListBuilder.buildList(
jsonDecode(utf8.decode(snapshot.data!.bodyBytes)), count: count)
.lessons;
return Column(children: lessons);
} else if (statusCode == 400) {
Navigator.push(
context, MaterialPageRoute(builder: (context) => Login()));
} else if (statusCode == 500) {
return const Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Center(child: Text("Es konnte kein Vertretungsplan gefunden werden.")),
);
} else if (statusCode == 404) {
return const Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Center(child: Text("Keine Verbindung mit dem MeinCantor-Server möglich. Bitte prüfe deine Internetverbindung und deine DNS-Einstellungen oder wende dich an den MeinCantor-Support")),
);
}
return Center(child: Text('Error $statusCode'));
} else if (snapshot.hasError) {
return Text('$snapshot.error');
} else {
return const Center(child: CircularProgressIndicator());
}
},
);
}
Widget buildTodayClassTimetableLesson(int count) {
return FutureBuilder<http.Response>(
future: fetchTodayClassTimetable(),
builder: (context, snapshot) {
if (snapshot.hasData) {
int statusCode = snapshot.data!.statusCode;
if (statusCode == 200) {
List<Widget> lessons = LessonsListBuilder.buildList(
jsonDecode(utf8.decode(snapshot.data!.bodyBytes)), count: count)
.lessons;
if (lessons.isNotEmpty) {
return Column(children: lessons);
} else {
var chars = Runes('Keine Stunden mehr gefunden. Sieht so aus als hättest du Schluss für heute \u{1F389}');
List<Widget> cardChildren = [];
cardChildren.add(ListTile(
title: Text(String.fromCharCodes(chars),
style: const TextStyle(color: Palette.primary))
));
Card card = Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
color: Colors.white,
child: Column(
children: cardChildren,
));
return card;
}
} else if (statusCode == 400) {
Navigator.push(
context, MaterialPageRoute(builder: (context) => Login()));
} else if (statusCode == 500) {
return const Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Center(child: Text("Es konnte kein Vertretungsplan für heute gefunden werden.")),
);
} else if (statusCode == 404) {
return const Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Center(child: Text("Keine Verbindung mit dem MeinCantor-Server möglich. Bitte prüfe deine Internetverbindung und deine DNS-Einstellungen oder wende dich an den MeinCantor-Support")),
);
}
return Center(child: Text('Error $statusCode'));
} else if (snapshot.hasError) {
return Text('$snapshot.error');
} else {
return const Center(child: CircularProgressIndicator());
} }
}, },
); );
@ -95,21 +228,11 @@ Widget buildClassTimetable() {
Future<http.Response> fetchClassesList() async { Future<http.Response> fetchClassesList() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
/* var apiKey = prefs.getString('api_key');
var class_num;
print(prefs.getString('class_num'));
if(prefs.getString('class_num') != null){
class_num = prefs.getString('class_num')!;
} else {
class_num = '07_1';
}
*/
// await prefs.setString('api_key', "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJHZW9yZy1DYW50b3ItR3ltbmFzaXVtIEhhbGxlKFNhYWxlKSIsInVzZXIiOiJkZW55cy5rb25vdmFsb3ZAcG0ubWUiLCJyb2xlcyI6WyJTdHVkZW50IiwiQWRtaW4iXSwiYmxhY2tsaXN0IjpbIi9jbGFzc2VzIl0sIndoaXRlbGlzdCI6WyIvaGVsbG8vc2Vuc2l0aXZlIl0sImppZCI6IkFwcERldiBBbHBoYSBkdW1teSBrZXlAMDIvMDgvMjAyMSAxOTowODowOSIsImV4cCI6MTY1OTQ2NzI4OX0.a7Q83PK3ybeV7Bui-_rX1o6IZx1cNa6vsvUGG-kfqtc");
var api_key = prefs.getString('api_key');
var uri = Uri.https("mein.cantorgymnasium.de", "/api/classes"); var uri = Uri.https("mein.cantorgymnasium.de", "/api/classes");
var headers = {"x-api-key": "$api_key"}; var headers = {"x-api-key": "$apiKey"};
print(uri); print(uri);
final response = http.get(uri, headers: headers); final response = http.get(uri, headers: headers).onError((error, stackTrace) { return(http.Response("", 404)); } );
return response; return response;
} }
@ -120,20 +243,30 @@ Widget buildClassesChooser() {
if (snapshot.hasData) { if (snapshot.hasData) {
int statusCode = snapshot.data!.statusCode; int statusCode = snapshot.data!.statusCode;
if (statusCode == 200) { if (statusCode == 200) {
// List<Widget> children = [];
//ClassTimetable.fromJson(jsonDecode(utf8.decode(snapshot.data!.bodyBytes))).timetable.forEach((element) {
//});
List<String> items = []; List<String> items = [];
jsonDecode(utf8.decode(snapshot.data!.bodyBytes)).forEach((value) { jsonDecode(utf8.decode(snapshot.data!.bodyBytes)).forEach((value) {
items.add(value..toString()); items.add(value..toString());
}); });
return ClassesChooser(items: items); return ClassesChooser(items: items);
} else if (statusCode == 400) {
Navigator.push(
context, MaterialPageRoute(builder: (context) => Login()));
} else if (statusCode == 500) {
return const Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Center(child: Text("Serverfehler. Bitte wende dich an den MeinCantor-Support.")),
);
} else if (statusCode == 404) {
return const Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Center(child: Text("Keine Verbindung mit dem MeinCantor-Server möglich. Bitte prüfe deine Internetverbindung und deine DNS-Einstellungen oder wende dich an den MeinCantor-Support")),
);
} }
return Text('$statusCode'); return Text('$statusCode');
} else if (snapshot.hasError) { } else if (snapshot.hasError) {
return Text('$snapshot.error'); return Text('$snapshot.error');
} else { } else {
return Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} }
}, },
); );
@ -150,37 +283,38 @@ class ClassesChooser extends StatefulWidget {
class _ClassesChooserState extends State<ClassesChooser> { class _ClassesChooserState extends State<ClassesChooser> {
final List<String> items; final List<String> items;
//final String dropdownValue; //final String dropdownValue;
var dropdownValue; String? dropdownValue;
_ClassesChooserState(this.items); _ClassesChooserState(this.items);
@override
void initState() {
super.initState();
_read(); // read in initState
}
_read() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
dropdownValue = prefs.getString("class_num"); // get the value
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// var dropdown_items =
return DropdownButtonFormField<String>( return DropdownButtonFormField<String>(
// value: dropdownValue, value: dropdownValue,
/*icon: const Icon(Icons.arrow_downward), decoration: const InputDecoration(
iconSize: 24,
elevation: 16,*/
// style: const TextStyle(color: Color(0xFFFFBC3B)),
/*underline: Container(
height: 2,
color: Color(0xFFFFBC3B),
)
*/
decoration: InputDecoration(
icon: Icon(CupertinoIcons.number), icon: Icon(CupertinoIcons.number),
border: OutlineInputBorder(), border: OutlineInputBorder(),
labelText: 'Klasse (05_1, 07_3, 10_2...)', labelText: 'Klasse (05/1, 07/3, 10/2...)',
), ),
// icon: Icon(CupertinoIcons.number),
onChanged: (String? newValue) { onChanged: (String? newValue) {
setState(() async { setState(() async {
dropdownValue = newValue!; dropdownValue = newValue!;
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
String class_num = newValue; String classNum = newValue;
print('Set new class to $class_num'); print('Set new class to $classNum');
await prefs.setString('class_num', class_num); await prefs.setString('class_num', classNum);
}); });
}, },
items: items.map<DropdownMenuItem<String>>((String value) { items: items.map<DropdownMenuItem<String>>((String value) {

18
lib/raumuebersicht.dart Normal file

@ -0,0 +1,18 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class RoomOverview extends StatelessWidget {
const RoomOverview ({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Raumübersicht"),
centerTitle: true,
),
body: const Center(
child: Text("Derzeit nichts hier..."),
));
}
}

18
lib/schuelerzeitung.dart Normal file

@ -0,0 +1,18 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class SZ extends StatelessWidget {
const SZ({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Schülerzeitung"),
centerTitle: true,
),
body: const Center(
child: Text("Derzeit nichts hier..."),
));
}
}

18
lib/schulbibliothek.dart Normal file

@ -0,0 +1,18 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class SB extends StatelessWidget {
const SB ({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Schulbibliothek"),
centerTitle: true,
),
body: const Center(
child: Text("Derzeit nichts hier..."),
));
}
}

18
lib/schulcomputer.dart Normal file

@ -0,0 +1,18 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class SC extends StatelessWidget {
const SC ({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Schulcomputer"),
centerTitle: true,
),
body: const Center(
child: Text("Derzeit nichts hier..."),
));
}
}

65
lib/settings.dart Normal file

@ -0,0 +1,65 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'networking.dart';
class Settings extends StatelessWidget {
const Settings({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Einstellungen"),
centerTitle: true,
),
body: ListView(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
children: [
const ListTile(
leading: Icon(CupertinoIcons.rectangle_grid_1x2),
title: Text("Plan")),
/*
Padding(
padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
child: TextField(
decoration: InputDecoration(
icon: Icon(CupertinoIcons.lock),
border: OutlineInputBorder(),
labelText: 'API Key (POST /login)',
),
onSubmitted: (String value) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String api_key = value;
print('Set new API key to $api_key');
await prefs.setString('api_key', api_key);
}
)
),
*/
const Divider(),
buildClassesChooser(),
const Divider(),
const ListTile(
leading: Icon(Icons.code), title: Text("Entwickleroptionen")),
const Divider(),
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: TextField(
decoration: const InputDecoration(
icon: Icon(CupertinoIcons.lock),
border: OutlineInputBorder(),
labelText: 'API Key (POST /login)',
),
onSubmitted: (String value) async {
SharedPreferences prefs =
await SharedPreferences.getInstance();
String apiKey = value;
print('Set new API key to $apiKey');
await prefs.setString('api_key', apiKey);
})),
const Divider(),
],
));
}
}

261
lib/timetable.dart Normal file

@ -0,0 +1,261 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
class ClassTimetableBuilder {
final ListView view;
ClassTimetableBuilder({required this.view});
factory ClassTimetableBuilder.buildView(Map<String, dynamic> json) {
return ClassTimetableBuilder(
view: ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: LessonsListBuilder.buildList(json).lessons));
}
}
class LessonsListBuilder {
final List<Widget> lessons;
LessonsListBuilder({required this.lessons});
factory LessonsListBuilder.buildList(Map<String, dynamic> json, {int count: 0}) {
List<Widget> children = [];
ClassTimetable.fromJson(json).timetable.forEach((element) {
List<Widget> cardChildren = [];
print(element.count.toString() + " " + count.toString());
if (element.count.toString() == count.toString() || count == 0) {
print("teeee");
cardChildren.add(ListTile(
title: Text(element.count.toString() + '.' + ' ' + element.name,
style: TextStyle(color: element.fontColor)),
subtitle: Row(children: [
Icon(CupertinoIcons.person, color: element.fontColor),
const SizedBox(width: 5),
Text(element.teacher, style: TextStyle(color: element.fontColor)),
const Spacer(),
Icon(CupertinoIcons.home, color: element.fontColor),
const SizedBox(width: 5),
Text(element.room, style: TextStyle(color: element.fontColor))
]),
leading: Icon(CupertinoIcons.time, color: element.fontColor)));
if (element.info != '') {
cardChildren.add(ListTile(
title: Text(element.info,
style: TextStyle(color: element.fontColor))));
}
Card card = Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
color: element.color,
child: Column(
children: cardChildren,
));
children.add(card);
}
});
return LessonsListBuilder(lessons: children);
}
}
class ClassTimetable {
final List timetable;
ClassTimetable({required this.timetable});
factory ClassTimetable.fromJson(Map<String, dynamic> json) {
print(json);
List<TimetableLesson> lessons = [];
json['courses'].forEach((value) {
var name;
var teacher;
var room;
dynamic teachers = {
'Poli': 'Herr Polity',
'Zura': 'Frau Zuralski',
'Enzi': 'Frau Enzian',
'Bütt': 'Frau Büttner',
'Brod': 'Herr Brode',
'Rink': 'Frau Rinke',
'Schk': 'Frau Schmidt',
'Rudo': 'Frau Rudolph',
'Kipp': 'Frau Kipping',
'Bach': 'Frau Bachran',
'Bad': 'Herr Bader',
'Prei': 'Frau Preiß',
'Scha': 'Frau Schapitz',
'&nbsp;': '',
'Link': 'Herr Linke',
'Stei': 'Herr Stein',
'Tupp': 'Frau Tuppack',
'Hoff': 'Frau Hoffman',
'Knol': 'Frau Knoll',
'Bet': 'Frau Bethin',
'Schu': 'Frau Schulz',
'Seid': 'Frau Seidel',
'Krug': 'Frau Krug',
'Laer': 'Frau Langer',
'Youn': 'Frau Younso',
'Härt': 'Frau Härtig',
'Bros': 'Frau Brosig',
'Ber': 'Frau Bernhardt',
'Stüb': 'Frau Stüber',
'Bor': 'Frau Borchert',
'Dubb': 'Frau Dubberstein',
'Tren': 'Frau Trentsch',
'Meit': 'Herr Meitzner',
'Stol': 'Frau Stolpe',
'Jac': 'Frau Jacob',
'Jüne': 'Herr Jünemann',
'Bert': 'Frau Berthelmann',
'Felk': 'Frau Felke',
'Kimm': 'Herr Kimmel',
'PM1': 'Pädagosische(r) Mitarbeiter(in) 1',
'Schet': 'Herr Schetler',
'Mani': 'Herr Manigk',
'Segg': 'Frau Seggern',
'Opel': 'Frau Opel-Fritzler',
'Möll': 'Frau Möller',
'Laen': 'Herr Langen',
'Plin': 'Herr Plinke',
'Koch': 'Herr Koch',
'Gors': 'Herr Gorsler',
'Krau': 'Herr Krause',
'Henk': 'Frau Henke',
'Wolf': 'Herr Wolf'
};
if (value['Fa'].runtimeType != String) {
name = value['Fa']['#text'];
} else {
name = value['Fa'];
}
if (value['Le'].runtimeType != String) {
teacher = value['Le']['#text'];
} else {
teacher = value['Le'];
}
print(value['Ra']);
if (value['Ra'].runtimeType != String && value['Ra'].runtimeType != int) {
if (value['Ra']['#text'] == '&nbsp;') {
room = '';
} else if (value['Ra']['#text'] == null) {
room = '';
} else {
room = value['Ra']['#text'];
}
} else if (value['Ra'] == '&nbsp;') {
room = '';
} else {
room = value['Ra'];
}
Color fontColor;
if (name == '---') {
fontColor = Colors.red;
} else {
fontColor = Colors.white;
}
var info;
if (value['If'].runtimeType != String) {
info = '';
} else {
info = value['If'];
}
dynamic colors = {
'Bio': Colors.green,
'Mat': Colors.indigo,
'matL1': Colors.indigo,
'matL2': Colors.indigo,
'matL3': Colors.indigo,
'Deu': Colors.red,
'deu1': Colors.red,
'deu2': Colors.red,
'deu3': Colors.red,
'Kun': Colors.deepPurple,
'kun1': Colors.deepPurple,
'kun2': Colors.deepPurple,
'kun3': Colors.deepPurple,
'Geo': Colors.brown,
'Lat': Colors.teal,
'lat1': Colors.teal,
'lat2': Colors.teal,
'lat3': Colors.teal,
'Che': Colors.lightGreen,
'Eng': Colors.amber,
'eng1': Colors.amber,
'eng2': Colors.amber,
'eng3': Colors.amber,
'Phy': Colors.cyan,
'phy1': Colors.cyan,
'phy2': Colors.cyan,
'phy3': Colors.cyan,
'Inf': Colors.tealAccent[400],
'Mus': Colors.deepOrange,
'mus1': Colors.deepOrange,
'mus2': Colors.deepOrange,
'mus3': Colors.deepOrange,
'Lme': Colors.amber[700],
'Ges': Colors.grey,
'ges1': Colors.grey,
'ges2': Colors.grey,
'ges3': Colors.grey,
'Spa': Colors.redAccent,
'Frz': Colors.amberAccent[700],
'frz1': Colors.amberAccent[700],
'---': Colors.white
};
dynamic names = {
'Bio': 'Biologie',
'Mat': 'Mathematik',
'matL1': 'Mathematik Leistungskurs 1',
'Kun': 'Kunst',
'Mus': 'Musik',
'Geo': 'Geographie',
'Ges': 'Geschichte',
'Che': 'Chemie',
'Lat': 'Latein',
'Inf': 'Informatik',
'Eng': 'Englisch',
'Frz': 'Französisch',
'frz1': 'Französisch 1',
'Phy': 'Physik',
'---': '---',
'Spo': 'Sport',
'Deu': 'Deutsch',
'deu1': 'Deutsch 1',
'deu2': 'Deutsch 2',
'deu3': 'Deutsch 3',
'Lme': 'Lernmethoden',
'Eth': 'Ethik',
'EvR': 'Evangelische Religion',
'Soz': 'Sozialkunde',
'Ast': 'Astronomie',
'Spa': 'Spanisch',
'FK': 'Fachkurs',
'JIA': 'Junior-Ingenieur-Akademie',
'WoU': 'Wahlobligatorischer Unterricht'
};
lessons.add(TimetableLesson(
value['St'],
names[name] ?? name.toString(),
teachers[teacher] ?? teacher.toString(),
room.toString(),
value['If'].toString(),
colors[name] ?? Colors.grey[700],
fontColor,
info));
});
return ClassTimetable(timetable: lessons);
}
}
class TimetableLesson {
final int count;
final String name;
final String teacher;
final String room;
final String comment;
final Color color;
final Color fontColor;
final String info;
const TimetableLesson(this.count, this.name, this.teacher, this.room,
this.comment, this.color, this.fontColor, this.info);
}

@ -2,6 +2,8 @@
// Generated file. Do not edit. // Generated file. Do not edit.
// //
// clang-format off
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"

@ -2,6 +2,8 @@
// Generated file. Do not edit. // Generated file. Do not edit.
// //
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_ #ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_ #define GENERATED_PLUGIN_REGISTRANT_

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1 version: 0.6.5-alpha
environment: environment:
sdk: ">=2.12.0 <3.0.0" sdk: ">=2.12.0 <3.0.0"

@ -2,6 +2,8 @@
// Generated file. Do not edit. // Generated file. Do not edit.
// //
// clang-format off
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"

@ -2,6 +2,8 @@
// Generated file. Do not edit. // Generated file. Do not edit.
// //
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_ #ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_ #define GENERATED_PLUGIN_REGISTRANT_