├── ios
├── Runner
│ ├── Runner-Bridging-Header.h
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ ├── 100.png
│ │ │ ├── 1024.png
│ │ │ ├── 114.png
│ │ │ ├── 120.png
│ │ │ ├── 144.png
│ │ │ ├── 152.png
│ │ │ ├── 167.png
│ │ │ ├── 180.png
│ │ │ ├── 20.png
│ │ │ ├── 29.png
│ │ │ ├── 40.png
│ │ │ ├── 50.png
│ │ │ ├── 57.png
│ │ │ ├── 58.png
│ │ │ ├── 60.png
│ │ │ ├── 72.png
│ │ │ ├── 76.png
│ │ │ ├── 80.png
│ │ │ ├── 87.png
│ │ │ └── Contents.json
│ │ └── LaunchImage.imageset
│ │ │ ├── ic_launcher_foreground.png
│ │ │ ├── ic_launcher_foreground-1.png
│ │ │ ├── ic_launcher_foreground-2.png
│ │ │ ├── README.md
│ │ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── Main.storyboard
│ │ └── LaunchScreen.storyboard
│ └── Info.plist
├── Flutter
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── AppFrameworkInfo.plist
├── Runner.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── .gitignore
├── Podfile.lock
└── Podfile
├── assets
├── img
│ ├── logoApp.png
│ ├── backgroundHome.svg
│ ├── backgroundLogin.svg
│ └── backgroundLoginTablet.svg
└── fonts
│ ├── SFPro-Bold.otf
│ ├── SFPro-Light.otf
│ ├── SFPro-Medium.otf
│ ├── SFPro-Thin.otf
│ ├── SFPro-Regular.otf
│ └── SFPro-Semibold.otf
├── android
├── gradle.properties
├── app
│ ├── src
│ │ ├── main
│ │ │ ├── ic_launcher-playstore.png
│ │ │ ├── res
│ │ │ │ ├── values
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ ├── ic_launcher_round.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ ├── ic_launcher_round.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ ├── ic_launcher_round.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ ├── ic_launcher_round.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ ├── ic_launcher_round.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── mipmap-anydpi-v26
│ │ │ │ │ ├── ic_launcher.xml
│ │ │ │ │ └── ic_launcher_round.xml
│ │ │ │ ├── drawable
│ │ │ │ │ ├── launch_background.xml
│ │ │ │ │ └── ic_launcher_background.xml
│ │ │ │ ├── drawable-v21
│ │ │ │ │ └── launch_background.xml
│ │ │ │ └── values-night
│ │ │ │ │ └── styles.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── dariovarriale
│ │ │ │ │ ├── esse3
│ │ │ │ │ └── MainActivity.kt
│ │ │ │ │ └── esse3_unimore
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ └── build.gradle
├── .gitignore
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── build.gradle
├── analysis_options.yaml
├── .metadata
├── lib
├── utils
│ ├── interfaces
│ │ └── codable.dart
│ └── shared_wrapper.dart
├── models
│ ├── tassa_model.dart
│ ├── altre_info_appello_model.dart
│ ├── esame_model.dart
│ ├── libretto_model.dart
│ ├── auth_credential_model.dart
│ ├── appello_prenotato_model.dart
│ ├── appello_model.dart
│ ├── residenza_studente.dart
│ ├── studente_model.dart
│ ├── riepilogo_esami_studente.dart
│ ├── dati_personali_studente.dart
│ └── status_studente.dart
├── widgets
│ ├── chip_info.dart
│ ├── shimmer_custom.dart
│ ├── info_rich_text.dart
│ ├── box_info.dart
│ ├── login
│ │ ├── login_button.dart
│ │ ├── login_text_field.dart
│ │ └── login_form.dart
│ ├── error_libretto.dart
│ ├── connection_error.dart
│ ├── bottone_pagina_drawer.dart
│ ├── no_exams_widget.dart
│ ├── error_home_data.dart
│ ├── bottone_material_custom.dart
│ ├── loading_bacheca_prenotazioni.dart
│ ├── tasse_home_card.dart
│ ├── home
│ │ ├── animated_avatar.dart
│ │ ├── home_screen_builder.dart
│ │ └── future_home_screen.dart
│ ├── libretto
│ │ ├── badge_libretto.dart
│ │ ├── header_libretto.dart
│ │ ├── tile_materia_libretto.dart
│ │ ├── grafico_libretto.dart
│ │ └── andamento_libretto.dart
│ ├── shimmer_loader.dart
│ ├── ricarica_bacheca_prenotazioni.dart
│ ├── prossimi_appelli_card.dart
│ ├── drawer_header_home.dart
│ ├── reload_appelli_widget.dart
│ ├── card_appello_imminente.dart
│ ├── libretto_home_card.dart
│ ├── future_drawer_header_home.dart
│ ├── drawer_home.dart
│ └── tassa_expansion_tile.dart
├── extensions
│ └── string_extension.dart
├── main.dart
└── screens
│ ├── login_screen.dart
│ ├── info_app_screen.dart
│ ├── home_screen.dart
│ ├── libretto_screen.dart
│ └── bacheca_prenotazioni_screen.dart
├── README.md
├── .gitignore
├── pubspec.yaml
└── LICENSE
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/assets/img/logoApp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/assets/img/logoApp.png
--------------------------------------------------------------------------------
/assets/fonts/SFPro-Bold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/assets/fonts/SFPro-Bold.otf
--------------------------------------------------------------------------------
/assets/fonts/SFPro-Light.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/assets/fonts/SFPro-Light.otf
--------------------------------------------------------------------------------
/assets/fonts/SFPro-Medium.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/assets/fonts/SFPro-Medium.otf
--------------------------------------------------------------------------------
/assets/fonts/SFPro-Thin.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/assets/fonts/SFPro-Thin.otf
--------------------------------------------------------------------------------
/assets/fonts/SFPro-Regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/assets/fonts/SFPro-Regular.otf
--------------------------------------------------------------------------------
/assets/fonts/SFPro-Semibold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/assets/fonts/SFPro-Semibold.otf
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 | android.enableR8=true
5 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/android/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF600B
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/100.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/144.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/152.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/167.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/20.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/50.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/72.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | /key.properties
8 | GeneratedPluginRegistrant.java
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #DBDBDB
4 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/dariovarriale/esse3/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.dariovarriale.esse3
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity()
6 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/ic_launcher_foreground-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/ic_launcher_foreground-1.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/ic_launcher_foreground-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dariowskii/esse3_unimore/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/ic_launcher_foreground-2.png
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/dariovarriale/esse3_unimore/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.dariovarriale.esse3_unimore
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity()
6 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:lint/analysis_options.yaml
2 |
3 | linter:
4 | rules:
5 | prefer_final_fields: false
6 | prefer_const_constructor: true
7 | avoid_classes_with_only_static_members: false
8 | must_be_immutable: false
9 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
7 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision:
8 | channel: unknown
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/utils/interfaces/codable.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | abstract class Codable {
4 | static String get sharedKey => '';
5 |
6 | String encode() => json.encode(toJson());
7 | void decode(String data);
8 |
9 | Map toJson();
10 |
11 | void fromJson(Map json);
12 | }
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/lib/models/tassa_model.dart:
--------------------------------------------------------------------------------
1 | class TassaModel {
2 | final String titolo;
3 | final String descrizione;
4 | final String importo;
5 | final String scadenza;
6 | final StatoPagamento? stato;
7 |
8 | TassaModel({
9 | required this.titolo,
10 | required this.descrizione,
11 | required this.importo,
12 | required this.scadenza,
13 | required this.stato,
14 | });
15 | }
16 |
17 | enum StatoPagamento { pagato, nonPagato, inAttesa }
18 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | -
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | -
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "ic_launcher_foreground.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "filename" : "ic_launcher_foreground-1.png",
10 | "idiom" : "universal",
11 | "scale" : "2x"
12 | },
13 | {
14 | "filename" : "ic_launcher_foreground-2.png",
15 | "idiom" : "universal",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "author" : "xcode",
21 | "version" : 1
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Esse3 Unimore (Client)
2 | Client per la visualizzazione di Esse3 per UniMoRe.
3 |
4 | L'applicazione fornisce un metodo di visualizzazione alternativa al portale Esse3 di Unimore (https://www.esse3.unimore.it/), effettuando dello scraping sul body delle response per manipolare i dati.
5 |
6 | ## Privacy Policy
7 | Esse3 Unimore (Client) è un progetto individuale sviluppato per gli studenti, pertanto è stato reso pubblico e accessibile a chiunque. L'applicazione non raccoglie in nessun modo i dati sensibili inseriti dallo studente, né li trasmette a terzi per fini statistici o commerciali.
8 |
9 | Made with ❤ and Flutter by dariowskii.
10 |
11 | Copyright © 2021 Dario Varriale
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/lib/models/altre_info_appello_model.dart:
--------------------------------------------------------------------------------
1 | class AltreInfoAppelloModel {
2 | AltreInfoAppelloModel({
3 | required this.tipoEsame,
4 | required this.verbalizzazione,
5 | required this.docente,
6 | required this.numIscritti,
7 | required this.aula,
8 | });
9 |
10 | final String tipoEsame;
11 | final String verbalizzazione;
12 | final String docente;
13 | final String numIscritti;
14 | final String aula;
15 | }
16 |
17 | class AltreInfoAppelloWrapper {
18 | final int numHiddens;
19 | final Map hiddens = {};
20 | late AltreInfoAppelloModel altreInfo;
21 |
22 | AltreInfoAppelloWrapper({
23 | required this.numHiddens,
24 | });
25 | }
26 |
--------------------------------------------------------------------------------
/lib/widgets/chip_info.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// Chip utilizzata per le info primarie della [HomeScreen].
4 | class ChipInfo extends StatelessWidget {
5 | /// Testo della chip.
6 | final String? text;
7 |
8 | /// Grandezza del font della chip.
9 | final double textSize;
10 |
11 | const ChipInfo({this.text = 'null', this.textSize = 14});
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Chip(
16 | backgroundColor: Theme.of(context).primaryColor,
17 | label: Text(
18 | text!,
19 | style: TextStyle(
20 | color: Colors.white,
21 | fontSize: textSize,
22 | ),
23 | ),
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.7.20'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.3.1'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/lib/models/esame_model.dart:
--------------------------------------------------------------------------------
1 | class EsameModel {
2 | EsameModel({
3 | required this.nome,
4 | required this.codiceEsame,
5 | required this.dataEsame,
6 | required this.crediti,
7 | required this.voto,
8 | this.altroVoto,
9 | });
10 |
11 | final String nome;
12 | final String codiceEsame;
13 | final String dataEsame;
14 | final String? altroVoto;
15 | final int crediti;
16 | final int voto;
17 |
18 | bool get esameIdoneo => altroVoto != null || voto > 0;
19 |
20 | DateTime get dataEsameDateTime {
21 | final anno = dataEsame.substring(6);
22 | final mese = dataEsame.substring(3, 5);
23 | final giorno = dataEsame.substring(0, 2);
24 | return DateTime.parse("$anno-$mese-$giorno");
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/widgets/shimmer_custom.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:shimmer/shimmer.dart';
3 |
4 | /// Animazione di caricamento custom da [Shimmer].
5 | class ShimmerCustom extends StatelessWidget {
6 | final double height;
7 |
8 | const ShimmerCustom({Key? key, this.height = 200}) : super(key: key);
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return Shimmer.fromColors(
13 | baseColor: Colors.grey[400]!,
14 | highlightColor: Colors.grey[300]!,
15 | child: Container(
16 | width: double.infinity,
17 | height: height,
18 | decoration: BoxDecoration(
19 | color: Colors.black26, borderRadius: BorderRadius.circular(10)),
20 | ),
21 | );
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/extensions/string_extension.dart:
--------------------------------------------------------------------------------
1 | extension StringExtension on String {
2 | String camelCase() {
3 | var result = '';
4 |
5 | for (var i = 0; i < length; i++) {
6 | if (i == 0) {
7 | result += this[i].toUpperCase();
8 | continue;
9 | }
10 |
11 | if (this[i] == ' ') {
12 | i++;
13 | result += ' ';
14 | result += this[i].toUpperCase();
15 | } else {
16 | result += this[i];
17 | }
18 | }
19 |
20 | return result;
21 | }
22 |
23 | String cleanFromEntities() {
24 | var result = this;
25 |
26 | final regex = RegExp('&(.*?);');
27 | for (final match in regex.allMatches(this)) {
28 | result = result.replaceFirst(match.group(0)!, ' ');
29 | }
30 |
31 | return result;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | build/
33 | test/
34 | web/
35 | macos/
36 |
37 | # Web related
38 | lib/generated_plugin_registrant.dart
39 |
40 | # Symbolication related
41 | app.*.symbols
42 |
43 | # Obfuscation related
44 | app.*.map.json
45 |
--------------------------------------------------------------------------------
/lib/widgets/info_rich_text.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class InfoRichText extends StatelessWidget {
4 | const InfoRichText({
5 | Key? key,
6 | required this.text,
7 | required this.value,
8 | this.fontSize = 14,
9 | }) : super(key: key);
10 |
11 | final String text;
12 | final String value;
13 | final double fontSize;
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return RichText(
18 | text: TextSpan(
19 | text: text,
20 | style: DefaultTextStyle.of(context).style,
21 | children: [
22 | TextSpan(
23 | text: value,
24 | style: TextStyle(
25 | fontWeight: FontWeight.bold,
26 | fontSize: fontSize,
27 | ),
28 | )
29 | ],
30 | ),
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/lib/widgets/box_info.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class BoxInfo extends StatelessWidget {
4 | const BoxInfo({
5 | Key? key,
6 | required this.darkModeOn,
7 | required this.child,
8 | this.minWidth = double.maxFinite,
9 | this.backgroundColor,
10 | }) : super(key: key);
11 |
12 | final bool darkModeOn;
13 | final Widget child;
14 | final double? minWidth;
15 | final Color? backgroundColor;
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return Container(
20 | padding: const EdgeInsets.all(10),
21 | width: minWidth,
22 | decoration: BoxDecoration(
23 | color: backgroundColor ??
24 | (darkModeOn ? Colors.white12 : Colors.black12.withOpacity(0.04)),
25 | borderRadius: const BorderRadius.all(Radius.circular(10)),
26 | ),
27 | child: child,
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 9.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/widgets/login/login_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | class LoginButton extends StatelessWidget {
5 | const LoginButton({
6 | Key? key,
7 | required this.onPressed,
8 | }) : super(key: key);
9 | final Function()? onPressed;
10 | @override
11 | Widget build(BuildContext context) {
12 | return MaterialButton(
13 | disabledColor: Constants.buttonDisabled,
14 | onPressed: onPressed,
15 | padding: const EdgeInsets.all(16),
16 | color: Constants.mainColor,
17 | textColor: Colors.white,
18 | disabledTextColor: Colors.black26,
19 | minWidth: double.infinity,
20 | shape: const RoundedRectangleBorder(
21 | borderRadius: BorderRadius.all(Radius.circular(30)),
22 | ),
23 | child: const Text(
24 | 'ACCEDI',
25 | style: Constants.fontBold,
26 | ),
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/models/libretto_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/models/esame_model.dart';
2 |
3 | class LibrettoModel {
4 | final String? mediaAritmetica;
5 | final String? mediaPonderata;
6 | final int esamiTotali;
7 | final List esami = [];
8 |
9 | int _esamiSuperati = 0;
10 | int _cfuTotali = 0;
11 |
12 | double? get mediaAritmeticaDouble {
13 | return double.tryParse(mediaAritmetica!);
14 | }
15 |
16 | double? get mediaPonderataDouble {
17 | return double.tryParse(mediaAritmetica!);
18 | }
19 |
20 | int get esamiSuperati => _esamiSuperati;
21 | int get cfuTotali => _cfuTotali;
22 |
23 | void aggiungiCfuAlTotale({required int cfu}) {
24 | if (cfu > 0) {
25 | _cfuTotali += cfu;
26 | }
27 | }
28 |
29 | void incrementaEsamiSuperati() {
30 | _esamiSuperati++;
31 | }
32 |
33 | LibrettoModel({
34 | required this.mediaAritmetica,
35 | required this.mediaPonderata,
36 | required this.esamiTotali,
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/lib/widgets/error_libretto.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// Widget di errore in caso in cui non si riesca a caricare il libretto
4 | /// in [LibrettoHomeCard].
5 | class ErrorLibretto extends StatelessWidget {
6 | /// Future del libretto da ricaricare.
7 | final Function()? onPressed;
8 |
9 | const ErrorLibretto({Key? key, this.onPressed}) : super(key: key);
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Row(
14 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
15 | children: [
16 | Row(
17 | children: const [
18 | Icon(
19 | Icons.error,
20 | color: Colors.redAccent,
21 | ),
22 | SizedBox(width: 5),
23 | Text(
24 | 'Errore nel recuperare i dati!',
25 | ),
26 | ],
27 | ),
28 | IconButton(
29 | icon: const Icon(Icons.refresh),
30 | onPressed: onPressed,
31 | ),
32 | ],
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/lib/models/auth_credential_model.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:Esse3/utils/interfaces/codable.dart';
4 |
5 | class AuthCredential extends Codable {
6 | static String get sharedKey => 'authCredential';
7 |
8 | String? _username;
9 | String? _password;
10 |
11 | String? get username => _username;
12 | String? get password => _password;
13 |
14 | AuthCredential({
15 | String? username,
16 | String? password,
17 | }) : _username = username,
18 | _password = password;
19 |
20 | @override
21 | void decode(String data) {
22 | final jsonData =
23 | Map.from(json.decode(data) as Map);
24 | _username = jsonData['username'];
25 | _password = jsonData['password'];
26 | }
27 |
28 | @override
29 | Map toJson() => {
30 | 'username': _username,
31 | 'password': _password,
32 | };
33 |
34 | @override
35 | void fromJson(Map json) {
36 | _username = json['username'] as String;
37 | _password = json['password'] as String;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: Esse3
2 | description: Esse3 Unimore
3 |
4 | version: 1.4.2+43
5 |
6 | environment:
7 | sdk: '>=2.12.0 <3.0.0'
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 | http: ^0.13.4
13 | shared_preferences: ^2.0.15
14 | html: ^0.15.0
15 | add_2_calendar: ^2.1.3
16 | url_launcher: ^6.1.4
17 | fl_chart: ^0.55.0
18 | liquid_pull_to_refresh: ^3.0.1
19 | flutter_svg: ^1.1.0
20 | shimmer: ^2.0.0
21 | cupertino_icons: ^1.0.5
22 |
23 | dev_dependencies:
24 | flutter_test:
25 | sdk: flutter
26 | lint: ^1.8.2
27 |
28 | flutter:
29 | uses-material-design: true
30 | assets:
31 | - assets/img/
32 | fonts:
33 | - family: SF Pro
34 | fonts:
35 | - asset: assets/fonts/SFPro-Regular.otf
36 | - asset: assets/fonts/SFPro-Light.otf
37 | weight: 300
38 | - asset: assets/fonts/SFPro-Thin.otf
39 | weight: 100
40 | - asset: assets/fonts/SFPro-Medium.otf
41 | weight: 500
42 | - asset: assets/fonts/SFPro-Semibold.otf
43 | weight: 600
44 | - asset: assets/fonts/SFPro-Bold.otf
45 | weight: 700
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Dario Varriale
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - add_2_calendar (0.0.1):
3 | - Flutter
4 | - Flutter (1.0.0)
5 | - shared_preferences_ios (0.0.1):
6 | - Flutter
7 | - url_launcher_ios (0.0.1):
8 | - Flutter
9 |
10 | DEPENDENCIES:
11 | - add_2_calendar (from `.symlinks/plugins/add_2_calendar/ios`)
12 | - Flutter (from `Flutter`)
13 | - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
14 | - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
15 |
16 | EXTERNAL SOURCES:
17 | add_2_calendar:
18 | :path: ".symlinks/plugins/add_2_calendar/ios"
19 | Flutter:
20 | :path: Flutter
21 | shared_preferences_ios:
22 | :path: ".symlinks/plugins/shared_preferences_ios/ios"
23 | url_launcher_ios:
24 | :path: ".symlinks/plugins/url_launcher_ios/ios"
25 |
26 | SPEC CHECKSUMS:
27 | add_2_calendar: e9d68636aed37fb18e12f5a3d74c2e0589487af0
28 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
29 | shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
30 | url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
31 |
32 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
33 |
34 | COCOAPODS: 1.11.3
35 |
--------------------------------------------------------------------------------
/lib/widgets/connection_error.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_svg/flutter_svg.dart';
4 |
5 | /// Schermata di errore riguardo la connessione.
6 | class ConnectionError extends StatelessWidget {
7 | final double? deviceWidth;
8 | final bool? isTablet;
9 |
10 | const ConnectionError({Key? key, this.deviceWidth, this.isTablet})
11 | : super(key: key);
12 | @override
13 | Widget build(BuildContext context) {
14 | return Container(
15 | padding: const EdgeInsets.all(32.0),
16 | child: Column(
17 | children: [
18 | SvgPicture.asset(
19 | 'assets/img/networkError.svg',
20 | width: isTablet! ? deviceWidth! * 0.5 : deviceWidth! / 1.7,
21 | ),
22 | SizedBox(height: isTablet! ? 30 : 15),
23 | const Text('Errore di connessione', style: Constants.fontBold28),
24 | const SizedBox(height: 5),
25 | const Text(
26 | "Sembra ci siano problemi nel recuperare i tuoi dati, riaggiorna oppure riprova tra un po'!",
27 | style: Constants.font18,
28 | textAlign: TextAlign.center,
29 | )
30 | ],
31 | ),
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/widgets/bottone_pagina_drawer.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | /// Bottone utilizzato in [HomeScreen] nel [Drawer].
5 | class BottonePaginaDrawer extends StatelessWidget {
6 | final String testoBottone;
7 |
8 | /// Icona del bottone, in caso ne abbia una.
9 | final IconData? icona;
10 |
11 | /// Funzione del bottone quando viene premuto.
12 | final Function() onPressed;
13 | final Color? textColor;
14 |
15 | const BottonePaginaDrawer({
16 | Key? key,
17 | required this.testoBottone,
18 | required this.onPressed,
19 | this.textColor,
20 | this.icona,
21 | }) : super(key: key);
22 |
23 | @override
24 | Widget build(BuildContext context) {
25 | return TextButton(
26 | onPressed: onPressed,
27 | child: Row(
28 | mainAxisAlignment: MainAxisAlignment.center,
29 | children: [
30 | if (icona != null) ...[
31 | Icon(icona, color: textColor, size: 15),
32 | const SizedBox(width: 10),
33 | ],
34 | Text(
35 | testoBottone,
36 | textAlign: TextAlign.center,
37 | style: Constants.fontBold.copyWith(color: textColor),
38 | ),
39 | ],
40 | ),
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/widgets/no_exams_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_svg/flutter_svg.dart';
4 |
5 | /// Schermata in cui non ci sono esami in [ProssimiAppelliScreen].
6 | class NoExams extends StatelessWidget {
7 | final double? deviceWidth;
8 | final bool? isTablet;
9 |
10 | const NoExams({Key? key, this.deviceWidth, this.isTablet}) : super(key: key);
11 | @override
12 | Widget build(BuildContext context) {
13 | return Container(
14 | padding: const EdgeInsets.all(32.0),
15 | child: Column(
16 | children: [
17 | SvgPicture.asset(
18 | 'assets/img/party.svg',
19 | width: isTablet! ? deviceWidth! * 0.6 : deviceWidth! / 1.7,
20 | ),
21 | SizedBox(height: isTablet! ? 30 : 15),
22 | const Text('Nessun appello', style: Constants.fontBold28),
23 | const SizedBox(height: 10),
24 | const Text(
25 | 'Sembra non ci siano appelli, fantastico!',
26 | style: Constants.font18,
27 | textAlign: TextAlign.center,
28 | ),
29 | const Text(
30 | 'Adesso scappa finchè sei in tempo',
31 | style: Constants.font14,
32 | textAlign: TextAlign.center,
33 | )
34 | ],
35 | ),
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/widgets/error_home_data.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_svg/flutter_svg.dart';
3 |
4 | /// Schermata di errore per la [HomeScreen] in caso in cui non
5 | /// si riescano a recuperare le informazioni dell'utente.
6 | class ErrorHomeData extends StatelessWidget {
7 | @override
8 | Widget build(BuildContext context) {
9 | return Padding(
10 | padding: const EdgeInsets.all(32.0),
11 | child: Center(
12 | child: Column(
13 | mainAxisAlignment: MainAxisAlignment.center,
14 | children: [
15 | SvgPicture.asset('assets/img/networkError.svg', width: 200),
16 | const SizedBox(
17 | height: 20,
18 | ),
19 | const Text(
20 | 'Oops..',
21 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 32),
22 | ),
23 | const SizedBox(height: 10),
24 | const Text(
25 | 'Ci sono problemi nel recuperare i tuoi dati, aggiorna oppure riprova tra un pò!',
26 | softWrap: true,
27 | textAlign: TextAlign.center,
28 | style: TextStyle(
29 | fontWeight: FontWeight.w500,
30 | fontSize: 20,
31 | ),
32 | )
33 | ],
34 | ),
35 | ),
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/utils/shared_wrapper.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/models/auth_credential_model.dart';
2 | import 'package:shared_preferences/shared_preferences.dart';
3 |
4 | import '../models/studente_model.dart';
5 |
6 | class SharedWrapper {
7 | SharedWrapper._();
8 | static final SharedWrapper shared = SharedWrapper._();
9 |
10 | Future _prefs() async => SharedPreferences.getInstance();
11 |
12 | Future getUserCreditentials() async {
13 | final prefs = await _prefs();
14 | final authCredential = AuthCredential();
15 | final raw = prefs.getString(AuthCredential.sharedKey);
16 | if (raw != null) {
17 | authCredential.decode(raw);
18 | return authCredential;
19 | }
20 | return null;
21 | }
22 |
23 | Future setUserCreditentials(AuthCredential authCredential) async {
24 | final prefs = await _prefs();
25 | prefs.setString(AuthCredential.sharedKey, authCredential.encode());
26 | }
27 |
28 | Future setStudenteModel(StudenteModel studenteModel) async {
29 | final prefs = await _prefs();
30 | prefs.setString(StudenteModel.sharedKey, studenteModel.encode());
31 | }
32 |
33 | Future getStudenteModel() async {
34 | final prefs = await _prefs();
35 | final studenteModel = StudenteModel();
36 | final raw = prefs.getString(StudenteModel.sharedKey);
37 | if (raw != null) {
38 | studenteModel.decode(raw);
39 | return studenteModel;
40 | }
41 | return null;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/assets/img/backgroundHome.svg:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------
/lib/widgets/bottone_material_custom.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// Bottone custom utilizzato in [HomeScreen] nel [Drawer].
4 | class BottoneMaterialCustom extends StatelessWidget {
5 | final Function() onPressed;
6 | final Color textColor, backgroundColor;
7 | final String textButton;
8 | final double minWidth, height, elevation, fontSize, padding;
9 | final FontWeight fontWeight;
10 |
11 | const BottoneMaterialCustom(
12 | {Key? key,
13 | required this.onPressed,
14 | this.backgroundColor = Colors.redAccent,
15 | this.textColor = Colors.white,
16 | required this.textButton,
17 | this.minWidth = double.infinity,
18 | this.height = 40,
19 | this.fontWeight = FontWeight.bold,
20 | this.elevation = 1,
21 | this.fontSize = 14,
22 | this.padding = 0})
23 | : super(key: key);
24 |
25 | @override
26 | Widget build(BuildContext context) {
27 | return MaterialButton(
28 | minWidth: minWidth,
29 | shape: const RoundedRectangleBorder(
30 | borderRadius: BorderRadius.all(Radius.circular(50)),
31 | ),
32 | height: height,
33 | elevation: elevation,
34 | textColor: textColor,
35 | color: backgroundColor,
36 | onPressed: onPressed,
37 | child: Padding(
38 | padding: EdgeInsets.all(padding),
39 | child: Text(
40 | textButton,
41 | style: TextStyle(fontWeight: fontWeight, fontSize: fontSize),
42 | ),
43 | ),
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/assets/img/backgroundLogin.svg:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/lib/models/appello_prenotato_model.dart:
--------------------------------------------------------------------------------
1 | class AppelloPrenotatoModel {
2 | AppelloPrenotatoModel({
3 | required this.nomeMateria,
4 | required this.iscrizione,
5 | required this.tipoEsame,
6 | required this.svolgimento,
7 | required this.dataAppello,
8 | required this.oraAppello,
9 | required this.docenti,
10 | required this.linkCancellazione,
11 | required this.codiceEsame,
12 | });
13 |
14 | final String nomeMateria;
15 | final String codiceEsame;
16 | final String iscrizione;
17 | final String tipoEsame;
18 | final String svolgimento;
19 | final String dataAppello;
20 | final String oraAppello;
21 | final String docenti;
22 | final String? linkCancellazione;
23 |
24 | DateTime get dataAppelloDateTime {
25 | final anno = dataAppello.substring(6);
26 | final mese = dataAppello.substring(3, 5);
27 | final giorno = dataAppello.substring(0, 2);
28 | return DateTime.parse("$anno-$mese-$giorno");
29 | }
30 |
31 | Map get hiddensCancellazione {
32 | final map = {};
33 |
34 | final buffer = linkCancellazione!
35 | .replaceFirst('auth/studente/Appelli/ConfermaCancellaAppello.do?', '');
36 | final list = buffer.split('&');
37 |
38 | for (final String element in list) {
39 | final newBuffer = element.split('=');
40 | map[newBuffer[0]] = newBuffer[1];
41 | }
42 |
43 | return map;
44 | }
45 | }
46 |
47 | class AppelliPrenotatiWrapper {
48 | AppelliPrenotatiWrapper({
49 | required this.appelliTotali,
50 | });
51 |
52 | final int appelliTotali;
53 | final List appelli = [];
54 | }
55 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CADisableMinimumFrameDurationOnPhone
6 |
7 | CFBundleDevelopmentRegion
8 | it_IT
9 | CFBundleDisplayName
10 | Esse3
11 | CFBundleExecutable
12 | $(EXECUTABLE_NAME)
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | esse3_unimore
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | $(MARKETING_VERSION)
23 | CFBundleSignature
24 | ????
25 | CFBundleVersion
26 | $(CURRENT_PROJECT_VERSION)
27 | LSRequiresIPhoneOS
28 |
29 | NSCalendarsUsageDescription
30 | Per aggiungere il promemoria al calendario ho bisogno del tuo permesso.
31 | UILaunchStoryboardName
32 | LaunchScreen
33 | UIMainStoryboardFile
34 | Main
35 | UIRequiresFullScreen
36 |
37 | UISupportedInterfaceOrientations
38 |
39 | UIInterfaceOrientationPortrait
40 |
41 | UISupportedInterfaceOrientations~ipad
42 |
43 | UIInterfaceOrientationPortrait
44 |
45 | UIViewControllerBasedStatusBarAppearance
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/lib/models/appello_model.dart:
--------------------------------------------------------------------------------
1 | class AppelloModel {
2 | AppelloModel({
3 | required this.nomeMateria,
4 | required this.dataAppello,
5 | required this.periodoIscrizione,
6 | required this.descrizione,
7 | required this.sessione,
8 | required this.linkInfo,
9 | });
10 |
11 | final String nomeMateria;
12 | final String dataAppello;
13 | final String periodoIscrizione;
14 | final String descrizione;
15 | final String sessione;
16 | final String? linkInfo;
17 |
18 | DateTime get dataAppelloDateTime {
19 | final anno = dataAppello.substring(6);
20 | final mese = dataAppello.substring(3, 5);
21 | final giorno = dataAppello.substring(0, 2);
22 | return DateTime.parse("$anno-$mese-$giorno");
23 | }
24 | }
25 |
26 | class AppelliWrapper {
27 | AppelliWrapper({
28 | required this.totaleApelli,
29 | });
30 |
31 | final int totaleApelli;
32 | final List appelli = [];
33 | final List _appelliImminenti = [];
34 |
35 | List get appelliImminenti {
36 | if (_appelliImminenti.isNotEmpty) {
37 | return _appelliImminenti;
38 | }
39 |
40 | final appelliImminenti = [];
41 | final dataOggi = DateTime.now();
42 |
43 | for (var i = 0; i < totaleApelli; i++) {
44 | final appello = appelli[i];
45 | final diffTempo = appello.dataAppelloDateTime.difference(dataOggi);
46 | if (diffTempo.inDays <= 20) {
47 | appelliImminenti.add(appello);
48 | }
49 | }
50 |
51 | appelliImminenti
52 | .sort((a, b) => a.dataAppelloDateTime.compareTo(b.dataAppelloDateTime));
53 |
54 | _appelliImminenti.addAll(appelliImminenti);
55 | return appelliImminenti;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
16 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
31 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/lib/widgets/loading_bacheca_prenotazioni.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | class LoadingBachecaPrenotazioni extends StatelessWidget {
5 | const LoadingBachecaPrenotazioni({
6 | Key? key,
7 | required this.darkModeOn,
8 | }) : assert(darkModeOn != null),
9 | super(key: key);
10 |
11 | final bool darkModeOn;
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Padding(
16 | padding: const EdgeInsets.all(16.0),
17 | child: Column(
18 | crossAxisAlignment: CrossAxisAlignment.start,
19 | children: [
20 | const Text(
21 | 'Bacheca prenotazioni',
22 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25),
23 | ),
24 | const Divider(),
25 | Flexible(
26 | child: SizedBox(
27 | width: double.maxFinite,
28 | child: Column(
29 | mainAxisAlignment: MainAxisAlignment.center,
30 | children: [
31 | const CircularProgressIndicator(
32 | valueColor: AlwaysStoppedAnimation(
33 | Constants.mainColorLighter),
34 | ),
35 | const SizedBox(height: 15),
36 | Text(
37 | 'Sto scaricando i dati...',
38 | style: Constants.font16.copyWith(
39 | color: darkModeOn
40 | ? Colors.white
41 | : Constants.mainColorLighter),
42 | )
43 | ],
44 | ),
45 | ),
46 | )
47 | ],
48 | ),
49 | );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/lib/models/residenza_studente.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:Esse3/extensions/string_extension.dart';
4 | import 'package:Esse3/utils/interfaces/codable.dart';
5 | import 'package:html/dom.dart';
6 | import 'package:html/parser.dart';
7 |
8 | class ResidenzaStudente extends Codable {
9 | static String get sharedKey => 'residenzaStudente';
10 |
11 | String? _indirizzo;
12 | String? _citta;
13 | String? _telefono;
14 |
15 | String? get indirizzo => _indirizzo;
16 | String? get citta => _citta;
17 | String? get telefono => _telefono;
18 |
19 | @override
20 | void decode(String data) {
21 | final jsonData =
22 | Map.from(json.decode(data) as Map);
23 | _indirizzo = jsonData['indirizzo'];
24 | _citta = jsonData['citta'];
25 | _telefono = jsonData['telefono'];
26 | }
27 |
28 | @override
29 | Map toJson() => {
30 | 'indirizzo': _indirizzo,
31 | 'citta': _citta,
32 | 'telefono': _telefono,
33 | };
34 |
35 | @override
36 | void fromJson(Map json) {
37 | _indirizzo = json['indirizzo'] as String;
38 | _citta = json['citta'] as String;
39 | _telefono = json['telefono'] as String;
40 | }
41 |
42 | void fromHtmlElement({required Element residenza}) {
43 | final tmp = residenza.innerHtml.split('
');
44 | if (tmp.length <= 2) {
45 | return;
46 | }
47 |
48 | final indirizzo = parseFragment(tmp[0]).text?.cleanFromEntities();
49 | final capCitta = parseFragment(tmp[1]).text?.cleanFromEntities();
50 | final tel = parseFragment(tmp[2]).text?.cleanFromEntities();
51 |
52 | if (indirizzo != null) {
53 | _indirizzo = indirizzo;
54 | }
55 | if (capCitta != null) {
56 | _citta = capCitta;
57 | }
58 | if (tel != null) {
59 | _telefono = tel;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib/widgets/login/login_text_field.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | class LoginTextField extends StatelessWidget {
5 | const LoginTextField({
6 | Key? key,
7 | required this.enabled,
8 | required this.onSubmitted,
9 | required this.labelText,
10 | this.focusNode,
11 | this.obscureText = false,
12 | this.controller,
13 | this.maxLenght,
14 | this.keyboardType,
15 | }) : super(key: key);
16 |
17 | final bool enabled;
18 | final FocusNode? focusNode;
19 | final TextEditingController? controller;
20 | final bool obscureText;
21 | final Function(String) onSubmitted;
22 | final String labelText;
23 | final int? maxLenght;
24 | final TextInputType? keyboardType;
25 |
26 | @override
27 | Widget build(BuildContext context) {
28 | return TextField(
29 | enabled: enabled,
30 | obscureText: obscureText,
31 | focusNode: focusNode,
32 | controller: controller,
33 | maxLength: maxLenght,
34 | keyboardType: keyboardType,
35 | decoration: InputDecoration(
36 | labelText: labelText,
37 | counterText: '',
38 | focusedBorder: const OutlineInputBorder(
39 | borderRadius: BorderRadius.all(Radius.circular(10)),
40 | borderSide: BorderSide(
41 | color: Constants.mainColor,
42 | width: 2,
43 | ),
44 | ),
45 | enabledBorder: const OutlineInputBorder(
46 | borderRadius: BorderRadius.all(Radius.circular(10)),
47 | borderSide: BorderSide(
48 | color: Colors.grey,
49 | ),
50 | ),
51 | border: const OutlineInputBorder(
52 | borderRadius: BorderRadius.all(Radius.circular(10)),
53 | borderSide: BorderSide(
54 | color: Colors.grey,
55 | ),
56 | ),
57 | ),
58 | onSubmitted: onSubmitted,
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/lib/widgets/tasse_home_card.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:Esse3/screens/tasse_screen.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | /// Card per reinderizzare l'utente in [TasseScreen].
6 | class TasseHomeCard extends StatelessWidget {
7 | @override
8 | Widget build(BuildContext context) {
9 | return Container(
10 | decoration: const BoxDecoration(
11 | color: Colors.redAccent,
12 | borderRadius: BorderRadius.all(Radius.circular(10)),
13 | ),
14 | child: Padding(
15 | padding: const EdgeInsets.all(16.0),
16 | child: Column(
17 | crossAxisAlignment: CrossAxisAlignment.start,
18 | children: [
19 | Row(
20 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
21 | children: [
22 | Text(
23 | 'Tasse universitarie',
24 | style: Constants.fontBold20.copyWith(
25 | color: Colors.white,
26 | ),
27 | ),
28 | const Icon(
29 | Icons.monetization_on,
30 | color: Colors.white,
31 | ),
32 | ],
33 | ),
34 | const SizedBox(height: 20),
35 | MaterialButton(
36 | elevation: 0,
37 | onPressed: () {
38 | Navigator.of(context).pushNamed(TasseScreen.id);
39 | },
40 | color: Colors.white,
41 | textColor: Colors.redAccent,
42 | minWidth: double.infinity,
43 | shape: const RoundedRectangleBorder(
44 | borderRadius: BorderRadius.all(Radius.circular(50))),
45 | child: const Text(
46 | 'Sei sicuro di volerle guardare?',
47 | ),
48 | ),
49 | ],
50 | ),
51 | ),
52 | );
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/assets/img/backgroundLoginTablet.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/lib/widgets/home/animated_avatar.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:Esse3/constants.dart';
4 | import 'package:Esse3/models/studente_model.dart';
5 | import 'package:flutter/material.dart';
6 |
7 | class AnimatedAvatar extends StatelessWidget {
8 | const AnimatedAvatar({
9 | Key? key,
10 | required this.animation,
11 | required this.studenteModel,
12 | }) : super(key: key);
13 |
14 | final Animation? animation;
15 | final StudenteModel studenteModel;
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | final hasProfilePic = studenteModel.datiPersonali?.profilePicture != null;
20 | return Container(
21 | margin: const EdgeInsets.symmetric(vertical: 20),
22 | alignment: Alignment.center,
23 | width: 100,
24 | height: 100,
25 | child: Container(
26 | width: (animation!.value as double) * 100,
27 | height: (animation!.value as double) * 100,
28 | padding: const EdgeInsets.all(2.0),
29 | decoration: BoxDecoration(
30 | color: const Color(0xFFFFFFFF),
31 | shape: BoxShape.circle,
32 | boxShadow: [
33 | BoxShadow(
34 | color: Colors.black12
35 | .withOpacity((animation!.value as double) * 0.12),
36 | blurRadius: 10,
37 | offset: const Offset(0, 5),
38 | spreadRadius: 3,
39 | )
40 | ],
41 | ),
42 | child: CircleAvatar(
43 | backgroundColor: Constants.mainColorLighter,
44 | radius: (animation!.value as double) * 50,
45 | backgroundImage: hasProfilePic
46 | ? MemoryImage(studenteModel.datiPersonali!.profilePictureBytes!)
47 | : null,
48 | child: hasProfilePic
49 | ? const SizedBox.shrink()
50 | : Text(
51 | studenteModel.datiPersonali!.textAvatar,
52 | style: const TextStyle(
53 | fontWeight: FontWeight.w100,
54 | fontSize: 40,
55 | color: Colors.white,
56 | ),
57 | ),
58 | ),
59 | ),
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib/widgets/libretto/badge_libretto.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | class BadgeLibretto extends StatelessWidget {
5 | final double mediaPonderata;
6 |
7 | const BadgeLibretto({Key? key, required this.mediaPonderata})
8 | : assert(mediaPonderata != null),
9 | super(key: key);
10 | @override
11 | Widget build(BuildContext context) {
12 | return Positioned(
13 | bottom: (mediaPonderata >= 24) ? -22 : -20,
14 | child: Container(
15 | padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
16 | decoration: BoxDecoration(
17 | color: mediaPonderata >= 24
18 | ? Colors.yellow[700]
19 | : mediaPonderata >= 18
20 | ? Colors.green[700]
21 | : Colors.yellow[700],
22 | borderRadius: const BorderRadius.all(Radius.circular(30)),
23 | boxShadow: [
24 | BoxShadow(
25 | color: mediaPonderata >= 24 ? Colors.black12 : Colors.transparent,
26 | blurRadius: 10,
27 | spreadRadius: 3,
28 | ),
29 | ],
30 | border: Border.all(
31 | color: mediaPonderata >= 24 ? Colors.white : Colors.transparent,
32 | width: mediaPonderata >= 24 ? 2 : 0),
33 | ),
34 | child: Row(
35 | mainAxisSize: MainAxisSize.min,
36 | children: [
37 | Icon(
38 | mediaPonderata >= 24
39 | ? Icons.stars
40 | : mediaPonderata >= 18
41 | ? Icons.check_circle
42 | : Icons.warning,
43 | color: Colors.white),
44 | const SizedBox(width: 5),
45 | Text(
46 | mediaPonderata >= 24
47 | ? 'fantastico!'.toUpperCase()
48 | : mediaPonderata >= 18
49 | ? 'vai così'.toUpperCase()
50 | : 'attenzione'.toUpperCase(),
51 | style: Constants.fontBold.copyWith(color: Colors.white),
52 | ),
53 | ],
54 | ),
55 | ),
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:Esse3/screens/bacheca_prenotazioni_screen.dart';
3 | import 'package:Esse3/screens/home_screen.dart';
4 | import 'package:Esse3/screens/info_app_screen.dart';
5 | import 'package:Esse3/screens/login_screen.dart';
6 | import 'package:Esse3/screens/prossimi_appelli_screen.dart';
7 | import 'package:Esse3/screens/tasse_screen.dart';
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter/services.dart';
10 | import 'package:shared_preferences/shared_preferences.dart';
11 |
12 | /// Metodo principale che avvia [Esse3].
13 | ///
14 | /// Controlla se l'utente è già loggato o se deve effettuare il login,
15 | /// in modo da reinderizzare correttamente l'utente.
16 | Future main() async {
17 | WidgetsFlutterBinding.ensureInitialized();
18 | //Forzo il device ad orientarsi verticalmente
19 | await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
20 | //Controllo se l'utente è loggato
21 | final prefs = await SharedPreferences.getInstance();
22 | final version = prefs.getBool(Constants.version) ?? false;
23 | if (!version) {
24 | await prefs.clear();
25 | await prefs.setBool(Constants.version, true);
26 | }
27 | final status = prefs.getBool('isLoggedIn') ?? false;
28 | runApp(Esse3(logged: status));
29 | }
30 |
31 | /// Rappresenta la classe che avvia l'applicazione.
32 | class Esse3 extends StatelessWidget {
33 | const Esse3({required this.logged});
34 |
35 | /// Serve per controllare se l'utente è loggato.
36 | final bool logged;
37 | @override
38 | Widget build(BuildContext context) {
39 | return MaterialApp(
40 | debugShowCheckedModeBanner: false,
41 | darkTheme: Constants.darkTheme,
42 | theme: Constants.lightTheme,
43 | initialRoute: logged ? HomeScreen.id : LoginScreen.id,
44 | routes: {
45 | LoginScreen.id: (context) => LoginScreen(),
46 | HomeScreen.id: (context) => HomeScreen(),
47 | TasseScreen.id: (context) => TasseScreen(),
48 | BachecaPrenotazioniScreen.id: (context) => BachecaPrenotazioniScreen(),
49 | ProssimiAppelliScreen.id: (context) => ProssimiAppelliScreen(),
50 | InfoApp.id: (context) => InfoApp(),
51 | },
52 | );
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/screens/login_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:Esse3/widgets/login/login_form.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_svg/flutter_svg.dart';
5 |
6 | /// Pagina iniziale di login.
7 | class LoginScreen extends StatelessWidget {
8 | static const String id = 'loginScreen';
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | final deviceWidth = MediaQuery.of(context).size.width;
13 | final isTablet = deviceWidth > Constants.tabletWidth;
14 | return Stack(
15 | children: [
16 | const Scaffold(),
17 | SvgPicture.asset(
18 | isTablet
19 | ? 'assets/img/backgroundLoginTablet.svg'
20 | : 'assets/img/backgroundLogin.svg',
21 | width: deviceWidth,
22 | height: MediaQuery.of(context).size.height,
23 | fit: BoxFit.cover,
24 | ),
25 | GestureDetector(
26 | onTap: () {
27 | FocusScope.of(context).unfocus();
28 | },
29 | child: Scaffold(
30 | backgroundColor: Colors.transparent,
31 | body: Builder(builder: (BuildContext context) {
32 | return Padding(
33 | padding: isTablet
34 | ? EdgeInsets.symmetric(horizontal: deviceWidth / 6)
35 | : const EdgeInsets.all(32.0),
36 | child: Center(
37 | child: Column(
38 | mainAxisAlignment: MainAxisAlignment.center,
39 | crossAxisAlignment: CrossAxisAlignment.start,
40 | children: [
41 | const Text(
42 | 'Esse3 Unimore',
43 | style: Constants.fontBold32,
44 | ),
45 | const Text(
46 | 'App non (ancora) ufficiale',
47 | style: Constants.font20,
48 | ),
49 | const SizedBox(height: 30),
50 | LoginForm(),
51 | ],
52 | ),
53 | ),
54 | );
55 | }),
56 | ),
57 | ),
58 | ],
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | def keystoreProperties = new Properties()
29 | def keystorePropertiesFile = rootProject.file('key.properties')
30 | if (keystorePropertiesFile.exists()) {
31 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
32 | }
33 |
34 | android {
35 | compileSdkVersion 33
36 |
37 | sourceSets {
38 | main.java.srcDirs += 'src/main/kotlin'
39 | }
40 |
41 | lintOptions {
42 | disable 'InvalidPackage'
43 | }
44 |
45 | defaultConfig {
46 | applicationId "com.dariovarriale.esse3"
47 | minSdkVersion 16
48 | targetSdkVersion 33
49 | versionCode flutterVersionCode.toInteger()
50 | versionName flutterVersionName
51 | }
52 |
53 | signingConfigs {
54 | release {
55 | keyAlias keystoreProperties['keyAlias']
56 | keyPassword keystoreProperties['keyPassword']
57 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
58 | storePassword keystoreProperties['storePassword']
59 | }
60 | }
61 |
62 | buildTypes {
63 | release {
64 | signingConfig signingConfigs.release
65 | }
66 | }
67 | }
68 |
69 | flutter {
70 | source '../..'
71 | }
72 |
73 | dependencies {
74 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
75 | }
76 |
--------------------------------------------------------------------------------
/lib/widgets/shimmer_loader.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:Esse3/constants.dart';
4 | import 'package:Esse3/widgets/shimmer_custom.dart';
5 | import 'package:flutter/cupertino.dart';
6 | import 'package:flutter/material.dart';
7 |
8 | /// Animazione di caricamento per la pagina [TasseScreen]
9 | /// e [ProssimiAppelliScreen].
10 | class ShimmerLoader extends StatelessWidget {
11 | final bool? isTablet;
12 | final double? deviceWidth;
13 | final double shimmerHeight;
14 | final Color colorCircular;
15 |
16 | const ShimmerLoader(
17 | {Key? key,
18 | this.isTablet,
19 | this.deviceWidth,
20 | this.shimmerHeight = 200,
21 | this.colorCircular = Constants.mainColorLighter})
22 | : super(key: key);
23 | @override
24 | Widget build(BuildContext context) {
25 | // ignore: avoid_unnecessary_containers
26 | return Container(
27 | child: Column(
28 | children: [
29 | Padding(
30 | padding: const EdgeInsets.all(32.0),
31 | child: Row(
32 | mainAxisAlignment: MainAxisAlignment.center,
33 | children: [
34 | if (Platform.isIOS)
35 | const CupertinoActivityIndicator()
36 | else
37 | CircularProgressIndicator(
38 | valueColor: AlwaysStoppedAnimation(colorCircular),
39 | ),
40 | const SizedBox(width: 20),
41 | const Text(
42 | 'Sto scaricando i dati...',
43 | style: TextStyle(fontStyle: FontStyle.italic),
44 | ),
45 | ],
46 | ),
47 | ),
48 | Padding(
49 | padding: isTablet!
50 | ? EdgeInsets.symmetric(horizontal: deviceWidth! / 6)
51 | : const EdgeInsets.only(bottom: 16.0, left: 16, right: 16),
52 | child: Column(
53 | children: [
54 | ShimmerCustom(height: shimmerHeight),
55 | const SizedBox(height: 10),
56 | ShimmerCustom(height: shimmerHeight),
57 | if (isTablet!) ...[
58 | const SizedBox(height: 10),
59 | ShimmerCustom(height: shimmerHeight)
60 | ],
61 | ],
62 | ),
63 | ),
64 | ],
65 | ),
66 | );
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/lib/widgets/libretto/header_libretto.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:Esse3/widgets/libretto/grafico_libretto.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | class HeaderLibretto extends StatelessWidget {
6 | const HeaderLibretto({
7 | Key? key,
8 | this.darkModeOn = false,
9 | required this.puntiGrafico,
10 | }) : super(key: key);
11 |
12 | final bool darkModeOn;
13 | final List puntiGrafico;
14 | @override
15 | Widget build(BuildContext context) {
16 | return Column(
17 | crossAxisAlignment: CrossAxisAlignment.start,
18 | children: [
19 | Text(
20 | 'Libretto',
21 | style: Constants.fontBold32.copyWith(
22 | color: darkModeOn
23 | ? Theme.of(context).primaryColorLight
24 | : Colors.white),
25 | ),
26 | Text(
27 | 'Qui puoi vedere il tuo libretto universitario.',
28 | style: darkModeOn
29 | ? Constants.font16
30 | : Constants.font16.copyWith(color: Colors.white),
31 | ),
32 | const SizedBox(height: 20),
33 | Column(
34 | children: [
35 | Row(
36 | mainAxisAlignment: MainAxisAlignment.end,
37 | children: [
38 | if (puntiGrafico.isNotEmpty)
39 | Text(
40 | puntiGrafico.isEmpty
41 | ? 'Nessun esame con voto ancora'
42 | : puntiGrafico.length == 1
43 | ? 'Il tuo ultimo esame'
44 | : 'I tuoi ultimi ${puntiGrafico.length} esami',
45 | style: const TextStyle(
46 | fontStyle: FontStyle.italic,
47 | fontSize: 12,
48 | color: Colors.white,
49 | ),
50 | ),
51 | ],
52 | ),
53 | const SizedBox(height: 5),
54 | if (puntiGrafico.isEmpty)
55 | const Text(
56 | 'Non ho abbastanza elementi\nper disegnare un grafico...',
57 | style:
58 | TextStyle(fontStyle: FontStyle.italic, color: Colors.white),
59 | textAlign: TextAlign.center,
60 | )
61 | else
62 | GraficoLibretto(
63 | puntiGrafico: puntiGrafico,
64 | darkModeOn: darkModeOn,
65 | ),
66 | ],
67 | ),
68 | ],
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/lib/widgets/ricarica_bacheca_prenotazioni.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_svg/svg.dart';
4 | import 'package:liquid_pull_to_refresh/liquid_pull_to_refresh.dart';
5 |
6 | class RicaricaBachecaPrenotazioni extends StatelessWidget {
7 | const RicaricaBachecaPrenotazioni(
8 | {Key? key, required this.refreshBacheca, required this.svgAsset})
9 | : assert(refreshBacheca != null),
10 | assert(svgAsset != null),
11 | super(key: key);
12 |
13 | final Future Function() refreshBacheca;
14 | final SvgPicture svgAsset;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return LiquidPullToRefresh(
19 | animSpeedFactor: 1.5,
20 | height: 80,
21 | color: Theme.of(context).primaryColorLight,
22 | onRefresh: refreshBacheca,
23 | showChildOpacityTransition: false,
24 | child: ListView(
25 | children: [
26 | Padding(
27 | padding: const EdgeInsets.all(16.0),
28 | child: Column(
29 | crossAxisAlignment: CrossAxisAlignment.start,
30 | children: [
31 | const Text(
32 | 'Bacheca prenotazioni',
33 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25),
34 | ),
35 | const Divider(),
36 | const SizedBox(height: 50),
37 | Center(
38 | child: Column(
39 | mainAxisAlignment: MainAxisAlignment.center,
40 | children: [
41 | svgAsset,
42 | Constants.sized20H,
43 | const Text(
44 | 'Nessuna prenotazione',
45 | style: TextStyle(
46 | fontWeight: FontWeight.bold, fontSize: 32),
47 | ),
48 | Constants.sized10H,
49 | const Text(
50 | 'È tempo di preparare qualche esame e prenotarsi!',
51 | softWrap: true,
52 | textAlign: TextAlign.center,
53 | style: TextStyle(
54 | fontWeight: FontWeight.w500,
55 | fontSize: 20,
56 | ),
57 | )
58 | ],
59 | ),
60 | )
61 | ],
62 | ),
63 | ),
64 | ],
65 | ),
66 | );
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/lib/widgets/prossimi_appelli_card.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:Esse3/screens/prossimi_appelli_screen.dart';
3 | import 'package:Esse3/utils/provider.dart';
4 | import 'package:flutter/material.dart';
5 |
6 | /// Card per reinderizzare l'utente in [ProssimiAppelliScreen].
7 | class ProssimiAppelliCard extends StatelessWidget {
8 | @override
9 | Widget build(BuildContext context) {
10 | return Container(
11 | width: double.infinity,
12 | decoration: BoxDecoration(
13 | color: Theme.of(context).cardColor,
14 | borderRadius: const BorderRadius.all(Radius.circular(10)),
15 | boxShadow: const [
16 | BoxShadow(
17 | color: Colors.black12,
18 | offset: Offset(0, 2),
19 | spreadRadius: 1,
20 | blurRadius: 3),
21 | ],
22 | ),
23 | child: Padding(
24 | padding: const EdgeInsets.all(16.0),
25 | child: Column(
26 | crossAxisAlignment: CrossAxisAlignment.start,
27 | children: [
28 | Row(
29 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
30 | children: [
31 | Text(
32 | 'Prossimi appelli',
33 | style: Constants.fontBold20.copyWith(
34 | color: Theme.of(context).primaryColorLight,
35 | ),
36 | ),
37 | Icon(
38 | Icons.mode_edit,
39 | color: Theme.of(context).primaryColorLight,
40 | ),
41 | ],
42 | ),
43 | const SizedBox(height: 20),
44 | MaterialButton(
45 | onPressed: () {
46 | Navigator.of(context)
47 | .push(
48 | MaterialPageRoute(
49 | builder: (context) => ProssimiAppelliScreen(),
50 | ),
51 | )
52 | .then((value) {
53 | Provider.getAppelliPrenotati();
54 | });
55 | },
56 | textColor: Colors.white,
57 | color: Theme.of(context).primaryColor,
58 | disabledColor: Theme.of(context).disabledColor,
59 | minWidth: double.infinity,
60 | shape: const RoundedRectangleBorder(
61 | borderRadius: BorderRadius.all(Radius.circular(50))),
62 | child: const Text(
63 | 'Guarda i prossimi appelli',
64 | ),
65 | ),
66 | ],
67 | ),
68 | ),
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/lib/widgets/drawer_header_home.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:Esse3/models/studente_model.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | class DrawerHeaderHome extends StatelessWidget {
6 | const DrawerHeaderHome({Key? key, required this.studenteModel})
7 | : super(key: key);
8 |
9 | final StudenteModel studenteModel;
10 | @override
11 | Widget build(BuildContext context) {
12 | final hasProfilePic = studenteModel.datiPersonali?.profilePicture != null;
13 | final hasTextAvatar = studenteModel.datiPersonali?.textAvatar != null;
14 | return Row(
15 | children: [
16 | Container(
17 | decoration: const BoxDecoration(
18 | shape: BoxShape.circle,
19 | color: Colors.white,
20 | ),
21 | padding: const EdgeInsets.all(1),
22 | child: CircleAvatar(
23 | backgroundColor: Constants.mainColor.withOpacity(0.9),
24 | radius: 30,
25 | backgroundImage: hasProfilePic
26 | ? MemoryImage(
27 | studenteModel.datiPersonali!.profilePictureBytes!,
28 | )
29 | : null,
30 | child: hasProfilePic
31 | ? const SizedBox.shrink()
32 | : hasTextAvatar
33 | ? Text(
34 | studenteModel.datiPersonali!.textAvatar,
35 | style: const TextStyle(
36 | fontWeight: FontWeight.w100,
37 | fontSize: 16,
38 | color: Colors.white,
39 | ),
40 | )
41 | : null,
42 | ),
43 | ),
44 | const SizedBox(width: 15),
45 | Flexible(
46 | child: Column(
47 | crossAxisAlignment: CrossAxisAlignment.start,
48 | mainAxisAlignment: MainAxisAlignment.center,
49 | children: [
50 | Text(
51 | studenteModel.datiPersonali?.nomeCompleto ?? 'null',
52 | style: const TextStyle(
53 | fontSize: 16,
54 | color: Colors.white,
55 | fontWeight: FontWeight.w600,
56 | ),
57 | softWrap: true,
58 | ),
59 | const SizedBox(height: 5),
60 | Text(
61 | 'Matr. ${studenteModel.datiPersonali?.matricola ?? 'null'}',
62 | style: const TextStyle(color: Colors.white),
63 | )
64 | ],
65 | ),
66 | ),
67 | ],
68 | );
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/lib/models/studente_model.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:Esse3/models/dati_personali_studente.dart';
4 | import 'package:Esse3/models/riepilogo_esami_studente.dart';
5 | import 'package:Esse3/models/status_studente.dart';
6 | import 'package:Esse3/utils/interfaces/codable.dart';
7 | import 'package:html/dom.dart';
8 |
9 | class StudenteModel extends Codable {
10 | static String get sharedKey => 'studenteModel';
11 |
12 | DatiPersonaliStudente? _datiPersonali;
13 | StatusStudente? _status;
14 | RiepilogoEsamiStudente? _riepilogoEsami;
15 |
16 | DatiPersonaliStudente? get datiPersonali => _datiPersonali;
17 | StatusStudente? get status => _status;
18 | RiepilogoEsamiStudente? get riepilogoEsami => _riepilogoEsami;
19 |
20 | bool get isValid =>
21 | datiPersonali != null && status != null && riepilogoEsami != null;
22 |
23 | @override
24 | void decode(String data) {
25 | final jsonData = json.decode(data) as Map;
26 |
27 | final datiPersonali = jsonData['datiPersonali'] as Map?;
28 | final status = jsonData['status'] as Map?;
29 | final riepilogoEsami = jsonData['riepilogoEsami'] as Map?;
30 | if (datiPersonali != null) {
31 | _datiPersonali = DatiPersonaliStudente()
32 | ..fromJson(datiPersonali);
33 | }
34 | if (status != null) {
35 | _status = StatusStudente()..fromJson(status);
36 | }
37 | if (riepilogoEsami != null) {
38 | _riepilogoEsami = RiepilogoEsamiStudente()..fromJson(riepilogoEsami);
39 | }
40 | }
41 |
42 | @override
43 | Map toJson() => {
44 | 'datiPersonali': _datiPersonali?.toJson(),
45 | 'status': _status?.toJson(),
46 | 'riepilogoEsami': _riepilogoEsami?.toJson(),
47 | };
48 |
49 | @override
50 | void fromJson(Map json) {
51 |
52 | }
53 |
54 | void fromHtmlBody({
55 | required Document document,
56 | String? matricola,
57 | String? profilePicture,
58 | }) {
59 | final datiPersonali = document
60 | .getElementById('gu-hpstu-boxDatiPersonali')
61 | ?.querySelector('.record-riga');
62 | final status = document.getElementById('gu-homepagestudente-cp2Child');
63 | final riepilogoEsami = document.getElementById('gu-boxRiepilogoEsami');
64 | if (datiPersonali != null) {
65 | _datiPersonali = DatiPersonaliStudente()
66 | ..fromHtmlElement(
67 | element: datiPersonali,
68 | matricola: matricola,
69 | profilePicture: profilePicture,
70 | );
71 | }
72 | if (status != null) {
73 | _status = StatusStudente()..fromHtmlElement(status: status);
74 | }
75 | if (riepilogoEsami != null) {
76 | _riepilogoEsami = RiepilogoEsamiStudente()
77 | ..fromHtmlElement(riepilogo: riepilogoEsami);
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/lib/widgets/reload_appelli_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_svg/flutter_svg.dart';
3 | import 'package:liquid_pull_to_refresh/liquid_pull_to_refresh.dart';
4 |
5 | /// Schermata di errore che appare quando la richiesta
6 | /// della [BachecaPrenotazioniScreen] non va a buon fine.
7 | class ReloadAppelli extends StatelessWidget {
8 | final Future Function()? onReload;
9 | final double? deviceHeight, deviceWidth;
10 |
11 | const ReloadAppelli(
12 | {Key? key, this.onReload, this.deviceHeight, this.deviceWidth})
13 | : super(key: key);
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return LiquidPullToRefresh(
18 | animSpeedFactor: 1.5,
19 | height: 80,
20 | color: Theme.of(context).primaryColorLight,
21 | onRefresh: onReload!,
22 | showChildOpacityTransition: false,
23 | child: SingleChildScrollView(
24 | physics: const AlwaysScrollableScrollPhysics(),
25 | child: Padding(
26 | padding: const EdgeInsets.all(16.0),
27 | child: Container(
28 | constraints: BoxConstraints(
29 | maxHeight: deviceHeight! - 130,
30 | ),
31 | child: Column(
32 | crossAxisAlignment: CrossAxisAlignment.start,
33 | children: [
34 | const Text(
35 | 'Bacheca prenotazioni',
36 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25),
37 | ),
38 | const Divider(),
39 | const SizedBox(height: 50),
40 | Center(
41 | child: Column(
42 | children: [
43 | SvgPicture.asset(
44 | 'assets/img/networkError.svg',
45 | width: deviceWidth! * 0.7,
46 | ),
47 | const SizedBox(
48 | height: 20,
49 | ),
50 | const Text(
51 | 'Oops..',
52 | style: TextStyle(
53 | fontWeight: FontWeight.bold, fontSize: 32),
54 | ),
55 | const SizedBox(height: 10),
56 | const Text(
57 | 'Ci sono problemi nel recuperare i tuoi dati, aggiorna oppure riprova tra un pò!',
58 | softWrap: true,
59 | textAlign: TextAlign.center,
60 | style: TextStyle(
61 | fontWeight: FontWeight.w500,
62 | fontSize: 20,
63 | ),
64 | )
65 | ],
66 | ),
67 | )
68 | ],
69 | ),
70 | ),
71 | ),
72 | ),
73 | );
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/lib/widgets/card_appello_imminente.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:Esse3/models/appello_model.dart';
3 | import 'package:Esse3/widgets/box_info.dart';
4 | import 'package:flutter/material.dart';
5 |
6 | /// Card con poche informazioni sull'appello, utilizzata per gli appelli
7 | /// imminenti in [ProssimiAppelliScreen].
8 | class CardAppelloImminente extends StatelessWidget {
9 | final String? nomeAppello;
10 | final String? dataAppello;
11 | final String? descrizione;
12 | String? get _nomeMateria {
13 | if (nomeAppello!.length > 20) {
14 | return "${nomeAppello!.substring(0, 20)}...";
15 | }
16 | return nomeAppello;
17 | }
18 |
19 | String? get _descrizione {
20 | if (descrizione!.length > 60) {
21 | return "${descrizione!.substring(0, 60)}...";
22 | }
23 | return descrizione;
24 | }
25 |
26 | /// Serve per gestire il layout del tablet.
27 | final bool isTablet;
28 |
29 | /// Larghezza del dispositivo. Serve per gestire il layout del tablet.
30 | final double deviceWidth;
31 |
32 | const CardAppelloImminente({
33 | Key? key,
34 | this.nomeAppello,
35 | this.dataAppello,
36 | this.descrizione,
37 | this.isTablet = false,
38 | this.deviceWidth = 0,
39 | }) : super(key: key);
40 |
41 | factory CardAppelloImminente.fromAppello(AppelloModel appello) {
42 | return CardAppelloImminente(
43 | nomeAppello: appello.nomeMateria,
44 | dataAppello: appello.dataAppello,
45 | descrizione: appello.descrizione,
46 | );
47 | }
48 |
49 | @override
50 | Widget build(BuildContext context) {
51 | final darkModeOn =
52 | MediaQuery.of(context).platformBrightness == Brightness.dark;
53 | return Container(
54 | padding: const EdgeInsets.all(16),
55 | width: isTablet ? 350 : deviceWidth - 70,
56 | decoration: BoxDecoration(
57 | borderRadius: BorderRadius.circular(10),
58 | boxShadow: [
59 | BoxShadow(
60 | color: Colors.black12.withOpacity(0.05),
61 | offset: const Offset(0, 3),
62 | blurRadius: 3,
63 | spreadRadius: 3,
64 | ),
65 | ],
66 | color: Theme.of(context).cardColor,
67 | ),
68 | child: Column(
69 | crossAxisAlignment: CrossAxisAlignment.start,
70 | children: [
71 | Row(
72 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
73 | children: [
74 | Flexible(
75 | child: Text(
76 | _nomeMateria!,
77 | style: Constants.fontBold20
78 | .copyWith(color: Theme.of(context).primaryColorLight),
79 | ),
80 | ),
81 | const SizedBox(width: 10),
82 | BoxInfo(
83 | darkModeOn: darkModeOn,
84 | minWidth: null,
85 | backgroundColor: Theme.of(context).primaryColorLight,
86 | child: Text(
87 | dataAppello!,
88 | style: Constants.fontBold.copyWith(color: Colors.white),
89 | ),
90 | ),
91 | ],
92 | ),
93 | const SizedBox(height: 10),
94 | const Text('Descrizione', style: Constants.fontBold),
95 | const SizedBox(height: 5),
96 | BoxInfo(
97 | darkModeOn: darkModeOn,
98 | child: Text(_descrizione!),
99 | ),
100 | ],
101 | ),
102 | );
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/lib/widgets/home/home_screen_builder.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:Esse3/constants.dart';
4 | import 'package:Esse3/models/studente_model.dart';
5 | import 'package:Esse3/widgets/chip_info.dart';
6 | import 'package:Esse3/widgets/home/animated_avatar.dart';
7 | import 'package:Esse3/widgets/libretto_home_card.dart';
8 | import 'package:Esse3/widgets/prossimi_appelli_card.dart';
9 | import 'package:Esse3/widgets/tasse_home_card.dart';
10 | import 'package:flutter/material.dart';
11 |
12 | class HomeScreenBuilder extends StatelessWidget {
13 | final double deviceWidth;
14 | final Animation animation;
15 | final StudenteModel studenteModel;
16 | late List cdl;
17 |
18 | HomeScreenBuilder({
19 | Key? key,
20 | required this.deviceWidth,
21 | required this.animation,
22 | required this.studenteModel,
23 | }) : super(key: key);
24 | @override
25 | Widget build(BuildContext context) {
26 | final hasCorso = studenteModel.status?.corso != null;
27 | if (hasCorso) {
28 | cdl = studenteModel.status!.corso!.split('(');
29 | cdl[1] = '(${cdl[1]}';
30 | }
31 | return Column(
32 | children: [
33 | Padding(
34 | padding: deviceWidth >= 390
35 | ? const EdgeInsets.symmetric(horizontal: 20)
36 | : const EdgeInsets.symmetric(horizontal: 0),
37 | child: Column(
38 | children: [
39 | AnimatedAvatar(
40 | animation: animation, studenteModel: studenteModel),
41 | Text(
42 | studenteModel.datiPersonali?.nomeCompleto ?? 'null',
43 | style: Constants.fontBold28,
44 | ),
45 | const SizedBox(height: 5),
46 | Text(
47 | '- Mat. ${studenteModel.datiPersonali?.matricola} -',
48 | style: Constants.font16,
49 | ),
50 | const SizedBox(height: 10),
51 | Text(
52 | cdl.first,
53 | textAlign: TextAlign.center,
54 | style: Constants.fontBold32,
55 | ),
56 | const SizedBox(height: 5),
57 | Text(
58 | cdl[1],
59 | style: Constants.font16,
60 | ),
61 | const SizedBox(height: 20),
62 | ],
63 | ),
64 | ),
65 | Wrap(
66 | alignment: WrapAlignment.center,
67 | spacing: 10,
68 | runSpacing: -5,
69 | children: [
70 | ChipInfo(
71 | text: 'Durata: ${studenteModel.status?.durataCorso}',
72 | textSize: deviceWidth >= 390 ? 13 : 10),
73 | ChipInfo(
74 | text: 'Percorso: ${studenteModel.status?.percorso}',
75 | textSize: deviceWidth >= 390 ? 13 : 10),
76 | ChipInfo(
77 | text: 'Anno di Corso: ${studenteModel.status?.annoCorso}',
78 | textSize: deviceWidth >= 390 ? 13 : 10),
79 | ChipInfo(
80 | text:
81 | 'Immatricolazione: ${studenteModel.status?.dataImmatricolazione}',
82 | textSize: deviceWidth >= 390 ? 13 : 10),
83 | // ChipInfo(
84 | // text: 'Part Time: ${userData.data!['part_time']}',
85 | // textSize: deviceWidth >= 390 ? 13 : 10),
86 | ],
87 | ),
88 | const SizedBox(height: 20),
89 | const LibrettoHomeCard(),
90 | const SizedBox(height: 15),
91 | ProssimiAppelliCard(),
92 | const SizedBox(height: 15),
93 | TasseHomeCard(),
94 | ],
95 | );
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/lib/widgets/libretto_home_card.dart:
--------------------------------------------------------------------------------
1 | import 'package:Esse3/constants.dart';
2 | import 'package:Esse3/utils/provider.dart';
3 | import 'package:Esse3/widgets/libretto/future_libretto.dart';
4 | import 'package:flutter/material.dart';
5 |
6 | /// Card in cui è possibile richiedere le informazioni del libretto,
7 | /// utilizzata nella [HomeScreen].
8 | class LibrettoHomeCard extends StatefulWidget {
9 | const LibrettoHomeCard({
10 | Key? key,
11 | }) : super(key: key);
12 |
13 | @override
14 | _LibrettoHomeCardState createState() => _LibrettoHomeCardState();
15 | }
16 |
17 | class _LibrettoHomeCardState extends State {
18 | /// Future del libretto.
19 | late Future