This repository has been archived on 2023-06-24. You can view files and clone it, but cannot push or open issues or pull requests.
meincantor-app/lib/dashboard.dart
Denys Konovalov 2dc41b7e24 - added license info
- fixed issues
2022-01-16 15:28:42 +01:00

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..."));
}
}
}