2dc41b7e24
- fixed issues
974 lines
42 KiB
Dart
974 lines
42 KiB
Dart
// GCG.MeinCantor - Die Schulplattform für Cantorianer.
|
|
// Copyright (C) 2021-2022 Georg-Cantor-Gymnasium Halle (Saale)
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published
|
|
// by the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
import 'dart:convert';
|
|
import 'dart:math';
|
|
|
|
import 'package:background_fetch/background_fetch.dart';
|
|
import 'package:meincantor/background_fetch.dart';
|
|
import 'package:meincantor/const.dart';
|
|
import 'package:meincantor/schuelerzeitung.dart';
|
|
import 'package:meincantor/Settings/dashboard.dart';
|
|
|
|
import 'package:meincantor/main.dart';
|
|
import 'package:meincantor/networking.dart';
|
|
import 'package:meincantor/login.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
|
import 'package:http/http.dart' as http;
|
|
|
|
import 'package:meincantor/news.dart';
|
|
|
|
Future<String> getSettingsString(String key) async {
|
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
String? value = prefs.getString(key);
|
|
if (value == null || value.isEmpty) {
|
|
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 SizedBox.shrink();
|
|
}
|
|
});
|
|
}
|
|
|
|
Future<List<String>> getFavClasses() async {
|
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
List<String>? listJson = prefs.getStringList("favClasses");
|
|
if (listJson == null || listJson.isEmpty) {
|
|
return <String>[];
|
|
} else {
|
|
return listJson;
|
|
}
|
|
}
|
|
|
|
List<Widget> buildFavClasses(
|
|
BuildContext context, List<String> favClasses, Function removeFavClass) {
|
|
if (favClasses.isEmpty) {
|
|
return [const SizedBox.shrink()];
|
|
} else {
|
|
List<Widget> list = [];
|
|
for (var element in favClasses) {
|
|
var card = SizedBox(
|
|
width: 170,
|
|
child: GestureDetector(
|
|
onTap: () async {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => DefaultTabController(
|
|
initialIndex: 0,
|
|
length: 3,
|
|
child: Scaffold(
|
|
appBar: AppBar(
|
|
leading: null,
|
|
elevation: 0,
|
|
title: Text("Klasse $element"),
|
|
bottom: const TabBar(
|
|
indicatorColor: Palette.accent,
|
|
enableFeedback: true,
|
|
indicatorPadding: EdgeInsets.all(5),
|
|
indicatorSize: TabBarIndicatorSize.label,
|
|
tabs: <Widget>[
|
|
Tab(
|
|
text: "Heute",
|
|
icon: Icon(Icons.calendar_today_outlined),
|
|
),
|
|
Tab(
|
|
text: "Morgen",
|
|
icon: Icon(MdiIcons.calendarToday),
|
|
),
|
|
Tab(
|
|
text: "Neuster Plan",
|
|
icon: Icon(Icons.calendar_view_day_outlined),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
body: TabBarView(
|
|
children: <Widget>[
|
|
LayoutBuilder(builder: (context, constraints) {
|
|
double widgetWidth = constraints.maxWidth;
|
|
|
|
int factor;
|
|
|
|
if (widgetWidth <= 600) {
|
|
factor = 1;
|
|
} else if (widgetWidth <= 1400) {
|
|
factor = 2;
|
|
} else if (widgetWidth <= 2000) {
|
|
factor = 3;
|
|
} else {
|
|
factor = 1;
|
|
}
|
|
|
|
return Center(
|
|
heightFactor: 1,
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth:
|
|
MediaQuery.of(context).size.width /
|
|
factor,
|
|
),
|
|
child: buildTimetable(
|
|
fetchClassTimetable(
|
|
"/${DateFormat("yyyyMMdd").format(DateTime.now())}",
|
|
element),
|
|
"Vertretungsplan für heute"),
|
|
));
|
|
}),
|
|
LayoutBuilder(builder: (context, constraints) {
|
|
double widgetWidth = constraints.maxWidth;
|
|
|
|
int factor;
|
|
|
|
if (widgetWidth <= 600) {
|
|
factor = 1;
|
|
} else if (widgetWidth <= 1400) {
|
|
factor = 2;
|
|
} else if (widgetWidth <= 2000) {
|
|
factor = 3;
|
|
} else {
|
|
factor = 1;
|
|
}
|
|
|
|
return Center(
|
|
heightFactor: 1,
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth:
|
|
MediaQuery.of(context).size.width /
|
|
factor,
|
|
),
|
|
child: buildTimetable(
|
|
fetchClassTimetable(
|
|
"/${DateFormat("yyyyMMdd").format(DateTime.now().add(const Duration(days: 1)))}",
|
|
element),
|
|
"Vertretungsplan für morgen"),
|
|
));
|
|
}),
|
|
LayoutBuilder(builder: (context, constraints) {
|
|
double widgetWidth = constraints.maxWidth;
|
|
|
|
int factor;
|
|
|
|
if (widgetWidth <= 600) {
|
|
factor = 1;
|
|
} else if (widgetWidth <= 1400) {
|
|
factor = 2;
|
|
} else if (widgetWidth <= 2000) {
|
|
factor = 3;
|
|
} else {
|
|
factor = 1;
|
|
}
|
|
|
|
return Center(
|
|
heightFactor: 1,
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: MediaQuery.of(context)
|
|
.size
|
|
.width /
|
|
factor,
|
|
),
|
|
child: buildTimetable(
|
|
fetchClassTimetable(
|
|
"/latest", element),
|
|
"aktueller Vertretungsplan")));
|
|
}),
|
|
],
|
|
),
|
|
))));
|
|
},
|
|
onLongPress: () async {
|
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
List<String> stringList = prefs.getStringList("favClasses")!;
|
|
stringList.remove(element);
|
|
prefs.setStringList("favClasses", stringList);
|
|
removeFavClass(element);
|
|
},
|
|
child: Card(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(10),
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topRight,
|
|
end: Alignment.bottomLeft,
|
|
colors: [
|
|
Colors.primaries[
|
|
Random().nextInt(Colors.primaries.length)],
|
|
Colors.primaries[
|
|
Random().nextInt(Colors.primaries.length)],
|
|
],
|
|
)),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(10),
|
|
child: Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.fromLTRB(15, 15, 15, 15),
|
|
child: Text(
|
|
element,
|
|
style: const TextStyle(color: Colors.white),
|
|
textScaleFactor: 2.0,
|
|
),
|
|
),
|
|
),
|
|
)),
|
|
),
|
|
),
|
|
);
|
|
list.add(card);
|
|
}
|
|
return (list);
|
|
}
|
|
}
|
|
|
|
class Dashboard extends StatefulWidget {
|
|
const Dashboard({Key? key, this.restorationId}) : super(key: key);
|
|
|
|
final String? restorationId;
|
|
|
|
@override
|
|
State<StatefulWidget> createState() => _DashboardState();
|
|
}
|
|
|
|
class _DashboardState extends State<Dashboard> with RestorationMixin {
|
|
final RestorableInt _currentIndex = RestorableInt(0);
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
initPlatformState();
|
|
}
|
|
|
|
Future<void> initPlatformState() async {
|
|
// Configure BackgroundFetch.
|
|
int status = await BackgroundFetch.configure(
|
|
BackgroundFetchConfig(
|
|
minimumFetchInterval: 15,
|
|
stopOnTerminate: false,
|
|
enableHeadless: true,
|
|
startOnBoot: true,
|
|
requiresBatteryNotLow: false,
|
|
requiresCharging: false,
|
|
requiresStorageNotLow: false,
|
|
requiresDeviceIdle: false,
|
|
requiredNetworkType: NetworkType.ANY), (String taskId) async {
|
|
// <-- Event handler
|
|
// This is the fetch-event callback.
|
|
print("[BackgroundFetch] Event received $taskId");
|
|
|
|
await backgroundFetchTimetable();
|
|
await backgroundFetchArticles();
|
|
|
|
// IMPORTANT: You must signal completion of your task or the OS can punish your app
|
|
// for taking too long in the background.
|
|
BackgroundFetch.finish(taskId);
|
|
}, (String taskId) async {
|
|
// <-- Task timeout handler.
|
|
// This task has exceeded its allowed running-time. You must stop what you're doing and immediately .finish(taskId)
|
|
print("[BackgroundFetch] TASK TIMEOUT taskId: $taskId");
|
|
BackgroundFetch.finish(taskId);
|
|
});
|
|
print('[BackgroundFetch] configure success: $status');
|
|
|
|
// If the widget was removed from the tree while the asynchronous platform
|
|
// message was in flight, we want to discard the reply rather than calling
|
|
// setState to update our non-existent appearance.
|
|
if (!mounted) return;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
var bottomNavBarItems = [
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(MdiIcons.homeOutline),
|
|
label: "Startseite",
|
|
),
|
|
const BottomNavigationBarItem(
|
|
icon: Icon(MdiIcons.timetable), label: "Vertretungsplan"),
|
|
];
|
|
return Scaffold(
|
|
body: _DashboardBottomNavView(
|
|
key: UniqueKey(), item: bottomNavBarItems[_currentIndex.value]),
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
showUnselectedLabels: false,
|
|
items: bottomNavBarItems,
|
|
currentIndex: _currentIndex.value,
|
|
onTap: (index) {
|
|
setState(() {
|
|
_currentIndex.value = index;
|
|
});
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
String? get restorationId => widget.restorationId;
|
|
|
|
@override
|
|
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
|
|
registerForRestoration(_currentIndex, 'bottom_navigation_tab_index');
|
|
}
|
|
}
|
|
|
|
class _DashboardBottomNavView extends StatefulWidget {
|
|
const _DashboardBottomNavView({Key? key, required this.item})
|
|
: super(key: key);
|
|
final BottomNavigationBarItem item;
|
|
|
|
@override
|
|
// ignore: no_logic_in_create_state
|
|
State<StatefulWidget> createState() => _DashboardBottomNavViewState(item);
|
|
}
|
|
|
|
class _DashboardBottomNavViewState extends State<_DashboardBottomNavView> {
|
|
final BottomNavigationBarItem item;
|
|
_DashboardBottomNavViewState(this.item);
|
|
|
|
List<String> favClasses = [];
|
|
|
|
void removeFavClass(String classNum) {
|
|
setState(() {
|
|
favClasses.remove(classNum);
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final drawerElements = ListView(
|
|
children: [
|
|
UserAccountsDrawerHeader(
|
|
accountName: buildSettingsString('name', const TextStyle()),
|
|
accountEmail: buildSettingsString('email', const TextStyle()),
|
|
currentAccountPicture: FutureBuilder(
|
|
future: Future.sync(() async {
|
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
String? user = prefs.getString("user");
|
|
if (user == null || user.isEmpty) {
|
|
user = "";
|
|
}
|
|
String? name = prefs.getString("name");
|
|
if (name == null || name.isEmpty) {
|
|
name = "";
|
|
}
|
|
Map data = {"user": user, "name": name};
|
|
return data;
|
|
}),
|
|
builder: (context, snapshot) {
|
|
if (snapshot.hasData) {
|
|
String url = "$avatarUrl/${(snapshot.data! as Map)['user']}";
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
image: DecorationImage(
|
|
fit: BoxFit.scaleDown,
|
|
image: NetworkImage(url))));
|
|
} else {
|
|
return const CircularProgressIndicator();
|
|
}
|
|
},
|
|
)),
|
|
Padding(
|
|
padding: const EdgeInsets.all(5),
|
|
child: ListTile(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(15.0)),
|
|
title: const Text("Einstellungen"),
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(builder: (context) => const Settings()),
|
|
);
|
|
},
|
|
leading: const Icon(Icons.settings_outlined),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.all(5),
|
|
child: ListTile(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(15.0)),
|
|
title: const Text("Abmelden"),
|
|
onTap: () async {
|
|
return showDialog<void>(
|
|
context: context,
|
|
barrierDismissible: false, // user must tap button!
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
title: const Text('Abmelden'),
|
|
content: SingleChildScrollView(
|
|
child: ListBody(
|
|
children: const <Widget>[
|
|
Text(
|
|
'Dabei werden alle persönlichen Enstellungen gelöscht!'),
|
|
],
|
|
),
|
|
),
|
|
actions: <Widget>[
|
|
TextButton(
|
|
child: const Text('Bestätigen'),
|
|
onPressed: () async {
|
|
SharedPreferences prefs =
|
|
await SharedPreferences.getInstance();
|
|
prefs.clear();
|
|
Navigator.pushReplacement(
|
|
context,
|
|
MaterialPageRoute(builder: (context) => Login()),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
},
|
|
leading: const Icon(Icons.exit_to_app_outlined),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
if (item.label == "Startseite") {
|
|
double _timeOfDayToDouble(TimeOfDay tod) => tod.hour + tod.minute / 60.0;
|
|
int lessonCount;
|
|
_timeOfDayToDouble(TimeOfDay.now()) <=
|
|
_timeOfDayToDouble(const TimeOfDay(hour: 7, minute: 30))
|
|
? lessonCount = 1
|
|
: _timeOfDayToDouble(TimeOfDay.now()) > _timeOfDayToDouble(const TimeOfDay(hour: 7, minute: 30)) &&
|
|
_timeOfDayToDouble(TimeOfDay.now()) <=
|
|
_timeOfDayToDouble(const TimeOfDay(hour: 8, minute: 20))
|
|
? lessonCount = 2
|
|
: _timeOfDayToDouble(TimeOfDay.now()) > _timeOfDayToDouble(const TimeOfDay(hour: 8, minute: 20)) &&
|
|
_timeOfDayToDouble(TimeOfDay.now()) <=
|
|
_timeOfDayToDouble(
|
|
const TimeOfDay(hour: 9, minute: 25))
|
|
? lessonCount = 3
|
|
: _timeOfDayToDouble(TimeOfDay.now()) > _timeOfDayToDouble(const TimeOfDay(hour: 9, minute: 25)) &&
|
|
_timeOfDayToDouble(TimeOfDay.now()) <=
|
|
_timeOfDayToDouble(
|
|
const TimeOfDay(hour: 10, minute: 15))
|
|
? lessonCount = 4
|
|
: _timeOfDayToDouble(TimeOfDay.now()) >
|
|
_timeOfDayToDouble(
|
|
const TimeOfDay(hour: 10, minute: 15)) &&
|
|
_timeOfDayToDouble(TimeOfDay.now()) <=
|
|
_timeOfDayToDouble(
|
|
const TimeOfDay(hour: 11, minute: 30))
|
|
? lessonCount = 5
|
|
: _timeOfDayToDouble(TimeOfDay.now()) > _timeOfDayToDouble(const TimeOfDay(hour: 11, minute: 30)) &&
|
|
_timeOfDayToDouble(TimeOfDay.now()) <=
|
|
_timeOfDayToDouble(
|
|
const TimeOfDay(hour: 12, minute: 20))
|
|
? lessonCount = 6
|
|
: _timeOfDayToDouble(TimeOfDay.now()) > _timeOfDayToDouble(const TimeOfDay(hour: 12, minute: 20)) &&
|
|
_timeOfDayToDouble(TimeOfDay.now()) <=
|
|
_timeOfDayToDouble(const TimeOfDay(
|
|
hour: 13, minute: 30))
|
|
? lessonCount = 7
|
|
: _timeOfDayToDouble(TimeOfDay.now()) > _timeOfDayToDouble(const TimeOfDay(hour: 13, minute: 30)) &&
|
|
_timeOfDayToDouble(TimeOfDay.now()) <=
|
|
_timeOfDayToDouble(const TimeOfDay(hour: 14, minute: 20))
|
|
? lessonCount = 8
|
|
: _timeOfDayToDouble(TimeOfDay.now()) > _timeOfDayToDouble(const TimeOfDay(hour: 14, minute: 20)) && _timeOfDayToDouble(TimeOfDay.now()) <= _timeOfDayToDouble(const TimeOfDay(hour: 15, minute: 10))
|
|
? lessonCount = 9
|
|
: _timeOfDayToDouble(TimeOfDay.now()) > _timeOfDayToDouble(const TimeOfDay(hour: 15, minute: 10)) && _timeOfDayToDouble(TimeOfDay.now()) <= _timeOfDayToDouble(const TimeOfDay(hour: 16, minute: 00))
|
|
? lessonCount = 10
|
|
: lessonCount = -1;
|
|
|
|
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),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(20, 20, 20, 10),
|
|
child: Wrap(
|
|
children: [
|
|
SizedBox(
|
|
width: 175,
|
|
child: GestureDetector(
|
|
onTap: () async {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(builder: (context) => const SZ()),
|
|
);
|
|
},
|
|
child: Card(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: const Padding(
|
|
padding: EdgeInsets.all(10),
|
|
child: ListTile(
|
|
title: Padding(
|
|
padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
|
|
child: Icon(
|
|
MdiIcons.newspaper,
|
|
color: Palette.accent,
|
|
size: 48,
|
|
),
|
|
),
|
|
subtitle: Center(
|
|
child: Padding(
|
|
padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
|
|
child: Text('Schülerzeitung'),
|
|
),
|
|
),
|
|
),
|
|
)),
|
|
)),
|
|
SizedBox(
|
|
width: 175,
|
|
child: GestureDetector(
|
|
onTap: () async {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(builder: (context) => const News()),
|
|
);
|
|
},
|
|
child: Card(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: const Padding(
|
|
padding: EdgeInsets.all(10),
|
|
child: ListTile(
|
|
title: Padding(
|
|
padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
|
|
child: Icon(
|
|
MdiIcons.newspaperVariantOutline,
|
|
color: Palette.accent,
|
|
size: 48,
|
|
),
|
|
),
|
|
subtitle: Center(
|
|
child: Padding(
|
|
padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
|
|
child: Text(
|
|
'Aktuelles',
|
|
),
|
|
),
|
|
),
|
|
),
|
|
)),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(20, 0, 20, 20),
|
|
child: Column(
|
|
children: [
|
|
const ListTile(title: Text("Favorisierte Klassen")),
|
|
FutureBuilder(
|
|
future: getFavClasses(),
|
|
builder: (context, snapshot) {
|
|
if (snapshot.hasData) {
|
|
favClasses = snapshot.data! as List<String>;
|
|
return Wrap(children: [
|
|
...(buildFavClasses(
|
|
context, favClasses, removeFavClass)),
|
|
SizedBox(
|
|
width: 170,
|
|
child: GestureDetector(
|
|
onTap: () async {
|
|
showModalBottomSheet<void>(
|
|
isScrollControlled: true,
|
|
shape: const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.only(
|
|
topLeft: Radius.circular(25.0),
|
|
topRight: Radius.circular(25.0)),
|
|
),
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return SizedBox(
|
|
height: 400,
|
|
child: ListView(
|
|
children: <Widget>[
|
|
ListTile(
|
|
title: const Text(
|
|
"Klasse hinzufügen",
|
|
style: TextStyle(
|
|
fontWeight:
|
|
FontWeight.bold)),
|
|
leading:
|
|
const Icon(Icons.arrow_back),
|
|
onTap: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
),
|
|
FutureBuilder<http.Response>(
|
|
future: fetchClassesList(),
|
|
builder: (context, snapshot) {
|
|
if (snapshot.hasData) {
|
|
if (snapshot
|
|
.data!.statusCode ==
|
|
200) {
|
|
List<Widget> classesList =
|
|
[];
|
|
for (var classNum
|
|
in jsonDecode(snapshot
|
|
.data!.body)) {
|
|
classesList
|
|
.add(ListTile(
|
|
title: Text(classNum),
|
|
onTap: () async {
|
|
SharedPreferences
|
|
prefs =
|
|
await SharedPreferences
|
|
.getInstance();
|
|
if (prefs
|
|
.getStringList(
|
|
"favClasses") !=
|
|
null &&
|
|
prefs
|
|
.getStringList(
|
|
"favClasses")!
|
|
.contains(
|
|
classNum)) {
|
|
const snackBar = SnackBar(
|
|
content: Text(
|
|
'Klasse bereits in den Favoriten'));
|
|
ScaffoldMessenger
|
|
.of(
|
|
context)
|
|
.showSnackBar(
|
|
snackBar);
|
|
} else if (prefs
|
|
.getStringList(
|
|
"favClasses") ==
|
|
null) {
|
|
List<String>
|
|
stringList = [
|
|
classNum
|
|
];
|
|
prefs.setStringList(
|
|
"favClasses",
|
|
stringList);
|
|
} else {
|
|
List<String>
|
|
stringList =
|
|
prefs.getStringList(
|
|
"favClasses")!;
|
|
stringList.add(
|
|
classNum);
|
|
prefs.setStringList(
|
|
"favClasses",
|
|
stringList);
|
|
setState(() {
|
|
favClasses =
|
|
stringList;
|
|
});
|
|
}
|
|
},
|
|
));
|
|
classesList.add(
|
|
const Divider());
|
|
}
|
|
return Column(
|
|
children:
|
|
classesList);
|
|
} else if (snapshot
|
|
.data!.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 (snapshot
|
|
.data!.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")),
|
|
);
|
|
} else {
|
|
return const Center(
|
|
child: Text(
|
|
"Uups... etwas ist schief gelaufen..."));
|
|
}
|
|
} else if (snapshot
|
|
.hasError) {
|
|
return const Center(
|
|
child: Text(
|
|
"Uups... etwas ist schief gelaufen..."));
|
|
} else {
|
|
return const Center(
|
|
child:
|
|
CircularProgressIndicator());
|
|
}
|
|
})
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
child: Card(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: const Padding(
|
|
padding: EdgeInsets.all(6),
|
|
child: ListTile(
|
|
title: Center(child: Text("+")),
|
|
subtitle: Center(
|
|
child: Text(
|
|
'Klasse hinzufügen',
|
|
textScaleFactor: 0.9,
|
|
),
|
|
),
|
|
),
|
|
)),
|
|
),
|
|
)
|
|
]);
|
|
} else if (snapshot.hasError) {
|
|
return const Center(
|
|
child:
|
|
Text("Uups... etwas ist schief gelaufen..."));
|
|
} else {
|
|
return const CircularProgressIndicator();
|
|
}
|
|
}),
|
|
],
|
|
))
|
|
]),
|
|
);
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text("GCG.MeinCantor"),
|
|
centerTitle: true,
|
|
),
|
|
drawer: Drawer(
|
|
child: drawerElements,
|
|
),
|
|
body: LayoutBuilder(builder: (context, constraints) {
|
|
double widgetWidth = constraints.maxWidth;
|
|
|
|
int factor;
|
|
|
|
if (widgetWidth <= 600) {
|
|
factor = 1;
|
|
} else if (widgetWidth <= 1400) {
|
|
factor = 2;
|
|
} else if (widgetWidth <= 2000) {
|
|
factor = 3;
|
|
} else {
|
|
factor = 1;
|
|
}
|
|
|
|
return Center(
|
|
heightFactor: 1,
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: MediaQuery.of(context).size.width / factor,
|
|
),
|
|
child: view),
|
|
);
|
|
}),
|
|
);
|
|
} else if (item.label == "Vertretungsplan") {
|
|
List<Widget> children = <Widget>[
|
|
LayoutBuilder(builder: (context, constraints) {
|
|
double widgetWidth = constraints.maxWidth;
|
|
|
|
int factor;
|
|
|
|
if (widgetWidth <= 600) {
|
|
factor = 1;
|
|
} else if (widgetWidth <= 1400) {
|
|
factor = 2;
|
|
} else if (widgetWidth <= 2000) {
|
|
factor = 3;
|
|
} else {
|
|
factor = 1;
|
|
}
|
|
|
|
return Center(
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: MediaQuery.of(context).size.width / factor,
|
|
),
|
|
child: buildTimetable(
|
|
fetchClassTimetable(
|
|
"/${DateFormat("yyyyMMdd").format(DateTime.now())}",
|
|
null),
|
|
"Vertretungsplan für heute"),
|
|
),
|
|
);
|
|
}),
|
|
LayoutBuilder(builder: (context, constraints) {
|
|
double widgetWidth = constraints.maxWidth;
|
|
|
|
int factor;
|
|
|
|
if (widgetWidth <= 600) {
|
|
factor = 1;
|
|
} else if (widgetWidth <= 1400) {
|
|
factor = 2;
|
|
} else if (widgetWidth <= 2000) {
|
|
factor = 3;
|
|
} else {
|
|
factor = 1;
|
|
}
|
|
|
|
return Center(
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: MediaQuery.of(context).size.width / factor,
|
|
),
|
|
child: buildTimetable(
|
|
fetchClassTimetable(
|
|
"/${DateFormat("yyyyMMdd").format(DateTime.now().add(const Duration(days: 1)))}",
|
|
null),
|
|
"Vertretungsplan für morgen"),
|
|
),
|
|
);
|
|
}),
|
|
LayoutBuilder(builder: (context, constraints) {
|
|
double widgetWidth = constraints.maxWidth;
|
|
|
|
int factor;
|
|
|
|
if (widgetWidth <= 600) {
|
|
factor = 1;
|
|
} else if (widgetWidth <= 1400) {
|
|
factor = 2;
|
|
} else if (widgetWidth <= 2000) {
|
|
factor = 3;
|
|
} else {
|
|
factor = 1;
|
|
}
|
|
|
|
return Center(
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
maxWidth: MediaQuery.of(context).size.width / factor,
|
|
),
|
|
child: buildTimetable(fetchClassTimetable("/latest", null),
|
|
"aktueller Vertretungsplan")),
|
|
);
|
|
})
|
|
];
|
|
return DefaultTabController(
|
|
initialIndex: 0,
|
|
length: 3,
|
|
child: Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text("GCG.MeinCantor"),
|
|
centerTitle: true,
|
|
bottom: const TabBar(
|
|
indicatorColor: Palette.accent,
|
|
enableFeedback: true,
|
|
indicatorPadding: EdgeInsets.all(5),
|
|
indicatorSize: TabBarIndicatorSize.label,
|
|
tabs: <Widget>[
|
|
Tab(
|
|
text: "Heute",
|
|
icon: Icon(Icons.calendar_today_outlined),
|
|
),
|
|
Tab(
|
|
text: "Morgen",
|
|
icon: Icon(MdiIcons.calendarToday),
|
|
),
|
|
Tab(
|
|
text: "Neuster Plan",
|
|
icon: Icon(Icons.calendar_view_day_outlined),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
drawer: Drawer(child: drawerElements),
|
|
body: TabBarView(children: children),
|
|
),
|
|
);
|
|
} else {
|
|
return const Center(child: Text("Derzeit nichts hier..."));
|
|
}
|
|
}
|
|
}
|