= 0.8.0-dev =
- code cleanup - caching - black-/whitelist - sz/news fixes - added settings options ...
This commit is contained in:
parent
649e226c66
commit
e86c1bad1d
@ -44,7 +44,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId "de.cantorgymnasium.meincantor"
|
applicationId "de.cantorgymnasium.meincantor"
|
||||||
minSdkVersion 18
|
minSdkVersion 20
|
||||||
targetSdkVersion 30
|
targetSdkVersion 30
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
23
android/app/proguard-rules.pro
vendored
Normal file
23
android/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
## Gson rules
|
||||||
|
# Gson uses generic type information stored in a class file when working with fields. Proguard
|
||||||
|
# removes such information by default, so configure it to keep all of it.
|
||||||
|
-keepattributes Signature
|
||||||
|
|
||||||
|
# For using GSON @Expose annotation
|
||||||
|
-keepattributes *Annotation*
|
||||||
|
|
||||||
|
# Gson specific classes
|
||||||
|
-dontwarn sun.misc.**
|
||||||
|
#-keep class com.google.gson.stream.** { *; }
|
||||||
|
|
||||||
|
# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
|
||||||
|
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
|
||||||
|
-keep class * extends com.google.gson.TypeAdapter
|
||||||
|
-keep class * implements com.google.gson.TypeAdapterFactory
|
||||||
|
-keep class * implements com.google.gson.JsonSerializer
|
||||||
|
-keep class * implements com.google.gson.JsonDeserializer
|
||||||
|
|
||||||
|
# Prevent R8 from leaving Data object members always null
|
||||||
|
-keepclassmembers,allowobfuscation class * {
|
||||||
|
@com.google.gson.annotations.SerializedName <fields>;
|
||||||
|
}
|
@ -10,6 +10,8 @@
|
|||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
android:showWhenLocked="true"
|
||||||
|
android:turnScreenOn="true"
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
while the Flutter UI initializes. After that, this theme continues
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
@ -23,10 +25,10 @@
|
|||||||
screen fades out. A splash screen is useful to avoid any visual
|
screen fades out. A splash screen is useful to avoid any visual
|
||||||
gap between the end of Android's launch screen and the painting of
|
gap between the end of Android's launch screen and the painting of
|
||||||
Flutter's first frame. -->
|
Flutter's first frame. -->
|
||||||
<meta-data
|
<!--meta-data
|
||||||
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
<android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||||
android:resource="@drawable/launch_background"
|
android:resource="@drawable/launch_background"
|
||||||
/>
|
/>-->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
BIN
android/app/src/main/res/drawable-hdpi/app_icon.png
Normal file
BIN
android/app/src/main/res/drawable-hdpi/app_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
BIN
android/app/src/main/res/drawable-mdpi/app_icon.png
Normal file
BIN
android/app/src/main/res/drawable-mdpi/app_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/app_icon.png
Normal file
BIN
android/app/src/main/res/drawable-xhdpi/app_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
BIN
android/app/src/main/res/drawable-xxhdpi/app_icon.png
Normal file
BIN
android/app/src/main/res/drawable-xxhdpi/app_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.3 KiB |
BIN
android/app/src/main/res/drawable-xxxhdpi/app_icon.png
Normal file
BIN
android/app/src/main/res/drawable-xxxhdpi/app_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.8 KiB |
3
android/app/src/main/res/raw/keep.xml
Normal file
3
android/app/src/main/res/raw/keep.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:keep="@drawable/*" />
|
@ -7,6 +7,10 @@ import Flutter
|
|||||||
_ application: UIApplication,
|
_ application: UIApplication,
|
||||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
|
if #available(iOS 10.0, *) {
|
||||||
|
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
|
||||||
|
}
|
||||||
|
|
||||||
GeneratedPluginRegistrant.register(with: self)
|
GeneratedPluginRegistrant.register(with: self)
|
||||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,7 @@ class DevSettings extends StatelessWidget {
|
|||||||
content:
|
content:
|
||||||
Text('Neuer API-Schlüssel gesetzt: $apiKey'));
|
Text('Neuer API-Schlüssel gesetzt: $apiKey'));
|
||||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||||
}
|
})),
|
||||||
)
|
|
||||||
),
|
|
||||||
const Divider(),
|
const Divider(),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
|
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
|
||||||
@ -51,7 +49,6 @@ class DevSettings extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:MeinCantor/const.dart';
|
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||||
|
import 'package:meincantor/const.dart';
|
||||||
|
import 'package:meincantor/main.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class InfoSettings extends StatelessWidget {
|
class InfoSettings extends StatelessWidget {
|
||||||
const InfoSettings({Key? key}) : super(key: key);
|
const InfoSettings({Key? key}) : super(key: key);
|
||||||
@ -19,9 +24,31 @@ class InfoSettings extends StatelessWidget {
|
|||||||
leading: Icon(Icons.info_outlined),
|
leading: Icon(Icons.info_outlined),
|
||||||
title: Text("Version"),
|
title: Text("Version"),
|
||||||
subtitle: Text(version)),
|
subtitle: Text(version)),
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.person_outlined),
|
||||||
|
title: Text("Autor"),
|
||||||
|
subtitle: Text(author),
|
||||||
|
onTap: () => launch("https://git.cantorgymnasium.de/denyskon"),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.source_outlined),
|
||||||
|
title: const Text("Quellcode"),
|
||||||
|
subtitle: Linkify(
|
||||||
|
onOpen: (link) async {
|
||||||
|
if (await canLaunch(link.url)) {
|
||||||
|
await launch(link.url);
|
||||||
|
} else {
|
||||||
|
throw 'Could not launch $link';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text: "https://git.cantorgymnasium.de/cantortechnik/meincantor-app",
|
||||||
|
linkStyle: const TextStyle(color: Palette.accent),
|
||||||
|
),
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.settings_backup_restore_outlined),
|
leading: const Icon(Icons.settings_backup_restore_outlined),
|
||||||
title: const Text("Änderungsverlauf"),
|
title: const Text("Änderungsverlauf"),
|
||||||
|
subtitle: const Text("Was ist neu?"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showModalBottomSheet<void>(
|
showModalBottomSheet<void>(
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
@ -30,14 +57,13 @@ class InfoSettings extends StatelessWidget {
|
|||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 400,
|
height: 400,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
AppBar(
|
AppBar(
|
||||||
title: const Text("Änderungsverlauf"),
|
title: const Text("Änderungsverlauf"),
|
||||||
),
|
),
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.all(10),
|
padding: EdgeInsets.all(10),
|
||||||
child: Text(""),
|
child: Text("1.0 --\nErste Release-Version!"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,51 +1,20 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:MeinCantor/main.dart';
|
import 'package:meincantor/main.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cyclop/cyclop.dart';
|
import 'package:cyclop/cyclop.dart';
|
||||||
|
|
||||||
import 'package:MeinCantor/networking.dart';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
import 'package:MeinCantor/presets/colors.dart';
|
import 'package:meincantor/presets/colors.dart';
|
||||||
import 'package:MeinCantor/presets/subjects.dart';
|
import 'package:meincantor/presets/subjects.dart';
|
||||||
|
|
||||||
import 'package:MeinCantor/presets/teachers.dart';
|
import 'package:meincantor/presets/teachers.dart';
|
||||||
|
|
||||||
class PlanSettings extends StatefulWidget {
|
class PlanSettings extends StatelessWidget {
|
||||||
const PlanSettings({Key? key}) : super(key: key);
|
const PlanSettings({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
|
||||||
State<StatefulWidget> createState() => _PlanSettingsState();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Color> buildPlanColors(lesson) async {
|
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
if (!prefs.containsKey("color$lesson")) {
|
|
||||||
prefs.setInt("color$lesson", colors[lesson].value ?? Colors.grey.value);
|
|
||||||
}
|
|
||||||
await fetchLessonList();
|
|
||||||
Color colorDeu = Color(prefs.getInt("color$lesson")!);
|
|
||||||
return colorDeu;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<dynamic>> buildLessonsList() async {
|
|
||||||
await fetchLessonList();
|
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
String lessonsJson = prefs.getString("lessons")!;
|
|
||||||
List<dynamic> lessons = jsonDecode(lessonsJson);
|
|
||||||
return lessons;
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PlanSettingsState extends State<PlanSettings> {
|
|
||||||
Set<Color> swatches = {
|
|
||||||
...Colors.primaries,
|
|
||||||
...Colors.accents,
|
|
||||||
Palette.accent,
|
|
||||||
Palette.primary
|
|
||||||
};
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -56,10 +25,202 @@ class _PlanSettingsState extends State<PlanSettings> {
|
|||||||
body: ListView(
|
body: ListView(
|
||||||
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
|
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
|
||||||
children: [
|
children: [
|
||||||
const ListTile(
|
ListTile(
|
||||||
title: Text("Kurse/Fächer"),
|
leading: const Icon(Icons.list_alt_outlined, color: Colors.red),
|
||||||
leading: Icon(Icons.list_alt_outlined),
|
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
|
||||||
|
title: const Text("Kurse"),
|
||||||
|
subtitle: const Text("Konfiguration der Kurse (Whitelist)"),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const WhitelistSettings()),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
leading:
|
||||||
|
const Icon(Icons.color_lens_outlined, color: Colors.teal),
|
||||||
|
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
|
||||||
|
title: const Text("Farben"),
|
||||||
|
subtitle:
|
||||||
|
const Text("Konfiguration der Farben für die Plankacheln"),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const PlanColorSettings()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WhitelistSettings extends StatefulWidget {
|
||||||
|
const WhitelistSettings({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _WhitelistSettingsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WhitelistSettingsState extends State<WhitelistSettings> {
|
||||||
|
Set<Color> swatches = {
|
||||||
|
...Colors.primaries,
|
||||||
|
...Colors.accents,
|
||||||
|
Palette.accent,
|
||||||
|
Palette.primary
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text("Farben"),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
|
||||||
|
children: [
|
||||||
|
FutureBuilder(
|
||||||
|
future: buildLessonsList(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
List<Widget> children = [];
|
||||||
|
for (var element in (snapshot.data as List<dynamic>)) {
|
||||||
|
String subject = element['subject'];
|
||||||
|
String teacher = element['teacher'];
|
||||||
|
int id = element['id'];
|
||||||
|
children.add(
|
||||||
|
FutureBuilder(
|
||||||
|
future: buildPlanColors(subject),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
Color color = snapshot.data as Color;
|
||||||
|
return FutureBuilder(
|
||||||
|
future: buildBlacklist(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
final _blacklist =
|
||||||
|
snapshot.data! as List<dynamic>;
|
||||||
|
final _blacklisted =
|
||||||
|
_blacklist.contains(id);
|
||||||
|
return ListTile(
|
||||||
|
leading: Checkbox(
|
||||||
|
value:
|
||||||
|
_blacklisted ? false : true,
|
||||||
|
onChanged: (state) async {
|
||||||
|
SharedPreferences prefs =
|
||||||
|
await SharedPreferences
|
||||||
|
.getInstance();
|
||||||
|
setState(() {
|
||||||
|
_blacklisted
|
||||||
|
? _blacklist.remove(id)
|
||||||
|
: _blacklist.add(id);
|
||||||
|
});
|
||||||
|
prefs.setString("blacklist",
|
||||||
|
jsonEncode(_blacklist));
|
||||||
|
},
|
||||||
|
activeColor: color),
|
||||||
|
title: Text(subjects[subject] ?? ""),
|
||||||
|
subtitle:
|
||||||
|
Text(teachers[teacher] ?? ""),
|
||||||
|
onTap: () async {
|
||||||
|
SharedPreferences prefs =
|
||||||
|
await SharedPreferences
|
||||||
|
.getInstance();
|
||||||
|
setState(() {
|
||||||
|
_blacklisted
|
||||||
|
? _blacklist.remove(id)
|
||||||
|
: _blacklist.add(id);
|
||||||
|
});
|
||||||
|
prefs.setString("blacklist",
|
||||||
|
jsonEncode(_blacklist));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const LinearProgressIndicator();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return (const LinearProgressIndicator());
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Column(
|
||||||
|
children: children,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (const Center(child: CircularProgressIndicator()));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PlanColorSettings extends StatefulWidget {
|
||||||
|
const PlanColorSettings({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _PlanColorSettingsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Color> buildPlanColors(String lesson) async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
String key = "color$lesson";
|
||||||
|
if (prefs.containsKey(key) == false) {
|
||||||
|
int colorValue;
|
||||||
|
if (colors.containsKey(lesson)) {
|
||||||
|
colorValue = colors[lesson].value;
|
||||||
|
} else {
|
||||||
|
colorValue = Colors.grey.value;
|
||||||
|
}
|
||||||
|
prefs.setInt(key, colorValue);
|
||||||
|
}
|
||||||
|
//await fetchLessonList();
|
||||||
|
Color color = Color(prefs.getInt(key)!);
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<dynamic>> buildLessonsList() async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
String lessonsJson = prefs.getString("lessons")!;
|
||||||
|
List<dynamic> lessons = jsonDecode(lessonsJson);
|
||||||
|
return lessons;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List> buildBlacklist() async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
if (!prefs.containsKey("blacklist") ||
|
||||||
|
jsonDecode(prefs.getString("blacklist")!).isEmpty) {
|
||||||
|
return <int>[];
|
||||||
|
}
|
||||||
|
String blacklistJson = prefs.getString("blacklist")!;
|
||||||
|
List blacklist = jsonDecode(blacklistJson);
|
||||||
|
return blacklist;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlanColorSettingsState extends State<PlanColorSettings> {
|
||||||
|
Set<Color> swatches = {
|
||||||
|
...Colors.primaries,
|
||||||
|
...Colors.accents,
|
||||||
|
Palette.accent,
|
||||||
|
Palette.primary
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text("Farben"),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
|
||||||
|
children: [
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: buildLessonsList(),
|
future: buildLessonsList(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
|
import 'package:cyclop/cyclop.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:MeinCantor/networking.dart';
|
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||||
|
import 'package:meincantor/Settings/Pages/plan_settings.dart';
|
||||||
|
import 'package:meincantor/networking.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:webview_flutter/webview_flutter.dart';
|
||||||
|
import 'dart:io' show Platform;
|
||||||
|
|
||||||
|
import '../../const.dart';
|
||||||
|
|
||||||
Future<String> getSettingsString(String key) async {
|
Future<String> getSettingsString(String key) async {
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
@ -28,17 +35,39 @@ class UserSettings extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
|
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
|
||||||
child: Container(
|
child: FutureBuilder(
|
||||||
width: 128.0,
|
future: Future.sync(() async {
|
||||||
height: 128.0,
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
decoration: const BoxDecoration(
|
String? user = prefs.getString("user");
|
||||||
shape: BoxShape.circle,
|
if (user == null || user.isEmpty) {
|
||||||
image: DecorationImage(
|
user = "";
|
||||||
fit: BoxFit.scaleDown,
|
}
|
||||||
image:
|
String? name = prefs.getString("name");
|
||||||
AssetImage("assets/images/meincantor_r.png")
|
if (name == null || name.isEmpty) {
|
||||||
)
|
name = "";
|
||||||
)
|
}
|
||||||
|
Map data = {"user": user, "name": name };
|
||||||
|
return data;
|
||||||
|
}),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
// .svg?text=${(snapshot.data! as Map)['name'][0]}
|
||||||
|
String url = "$avatarUrl/${(snapshot.data! as Map)['user']}";
|
||||||
|
return Container(
|
||||||
|
width: 120.0,
|
||||||
|
height: 120.0,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
image: DecorationImage(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
image: NetworkImage(url)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const CircularProgressIndicator();
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
@ -61,8 +90,44 @@ class UserSettings extends StatelessWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(5, 20, 5, 5),
|
padding: const EdgeInsets.fromLTRB(5, 20, 5, 5),
|
||||||
child: buildClassesChooser()),
|
child: buildClassesChooser()),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(MdiIcons.accountSettingsOutline),
|
||||||
|
trailing: const Icon(Icons.link, size: 16),
|
||||||
|
title: const Text("Account-Konsole"),
|
||||||
|
subtitle: const Text("Konto-Einstellungen öffnen"),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => AccountConsole()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AccountConsole extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
AccountConsoleState createState() => AccountConsoleState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class AccountConsoleState extends State<AccountConsole> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
// Enable virtual display.
|
||||||
|
if (Platform.isAndroid) WebView.platform = AndroidWebView();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const WebView(
|
||||||
|
initialUrl: 'https://mein.cantorgymnasium.de/auth/realms/GCG.MeinCantor/account/',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:MeinCantor/Settings/Pages/appearance_settings.dart';
|
import 'package:meincantor/Settings/Pages/appearance_settings.dart';
|
||||||
import 'package:MeinCantor/Settings/Pages/service_settings.dart';
|
import 'package:meincantor/Settings/Pages/service_settings.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||||
|
20
lib/cache_manager.dart
Normal file
20
lib/cache_manager.dart
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
Future<String> getCachedTimetable(String ext) async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
String classNum;
|
||||||
|
if (prefs.getString('class_num') != null) {
|
||||||
|
classNum = prefs.getString('class_num')!.replaceAll("/", "_");
|
||||||
|
} else {
|
||||||
|
classNum = '05_1';
|
||||||
|
}
|
||||||
|
var apiKey = prefs.getString('api_key');
|
||||||
|
var headers = {"x-api-key": "$apiKey"};
|
||||||
|
var file = await DefaultCacheManager().getSingleFile(
|
||||||
|
"https://mein.cantorgymnasium.de/api/timetable/$ext/$classNum",
|
||||||
|
headers: headers);
|
||||||
|
return (utf8.decode(await file.readAsBytes()));
|
||||||
|
}
|
@ -1,19 +1,24 @@
|
|||||||
import 'package:MeinCantor/raumuebersicht.dart';
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:MeinCantor/schulbibliothek.dart';
|
import 'package:meincantor/const.dart';
|
||||||
import 'package:MeinCantor/schulcomputer.dart';
|
import 'package:meincantor/raumuebersicht.dart';
|
||||||
import 'package:MeinCantor/schuelerzeitung.dart';
|
import 'package:meincantor/schulbibliothek.dart';
|
||||||
import 'package:MeinCantor/Settings/dashboard.dart';
|
import 'package:meincantor/schulcomputer.dart';
|
||||||
|
import 'package:meincantor/schuelerzeitung.dart';
|
||||||
|
import 'package:meincantor/Settings/dashboard.dart';
|
||||||
|
|
||||||
import 'package:MeinCantor/main.dart';
|
import 'package:meincantor/main.dart';
|
||||||
import 'package:MeinCantor/networking.dart';
|
import 'package:meincantor/networking.dart';
|
||||||
import 'package:MeinCantor/login.dart';
|
import 'package:meincantor/login.dart';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||||
|
|
||||||
|
import 'news.dart';
|
||||||
|
|
||||||
class Dashboard extends StatefulWidget {
|
class Dashboard extends StatefulWidget {
|
||||||
const Dashboard({Key? key, this.restorationId}) : super(key: key);
|
const Dashboard({Key? key, this.restorationId}) : super(key: key);
|
||||||
|
|
||||||
@ -53,8 +58,39 @@ class _DashboardState extends State<Dashboard> with RestorationMixin {
|
|||||||
UserAccountsDrawerHeader(
|
UserAccountsDrawerHeader(
|
||||||
accountName: buildSettingsString('name', const TextStyle()),
|
accountName: buildSettingsString('name', const TextStyle()),
|
||||||
accountEmail: buildSettingsString('user', const TextStyle()),
|
accountEmail: buildSettingsString('user', const TextStyle()),
|
||||||
currentAccountPicture:
|
currentAccountPicture: FutureBuilder(
|
||||||
Image.asset("assets/images/meincantor_r.png")),
|
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) {
|
||||||
|
// .svg?text=${(snapshot.data! as Map)['name'][0]}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text("Einstellungen"),
|
title: const Text("Einstellungen"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -175,7 +211,7 @@ class _DashboardBottomNavView extends StatelessWidget {
|
|||||||
_timeOfDayToDouble(const TimeOfDay(hour: 15, minute: 10))) {
|
_timeOfDayToDouble(const TimeOfDay(hour: 15, minute: 10))) {
|
||||||
lessonCount = 9;
|
lessonCount = 9;
|
||||||
} else if (_timeOfDayToDouble(TimeOfDay.now()) >
|
} else if (_timeOfDayToDouble(TimeOfDay.now()) >
|
||||||
_timeOfDayToDouble(const TimeOfDay(hour: 15, minute: 10)) &&
|
_timeOfDayToDouble(const TimeOfDay(hour: 15, minute: 10)) &&
|
||||||
_timeOfDayToDouble(TimeOfDay.now()) <=
|
_timeOfDayToDouble(TimeOfDay.now()) <=
|
||||||
_timeOfDayToDouble(const TimeOfDay(hour: 16, minute: 00))) {
|
_timeOfDayToDouble(const TimeOfDay(hour: 16, minute: 00))) {
|
||||||
lessonCount = 10;
|
lessonCount = 10;
|
||||||
@ -271,10 +307,10 @@ class _DashboardBottomNavView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
width: 175,
|
width: 170,
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 175,
|
width: 170,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
@ -310,7 +346,7 @@ class _DashboardBottomNavView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 175,
|
width: 170,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
@ -346,7 +382,7 @@ class _DashboardBottomNavView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 175,
|
width: 170,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
@ -381,6 +417,42 @@ class _DashboardBottomNavView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 170,
|
||||||
|
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',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -439,9 +511,16 @@ class _DashboardBottomNavView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
body: TabBarView(
|
body: TabBarView(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
buildTodayClassTimetable(),
|
buildTimetable(
|
||||||
buildTomorrowClassTimetable(),
|
fetchClassTimetable(
|
||||||
buildClassTimetable(),
|
"/${DateFormat("yyyyMMdd").format(DateTime.now())}"),
|
||||||
|
"Vertretungsplan für heute"),
|
||||||
|
buildTimetable(
|
||||||
|
fetchClassTimetable(
|
||||||
|
"/${DateFormat("yyyyMMdd").format(DateTime.now().add(const Duration(days: 1)))}"),
|
||||||
|
"Vertretungsplan für morgen"),
|
||||||
|
buildTimetable(fetchClassTimetable("/latest"),
|
||||||
|
"aktueller Vertretungsplan")
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -5,14 +5,14 @@
|
|||||||
// ignore_for_file: directives_ordering
|
// 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:shared_preferences_web/shared_preferences_web.dart';
|
import 'package:shared_preferences_web/shared_preferences_web.dart';
|
||||||
|
import 'package:url_launcher_web/url_launcher_web.dart';
|
||||||
|
|
||||||
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
|
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
|
||||||
|
|
||||||
// ignore: public_member_api_docs
|
// ignore: public_member_api_docs
|
||||||
void registerPlugins(Registrar registrar) {
|
void registerPlugins(Registrar registrar) {
|
||||||
FluttertoastWebPlugin.registerWith(registrar);
|
|
||||||
SharedPreferencesPlugin.registerWith(registrar);
|
SharedPreferencesPlugin.registerWith(registrar);
|
||||||
|
UrlLauncherPlugin.registerWith(registrar);
|
||||||
registrar.registerMessageHandler();
|
registrar.registerMessageHandler();
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ Future<bool> checkKey() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Login extends StatelessWidget {
|
class Login extends StatelessWidget {
|
||||||
|
|
||||||
final userController = TextEditingController();
|
final userController = TextEditingController();
|
||||||
final passwordController = TextEditingController();
|
final passwordController = TextEditingController();
|
||||||
final otpController = TextEditingController();
|
final otpController = TextEditingController();
|
||||||
@ -148,11 +149,7 @@ class Login extends StatelessWidget {
|
|||||||
child: const Text("Anmelden"))
|
child: const Text("Anmelden"))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)))));
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,28 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_local_notifications/flutter_local_notifications.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(const App());
|
void main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||||
|
FlutterLocalNotificationsPlugin();
|
||||||
|
// initialise the plugin. app_icon needs to be a added as a drawable resource to the Android head project
|
||||||
|
const AndroidInitializationSettings initializationSettingsAndroid =
|
||||||
|
AndroidInitializationSettings('app_icon');
|
||||||
|
final IOSInitializationSettings initializationSettingsIOS =
|
||||||
|
IOSInitializationSettings();
|
||||||
|
final MacOSInitializationSettings initializationSettingsMacOS =
|
||||||
|
MacOSInitializationSettings();
|
||||||
|
final InitializationSettings initializationSettings = InitializationSettings(
|
||||||
|
android: initializationSettingsAndroid,
|
||||||
|
iOS: initializationSettingsIOS,
|
||||||
|
macOS: initializationSettingsMacOS);
|
||||||
|
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
|
||||||
|
runApp(const App());
|
||||||
|
}
|
||||||
|
|
||||||
class App extends StatelessWidget {
|
class App extends StatelessWidget {
|
||||||
const App({Key? key}) : super(key: key);
|
const App({Key? key}) : super(key: key);
|
||||||
|
@ -1,19 +1,30 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
|
import 'package:meincantor/cache_manager.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/painting.dart';
|
import 'package:flutter/painting.dart';
|
||||||
|
import 'package:flutter_cache_manager/flutter_cache_manager.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:intl/intl.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:MeinCantor/const.dart';
|
import 'package:meincantor/const.dart';
|
||||||
import 'package:MeinCantor/timetable.dart';
|
import 'package:meincantor/timetable.dart';
|
||||||
import 'package:MeinCantor/login.dart';
|
import 'package:meincantor/login.dart';
|
||||||
import 'package:MeinCantor/main.dart';
|
import 'package:meincantor/main.dart';
|
||||||
|
|
||||||
Future<http.Response> getArticles() async {
|
Future<http.Response> getArticles() async {
|
||||||
var uri = Uri.https(szUrl["url"]!, "/articles");
|
var uri = Uri.https(szUrl["url"]!, "/articles");
|
||||||
final response = await http.get(uri);
|
final response = await http.get(uri);
|
||||||
return (response);
|
return (response);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<http.Response> getNews() async {
|
||||||
|
var uri = Uri.https(szUrl["url"]!, "/aktuelles");
|
||||||
|
final response = await http.get(uri);
|
||||||
|
return (response);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<http.Response> getToken(
|
Future<http.Response> getToken(
|
||||||
@ -40,7 +51,28 @@ Future<String> getUserInfo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<http.Response> fetchClassTimetable(String ext) async {
|
Future<http.Response> fetchClassTimetable(String ext) async {
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||||
|
const AndroidNotificationDetails androidPlatformChannelSpecifics =
|
||||||
|
AndroidNotificationDetails('de.cantorgymnasium.meincantor', 'GCG.MeinCantor',
|
||||||
|
channelDescription: '',
|
||||||
|
importance: Importance.max,
|
||||||
|
priority: Priority.high,
|
||||||
|
ticker: 'ticker');
|
||||||
|
const NotificationDetails platformChannelSpecifics =
|
||||||
|
NotificationDetails(android: androidPlatformChannelSpecifics);
|
||||||
|
await flutterLocalNotificationsPlugin.show(
|
||||||
|
0, 'Neuer Vertretungsplan geladen!', 'Du hast folgende Vertretungen:\nSt. 8 Deutsch Frau Rinke, Raum 203\nSt. 4 Biologie Frau Borchert, Raum 107', platformChannelSpecifics,
|
||||||
|
payload: 'item x');
|
||||||
|
try {
|
||||||
|
return (http.Response(await getCachedTimetable(ext), 200));
|
||||||
|
} on HttpExceptionWithStatus catch (e) {
|
||||||
|
return http.Response(e.message, e.statusCode);
|
||||||
|
} on HttpException catch (e) {
|
||||||
|
return http.Response(e.message, 500);
|
||||||
|
} on SocketException catch (e) {
|
||||||
|
return http.Response(e.message, 404);
|
||||||
|
}
|
||||||
|
/*SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
String classNum;
|
String classNum;
|
||||||
if (prefs.getString('class_num') != null) {
|
if (prefs.getString('class_num') != null) {
|
||||||
classNum = prefs.getString('class_num')!.replaceAll("/", "_");
|
classNum = prefs.getString('class_num')!.replaceAll("/", "_");
|
||||||
@ -53,45 +85,9 @@ Future<http.Response> fetchClassTimetable(String ext) async {
|
|||||||
final response = http.get(uri, headers: headers).onError((error, stackTrace) {
|
final response = http.get(uri, headers: headers).onError((error, stackTrace) {
|
||||||
return (http.Response("", 404));
|
return (http.Response("", 404));
|
||||||
});
|
});
|
||||||
return response;
|
return response;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Future<http.Response> fetchTodayClassTimetable() async {
|
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
String classNum;
|
|
||||||
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"};
|
|
||||||
final response = http.get(uri, headers: headers).onError((error, stackTrace) {
|
|
||||||
return (http.Response("", 404));
|
|
||||||
});
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<http.Response> fetchTomorrowClassTimetable() async {
|
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
String classNum;
|
|
||||||
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/tomorrow");
|
|
||||||
var headers = {"x-api-key": "$apiKey"};
|
|
||||||
final response = http.get(uri, headers: headers).onError((error, stackTrace) {
|
|
||||||
return (http.Response("", 404));
|
|
||||||
});
|
|
||||||
return response;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
fetchLessonList() async {
|
fetchLessonList() async {
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
String classNum;
|
String classNum;
|
||||||
@ -114,18 +110,6 @@ fetchLessonList() async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildClassTimetable() {
|
|
||||||
return buildTimetable(fetchClassTimetable(""), "aktueller Vertretungsplan");
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildTodayClassTimetable() {
|
|
||||||
return buildTimetable(fetchClassTimetable("/today"), "Vertretungsplan für heute");
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildTomorrowClassTimetable() {
|
|
||||||
return buildTimetable(fetchClassTimetable("/tomorrow"), "Vertretungsplan für morgen");
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildTimetable(Future<http.Response> future, String info) {
|
Widget buildTimetable(Future<http.Response> future, String info) {
|
||||||
return FutureBuilder<http.Response>(
|
return FutureBuilder<http.Response>(
|
||||||
future: future,
|
future: future,
|
||||||
@ -134,7 +118,7 @@ Widget buildTimetable(Future<http.Response> future, String info) {
|
|||||||
int statusCode = snapshot.data!.statusCode;
|
int statusCode = snapshot.data!.statusCode;
|
||||||
if (statusCode == 200) {
|
if (statusCode == 200) {
|
||||||
Widget timetableView = ClassTimetableBuilder.buildView(
|
Widget timetableView = ClassTimetableBuilder.buildView(
|
||||||
jsonDecode(utf8.decode(snapshot.data!.bodyBytes)), context)
|
jsonDecode(snapshot.data!.body), context)
|
||||||
.view
|
.view
|
||||||
.child;
|
.child;
|
||||||
return timetableView;
|
return timetableView;
|
||||||
@ -142,8 +126,7 @@ Widget buildTimetable(Future<http.Response> future, String info) {
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context, MaterialPageRoute(builder: (context) => Login()));
|
context, MaterialPageRoute(builder: (context) => Login()));
|
||||||
} else if (statusCode == 500) {
|
} else if (statusCode == 500) {
|
||||||
var chars = Runes(
|
var chars = Runes('Es konnte kein $info gefunden werden. \u{1F937}');
|
||||||
'Es konnte kein $info gefunden werden. \u{1F937}');
|
|
||||||
List<Widget> cardChildren = [];
|
List<Widget> cardChildren = [];
|
||||||
cardChildren.add(ListTile(
|
cardChildren.add(ListTile(
|
||||||
title: Text(String.fromCharCodes(chars),
|
title: Text(String.fromCharCodes(chars),
|
||||||
@ -182,8 +165,7 @@ Widget buildTimetable(Future<http.Response> future, String info) {
|
|||||||
padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
|
padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [card],
|
children: [card],
|
||||||
)
|
));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return Center(child: Text('Error $statusCode'));
|
return Center(child: Text('Error $statusCode'));
|
||||||
} else if (snapshot.hasError) {
|
} else if (snapshot.hasError) {
|
||||||
@ -197,13 +179,14 @@ Widget buildTimetable(Future<http.Response> future, String info) {
|
|||||||
|
|
||||||
Widget buildTodayClassTimetableLesson(int count) {
|
Widget buildTodayClassTimetableLesson(int count) {
|
||||||
return FutureBuilder<http.Response>(
|
return FutureBuilder<http.Response>(
|
||||||
future: fetchClassTimetable("/today"),
|
future: fetchClassTimetable(
|
||||||
|
"/${DateFormat("yyyyMMdd").format(DateTime.now())}"),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
int statusCode = snapshot.data!.statusCode;
|
int statusCode = snapshot.data!.statusCode;
|
||||||
if (statusCode == 200) {
|
if (statusCode == 200) {
|
||||||
List<Widget> lessons = LessonsListBuilder.buildList(
|
List<Widget> lessons = LessonsListBuilder.buildList(
|
||||||
jsonDecode(utf8.decode(snapshot.data!.bodyBytes)),
|
jsonDecode(snapshot.data!.body),
|
||||||
count: count)
|
count: count)
|
||||||
.lessons;
|
.lessons;
|
||||||
if (lessons.isNotEmpty) {
|
if (lessons.isNotEmpty) {
|
||||||
@ -223,7 +206,7 @@ Widget buildTodayClassTimetableLesson(int count) {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: cardChildren,
|
children: cardChildren,
|
||||||
));
|
));
|
||||||
return card;
|
return Column(children: [card]);
|
||||||
}
|
}
|
||||||
} else if (statusCode == 400) {
|
} else if (statusCode == 400) {
|
||||||
Future.delayed(Duration.zero, () {
|
Future.delayed(Duration.zero, () {
|
||||||
@ -264,11 +247,8 @@ Widget buildTodayClassTimetableLesson(int count) {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: cardChildren,
|
children: cardChildren,
|
||||||
));
|
));
|
||||||
return Padding(
|
return Column(
|
||||||
padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
|
children: [card],
|
||||||
child: ListView(
|
|
||||||
children: [card],
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Center(child: Text('Error $statusCode'));
|
return Center(child: Text('Error $statusCode'));
|
||||||
|
224
lib/news.dart
Normal file
224
lib/news.dart
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:meincantor/networking.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
Future<List> getNewsRead() async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
String? newsReadString = prefs.getString("newsRead");
|
||||||
|
List<dynamic> newsRead;
|
||||||
|
if (newsReadString == null ||
|
||||||
|
(jsonDecode(newsReadString) as List<dynamic>).isEmpty) {
|
||||||
|
newsRead = [];
|
||||||
|
} else {
|
||||||
|
newsRead = jsonDecode(newsReadString) as List<dynamic>;
|
||||||
|
}
|
||||||
|
return newsRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
class News extends StatefulWidget {
|
||||||
|
const News({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _NewsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NewsState extends State<News> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text("Aktuelles"),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: FutureBuilder<http.Response>(
|
||||||
|
future: getNews(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
int statusCode = snapshot.data!.statusCode;
|
||||||
|
if (statusCode == 200) {
|
||||||
|
String data = utf8.decode(snapshot.data!.bodyBytes);
|
||||||
|
List articles = jsonDecode(data);
|
||||||
|
List<Widget> articleTiles = [];
|
||||||
|
for (var element in articles) {
|
||||||
|
Color color = Colors.white70;
|
||||||
|
Widget card = FutureBuilder(
|
||||||
|
future: getNewsRead(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
List<dynamic> readList = snapshot.data! as List<dynamic>;
|
||||||
|
if (!readList.contains(element["id"])) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
SharedPreferences prefs =
|
||||||
|
await SharedPreferences.getInstance();
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => Article.fromData(
|
||||||
|
element["title"],
|
||||||
|
element["content"],
|
||||||
|
element["author"],
|
||||||
|
element["published_at"])
|
||||||
|
.widget),
|
||||||
|
);
|
||||||
|
readList.add(element["id"]);
|
||||||
|
prefs.setString("newsRead", jsonEncode(readList));
|
||||||
|
setState(() {
|
||||||
|
color = Colors.transparent;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Card(
|
||||||
|
color: color,
|
||||||
|
child: Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.fromLTRB(10, 10, 10, 10),
|
||||||
|
child: FutureBuilder(
|
||||||
|
future: Future.delayed(
|
||||||
|
const Duration(seconds: 0)),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (element["summary"] != null &&
|
||||||
|
(element["summary"] as String)
|
||||||
|
.isNotEmpty) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(element["title"],
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
subtitle: Text(
|
||||||
|
element["summary"],
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 2,
|
||||||
|
softWrap: true,
|
||||||
|
),
|
||||||
|
trailing: Text(
|
||||||
|
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(element["title"],
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
trailing: Text(
|
||||||
|
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => Article.fromData(
|
||||||
|
element["title"],
|
||||||
|
element["content"],
|
||||||
|
element["author"],
|
||||||
|
element["published_at"])
|
||||||
|
.widget),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Card(
|
||||||
|
child: Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.fromLTRB(10, 10, 10, 10),
|
||||||
|
child: FutureBuilder(
|
||||||
|
future: Future.delayed(
|
||||||
|
const Duration(seconds: 0)),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (element["summary"] != null &&
|
||||||
|
(element["summary"] as String)
|
||||||
|
.isNotEmpty) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(element["title"],
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
subtitle: Text(
|
||||||
|
element["summary"],
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 2,
|
||||||
|
softWrap: true,
|
||||||
|
),
|
||||||
|
trailing: Text(
|
||||||
|
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(element["title"],
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold)
|
||||||
|
),
|
||||||
|
trailing: Text(
|
||||||
|
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return const LinearProgressIndicator();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
articleTiles.add(card);
|
||||||
|
}
|
||||||
|
return ListView(
|
||||||
|
children: articleTiles.reversed.toList(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (const Center(
|
||||||
|
child: Text("Uups... Irgendwas ist schief gelaufen")));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return (const Center(child: CircularProgressIndicator()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Article {
|
||||||
|
Widget widget;
|
||||||
|
//const Article({Key? key}) : super(key: key);
|
||||||
|
Article({required this.widget});
|
||||||
|
factory Article.fromData(
|
||||||
|
String title, String content, String author, String publishDate) {
|
||||||
|
return Article(
|
||||||
|
widget: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal, child: Text(title)),
|
||||||
|
),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: const EdgeInsets.fromLTRB(15, 15, 15, 15),
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(MdiIcons.accountOutline),
|
||||||
|
title: Text(author),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(MdiIcons.calendarOutline),
|
||||||
|
title: Text(
|
||||||
|
"${DateTime.parse(publishDate).day.toString()}.${DateTime.parse(publishDate).month.toString()}.${DateTime.parse(publishDate).year.toString()}"),
|
||||||
|
),
|
||||||
|
MarkdownBody(data: content)
|
||||||
|
],
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
1
lib/notifications.dart
Normal file
1
lib/notifications.dart
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
dynamic colors = {
|
Map colors = {
|
||||||
'Bio': Colors.green,
|
'Bio': Colors.green,
|
||||||
'Mat': Colors.indigo,
|
'Mat': Colors.indigo,
|
||||||
'matL1': Colors.indigo,
|
'matL1': Colors.indigo,
|
||||||
|
@ -1,72 +1,190 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:meincantor/networking.dart';
|
||||||
import 'package:MeinCantor/networking.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class SZ extends StatelessWidget {
|
Future<List> getSZread() async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
String? szReadString = prefs.getString("SZread");
|
||||||
|
List<dynamic> szRead;
|
||||||
|
if (szReadString == null ||
|
||||||
|
(jsonDecode(szReadString) as List<dynamic>).isEmpty) {
|
||||||
|
szRead = [];
|
||||||
|
} else {
|
||||||
|
szRead = jsonDecode(szReadString) as List<dynamic>;
|
||||||
|
}
|
||||||
|
return szRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SZ extends StatefulWidget {
|
||||||
const SZ({Key? key}) : super(key: key);
|
const SZ({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _SZState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SZState extends State<SZ> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text("Schülerzeitung"),
|
title: const Text("Schülerzeitung"),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
),
|
),
|
||||||
body: FutureBuilder<http.Response>(
|
body: FutureBuilder<http.Response>(
|
||||||
future: getArticles(),
|
future: getArticles(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
int statusCode = snapshot.data!.statusCode;
|
int statusCode = snapshot.data!.statusCode;
|
||||||
if (statusCode == 200) {
|
if (statusCode == 200) {
|
||||||
String data = utf8.decode(snapshot.data!.bodyBytes);
|
String data = utf8.decode(snapshot.data!.bodyBytes);
|
||||||
List articles = jsonDecode(data);
|
List articles = jsonDecode(data);
|
||||||
List<Widget> articleTiles = [];
|
List<Widget> articleTiles = [];
|
||||||
for (var element in articles) {
|
for (var element in articles) {
|
||||||
Card card = Card(
|
Color color = Colors.white70;
|
||||||
child: Column(children: [
|
Widget card = FutureBuilder(
|
||||||
Padding(
|
future: getSZread(),
|
||||||
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
|
builder: (context, snapshot) {
|
||||||
child: ListTile(
|
if (snapshot.hasData) {
|
||||||
onTap: () {
|
List<dynamic> readList = snapshot.data! as List<dynamic>;
|
||||||
Navigator.push(
|
if (!readList.contains(element["id"])) {
|
||||||
context,
|
return GestureDetector(
|
||||||
MaterialPageRoute(builder: (context) => Article.fromData(element["title"], element["content"], element["author"], element["published_at"]).widget),
|
onTap: () async {
|
||||||
);
|
SharedPreferences prefs =
|
||||||
},
|
await SharedPreferences.getInstance();
|
||||||
title: Text(element["title"],
|
Navigator.push(
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold)),
|
context,
|
||||||
subtitle: Text(
|
MaterialPageRoute(
|
||||||
element["summary"])),
|
builder: (context) => Article.fromData(
|
||||||
)
|
element["title"],
|
||||||
]),
|
element["content"],
|
||||||
shape: RoundedRectangleBorder(
|
element["author"],
|
||||||
borderRadius: BorderRadius.circular(15),
|
element["published_at"])
|
||||||
),
|
.widget),
|
||||||
);
|
);
|
||||||
articleTiles.add(card);
|
readList.add(element["id"]);
|
||||||
}
|
prefs.setString("SZread", jsonEncode(readList));
|
||||||
return ListView(
|
setState(() {
|
||||||
children: articleTiles.reversed.toList(),
|
color = Colors.transparent;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Card(
|
||||||
|
color: color,
|
||||||
|
child: Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.fromLTRB(10, 10, 10, 10),
|
||||||
|
child: FutureBuilder(
|
||||||
|
future: Future.delayed(
|
||||||
|
const Duration(seconds: 0)),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (element["summary"] != null &&
|
||||||
|
(element["summary"] as String)
|
||||||
|
.isNotEmpty) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(element["title"],
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
subtitle: Text(
|
||||||
|
element["summary"],
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 2,
|
||||||
|
softWrap: true,
|
||||||
|
),
|
||||||
|
trailing: Text(
|
||||||
|
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(element["title"],
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
trailing: Text(
|
||||||
|
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => Article.fromData(
|
||||||
|
element["title"],
|
||||||
|
element["content"],
|
||||||
|
element["author"],
|
||||||
|
element["published_at"])
|
||||||
|
.widget),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Card(
|
||||||
|
child: Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.fromLTRB(10, 10, 10, 10),
|
||||||
|
child: FutureBuilder(
|
||||||
|
future: Future.delayed(
|
||||||
|
const Duration(seconds: 0)),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (element["summary"] != null &&
|
||||||
|
(element["summary"] as String)
|
||||||
|
.isNotEmpty) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(element["title"],
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
subtitle: Text(
|
||||||
|
element["summary"],
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 2,
|
||||||
|
softWrap: true,
|
||||||
|
),
|
||||||
|
trailing: Text(
|
||||||
|
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(element["title"],
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold)),
|
||||||
|
trailing: Text(
|
||||||
|
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return const LinearProgressIndicator();
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
} else {
|
articleTiles.add(card);
|
||||||
return(const Center(
|
|
||||||
child: Text("Uups... Irgendwas ist schief gelaufen")
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
return ListView(
|
||||||
|
children: articleTiles.reversed.toList(),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return(const Center(
|
return (const Center(
|
||||||
child: CircularProgressIndicator()
|
child: Text("Uups... Irgendwas ist schief gelaufen")));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
},
|
} else {
|
||||||
),
|
return (const Center(child: CircularProgressIndicator()));
|
||||||
);
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,33 +192,32 @@ class Article {
|
|||||||
Widget widget;
|
Widget widget;
|
||||||
//const Article({Key? key}) : super(key: key);
|
//const Article({Key? key}) : super(key: key);
|
||||||
Article({required this.widget});
|
Article({required this.widget});
|
||||||
factory Article.fromData(String title, String content, String author, String publishDate, ) {
|
factory Article.fromData(
|
||||||
return Article(widget: Scaffold(
|
String title, String content, String author, String publishDate) {
|
||||||
appBar: AppBar(
|
return Article(
|
||||||
title: SingleChildScrollView(
|
widget: Scaffold(
|
||||||
scrollDirection: Axis.vertical,
|
appBar: AppBar(
|
||||||
child: SingleChildScrollView(
|
title: SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.vertical,
|
||||||
child: Text(title)
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal, child: Text(title)),
|
||||||
|
),
|
||||||
|
centerTitle: true,
|
||||||
),
|
),
|
||||||
),
|
body: ListView(
|
||||||
centerTitle: true,
|
padding: const EdgeInsets.fromLTRB(15, 15, 15, 15),
|
||||||
),
|
children: [
|
||||||
body: ListView(
|
ListTile(
|
||||||
padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
|
leading: const Icon(MdiIcons.accountOutline),
|
||||||
children: [
|
title: Text(author),
|
||||||
ListTile(
|
),
|
||||||
leading: const Icon(MdiIcons.accountOutline),
|
ListTile(
|
||||||
title: Text(author),
|
leading: const Icon(MdiIcons.calendarOutline),
|
||||||
),
|
title: Text(
|
||||||
ListTile(
|
"${DateTime.parse(publishDate).day.toString()}.${DateTime.parse(publishDate).month.toString()}.${DateTime.parse(publishDate).year.toString()}"),
|
||||||
leading: const Icon(MdiIcons.calendarOutline),
|
),
|
||||||
title: Text("${DateTime.parse(publishDate).day.toString()}.${DateTime.parse(publishDate).month.toString()}.${DateTime.parse(publishDate).year.toString()}"),
|
MarkdownBody(data: content)
|
||||||
),
|
],
|
||||||
MarkdownBody(data: content)
|
)));
|
||||||
],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import 'package:MeinCantor/presets/teachers.dart';
|
import 'package:meincantor/presets/teachers.dart';
|
||||||
import 'package:MeinCantor/presets/subjects.dart';
|
import 'package:meincantor/presets/subjects.dart';
|
||||||
import 'package:MeinCantor/presets/colors.dart';
|
import 'package:meincantor/presets/colors.dart';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
import 'Settings/Pages/plan_settings.dart';
|
||||||
|
|
||||||
class ClassTimetableBuilder {
|
class ClassTimetableBuilder {
|
||||||
final RefreshIndicator view;
|
final RefreshIndicator view;
|
||||||
ClassTimetableBuilder({required this.view});
|
ClassTimetableBuilder({required this.view});
|
||||||
@ -54,15 +56,14 @@ class ClassTimetableBuilder {
|
|||||||
}
|
}
|
||||||
return ClassTimetableBuilder(
|
return ClassTimetableBuilder(
|
||||||
view: RefreshIndicator(
|
view: RefreshIndicator(
|
||||||
onRefresh: () {
|
onRefresh: () {
|
||||||
return Future.delayed(const Duration(seconds: 1));
|
return Future.delayed(const Duration(seconds: 1));
|
||||||
},
|
},
|
||||||
child: ListView(
|
child: ListView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
children: list,
|
children: list,
|
||||||
),
|
),
|
||||||
)
|
));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,22 +105,35 @@ class LessonsListBuilder {
|
|||||||
style: TextStyle(color: element.fontColor))));
|
style: TextStyle(color: element.fontColor))));
|
||||||
}
|
}
|
||||||
Widget card = FutureBuilder(
|
Widget card = FutureBuilder(
|
||||||
future: element.color,
|
future: buildBlacklist(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
return Card(
|
if (!((snapshot.data as List<dynamic>).contains(element.id))) {
|
||||||
shape: RoundedRectangleBorder(
|
return FutureBuilder(
|
||||||
borderRadius: BorderRadius.circular(15.0),
|
future: element.color,
|
||||||
),
|
builder: (context, snapshot) {
|
||||||
color: snapshot.data as Color,
|
if (snapshot.hasData) {
|
||||||
child: Column(
|
return Card(
|
||||||
children: cardChildren,
|
shape: RoundedRectangleBorder(
|
||||||
));
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
} else {
|
),
|
||||||
return (const Center(child: CircularProgressIndicator()));
|
color: snapshot.data as Color,
|
||||||
}
|
child: Column(
|
||||||
},
|
children: cardChildren,
|
||||||
);
|
));
|
||||||
|
} else {
|
||||||
|
return (const Center(
|
||||||
|
child: CircularProgressIndicator()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return const LinearProgressIndicator();
|
||||||
|
}
|
||||||
|
});
|
||||||
children.add(card);
|
children.add(card);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,6 +201,7 @@ class ClassTimetable {
|
|||||||
|
|
||||||
lessons.add(TimetableLesson(
|
lessons.add(TimetableLesson(
|
||||||
value['St'],
|
value['St'],
|
||||||
|
value["Nr"],
|
||||||
subjects[subject] ?? subject.toString(),
|
subjects[subject] ?? subject.toString(),
|
||||||
teachers[teacher] ?? teacher.toString(),
|
teachers[teacher] ?? teacher.toString(),
|
||||||
room.toString(),
|
room.toString(),
|
||||||
@ -202,6 +217,7 @@ class ClassTimetable {
|
|||||||
|
|
||||||
class TimetableLesson {
|
class TimetableLesson {
|
||||||
final int count;
|
final int count;
|
||||||
|
final int id;
|
||||||
final String name;
|
final String name;
|
||||||
final String teacher;
|
final String teacher;
|
||||||
final String room;
|
final String room;
|
||||||
@ -209,6 +225,6 @@ class TimetableLesson {
|
|||||||
final Future<Color> color;
|
final Future<Color> color;
|
||||||
final Color fontColor;
|
final Color fontColor;
|
||||||
final String info;
|
final String info;
|
||||||
const TimetableLesson(this.count, this.name, this.teacher, this.room,
|
const TimetableLesson(this.count, this.id, this.name, this.teacher, this.room,
|
||||||
this.comment, this.color, this.fontColor, this.info);
|
this.comment, this.color, this.fontColor, this.info);
|
||||||
}
|
}
|
||||||
|
14
pubspec.yaml
14
pubspec.yaml
@ -1,4 +1,4 @@
|
|||||||
name: MeinCantor
|
name: meincantor
|
||||||
description: Die Schulplatform für Cantorianer.
|
description: Die Schulplatform für Cantorianer.
|
||||||
|
|
||||||
# The following line prevents the package from being accidentally published to
|
# The following line prevents the package from being accidentally published to
|
||||||
@ -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: 0.7.5-beta1.nightly2021-11-16
|
version: 0.8.0-dev
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.12.0 <3.0.0"
|
sdk: ">=2.12.0 <3.0.0"
|
||||||
@ -37,11 +37,17 @@ dependencies:
|
|||||||
google_fonts: ^2.1.0
|
google_fonts: ^2.1.0
|
||||||
time: ^2.0.0
|
time: ^2.0.0
|
||||||
flutter_launcher_icons: ^0.9.1
|
flutter_launcher_icons: ^0.9.1
|
||||||
fluttertoast: ^8.0.8
|
|
||||||
flutter_colorpicker: ^0.6.0
|
|
||||||
material_design_icons_flutter: ^5.0.5955-rc.1
|
material_design_icons_flutter: ^5.0.5955-rc.1
|
||||||
cyclop: ^0.5.2
|
cyclop: ^0.5.2
|
||||||
flutter_markdown: ^0.6.8
|
flutter_markdown: ^0.6.8
|
||||||
|
flutter_cache_manager: ^3.2.0
|
||||||
|
intl: ^0.17.0
|
||||||
|
url_launcher: ^6.0.17
|
||||||
|
flutter_linkify: ^5.0.2
|
||||||
|
flutter_svg: ^1.0.0
|
||||||
|
webview_flutter: ^3.0.0
|
||||||
|
flutter_local_notifications: ^10.0.0-dev.1
|
||||||
|
background_fetch: ^1.0.3
|
||||||
|
|
||||||
flutter_icons:
|
flutter_icons:
|
||||||
# image_path: "assets/images/icon-128x128.png"
|
# image_path: "assets/images/icon-128x128.png"
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
"short_name": "MeinCantor",
|
"short_name": "MeinCantor",
|
||||||
"start_url": ".",
|
"start_url": ".",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#0175C2",
|
"background_color": "#1a1a37",
|
||||||
"theme_color": "#0175C2",
|
"theme_color": "#ffbc3b",
|
||||||
"description": "Die Schulplatform für Cantorianer.",
|
"description": "Die Schulplatform für Cantorianer.",
|
||||||
"orientation": "portrait-primary",
|
"orientation": "portrait-primary",
|
||||||
"prefer_related_applications": false,
|
"prefer_related_applications": false,
|
||||||
|
Reference in New Issue
Block a user