This commit is contained in:
Elias Müller 2024-03-29 21:58:51 +01:00
parent eaf6d9f547
commit a6f7c09671
9 changed files with 203 additions and 67 deletions

View File

@ -0,0 +1,9 @@
import 'package:flutter/material.dart';
import 'models/subject.dart';
abstract class AbiturCalculatorStep extends Step {
const AbiturCalculatorStep({required super.title, required super.content});
bool canGoNextStep(SubjectCollection subjects);
}

View File

@ -1,11 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../../widget/confirmDialog.dart';
import 'models/abiturCalculatorModel.dart'; import 'models/abiturCalculatorModel.dart';
import 'models/subject.dart'; import 'models/subject.dart';
import 'steps/resultStep.dart';
import 'steps/selectGkStep.dart';
import 'steps/selectLkStep.dart';
import 'steps/selectPfStep.dart';
import 'steps/welcomeStep.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class AbiturCalculatorView extends StatelessWidget { class AbiturCalculatorView extends StatelessWidget {
@ -33,18 +29,26 @@ class AbiturCalculatorView extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Abitur Notenrechner'), title: const Text('Abitur Notenrechner'),
actions: [
IconButton(
onPressed: () => ConfirmDialog(
title: 'Zurücksetzen',
content: 'Alle Felder werden zurückgesetzt',
confirmButton: 'Löschen',
onConfirm: state.reset,
).asDialog(context),
icon: const Icon(Icons.delete_outline_outlined),
)
],
), ),
body: Stepper( body: Stepper(
type: StepperType.vertical, type: StepperType.vertical,
steps: [ steps: state.getSteps,
const WelcomeStep(), currentStep: state.getCurrentStepIndex,
SelectLkStep(),
SelectGkStep(), onStepContinue: () => state.increaseStep(),
const SelectPfStep(), onStepCancel: () => state.decreaseStep(),
const ResultStep(),
],
currentStep: state.getStep,
onStepTapped: (step) => state.setStep = step,
stepIconBuilder: (stepIndex, stepState) => _stepIconBuilder(context, stepIndex, stepState), stepIconBuilder: (stepIndex, stepState) => _stepIconBuilder(context, stepIndex, stepState),
controlsBuilder: _controlsBuilder, controlsBuilder: _controlsBuilder,
) )
@ -57,9 +61,9 @@ class AbiturCalculatorView extends StatelessWidget {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
color: state.getStep == stepIndex color: state.getCurrentStepIndex == stepIndex
? Theme.of(context).primaryColor ? Theme.of(context).primaryColor
: Theme.of(context).colorScheme.surface, : Theme.of(context).colorScheme.surfaceVariant,
), ),
child: Center( child: Center(
child: Text((++stepIndex).toString()), child: Text((++stepIndex).toString()),
@ -68,7 +72,18 @@ class AbiturCalculatorView extends StatelessWidget {
}); });
} }
Widget _controlsBuilder(BuildContext context, details) { Widget _controlsBuilder(BuildContext context, ControlsDetails details) {
return const Row(children: [SizedBox.shrink()]); return Row(
children: [
TextButton(
onPressed: details.onStepCancel,
child: const Text('Zurück'),
),
TextButton(
onPressed: details.onStepContinue,
child: const Text('Weiter'),
),
]
);
} }
} }

View File

@ -1,52 +1,77 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../abiturCalculatorStep.dart';
import '../steps/resultStep.dart';
import '../steps/selectGkStep.dart';
import '../steps/selectLkStep.dart';
import '../steps/selectPfStep.dart';
import '../steps/welcomeStep.dart';
import 'subject.dart'; import 'subject.dart';
class AbiturCalculatorModel extends ChangeNotifier { class AbiturCalculatorModel extends ChangeNotifier {
static SubjectCollection subjects = SubjectCollection([ static final SubjectCollection _collection = SubjectCollection([
Subject(displayName: 'Deutsch', icon: Icons.translate_outlined, lkApplicable: true), Subject(displayName: 'Deutsch', icon: Icons.translate_outlined, canBeLk: true, importantLk: true),
Subject(displayName: 'Erste Fremdsprache', icon: Icons.translate_outlined, lkApplicable: true), Subject(displayName: 'Erste Fremdsprache', icon: Icons.translate_outlined, canBeLk: true, importantLk: true),
Subject(displayName: 'Zweite Fremdsprache', icon: Icons.translate_outlined, lkApplicable: true), Subject(displayName: 'Zweite Fremdsprache', icon: Icons.translate_outlined, canBeLk: true, importantLk: true),
Subject(displayName: 'Zweite Fremdsprache ab Kl. 11', icon: Icons.translate_outlined, lkApplicable: false), Subject(displayName: 'Zweite Fremdsprache ab Kl. 11', icon: Icons.translate_outlined, canBeLk: false, importantLk: false),
Subject(displayName: 'Kunst', icon: Icons.brush_outlined, lkApplicable: true), Subject(displayName: 'Kunst', icon: Icons.brush_outlined, canBeLk: true, importantLk: false),
Subject(displayName: 'Musik', icon: Icons.music_note_outlined, lkApplicable: true), Subject(displayName: 'Musik', icon: Icons.music_note_outlined, canBeLk: true, importantLk: false),
Subject(displayName: 'Darstellendes Spiel', icon: Icons.theater_comedy_outlined, lkApplicable: false), Subject(displayName: 'Darstellendes Spiel', icon: Icons.theater_comedy_outlined, canBeLk: false, importantLk: false),
Subject(displayName: 'PoWi', icon: Icons.book_outlined, lkApplicable: true), Subject(displayName: 'PoWi', icon: Icons.book_outlined, canBeLk: true, importantLk: false),
Subject(displayName: 'Geschichte', icon: Icons.history_outlined, lkApplicable: true), Subject(displayName: 'Geschichte', icon: Icons.history_outlined, canBeLk: true, importantLk: false),
Subject(displayName: 'Religion', icon: Icons.church_outlined, lkApplicable: true), Subject(displayName: 'Religion', icon: Icons.church_outlined, canBeLk: true, importantLk: false),
Subject(displayName: 'Erdkunde', icon: Icons.map_outlined, lkApplicable: true), Subject(displayName: 'Erdkunde', icon: Icons.map_outlined, canBeLk: true, importantLk: false),
Subject(displayName: 'Mathematik', icon: Icons.calculate_outlined, lkApplicable: true), Subject(displayName: 'Mathematik', icon: Icons.calculate_outlined, canBeLk: true, importantLk: true),
Subject(displayName: 'Erste Naturwissenschaft', icon: Icons.science_outlined, lkApplicable: true), Subject(displayName: 'Chemie', icon: Icons.science_outlined, canBeLk: true, importantLk: true),
Subject(displayName: 'Zweite Naturwissenschaft', icon: Icons.science_outlined, lkApplicable: true), Subject(displayName: 'Physik', icon: Icons.add, canBeLk: true, importantLk: true),
Subject(displayName: 'Dritte Naturwissenschaft', icon: Icons.science_outlined, lkApplicable: true), Subject(displayName: 'Biologie', icon: Icons.add, canBeLk: true, importantLk: true),
Subject(displayName: 'Biochemie', icon: Icons.biotech_outlined, lkApplicable: false), Subject(displayName: 'Biochemie', icon: Icons.biotech_outlined, canBeLk: false, importantLk: false),
Subject(displayName: 'Informatik', icon: Icons.code_outlined, lkApplicable: true), Subject(displayName: 'Informatik', icon: Icons.code_outlined, canBeLk: true, importantLk: false),
Subject(displayName: 'Sport', icon: Icons.sports_basketball_outlined, lkApplicable: true), Subject(displayName: 'Sport', icon: Icons.sports_basketball_outlined, canBeLk: true, importantLk: false),
]); ]);
static final List<AbiturCalculatorStep> _steps = [
const WelcomeStep(),
SelectLkStep(),
SelectGkStep(),
const SelectPfStep(),
const ResultStep(),
];
List<AbiturCalculatorStep> get getSteps => _steps;
AbiturCalculatorStep getCurrentStep() => _steps[getCurrentStepIndex];
static AbiturCalculatorModel get(BuildContext context) { static AbiturCalculatorModel get(BuildContext context) {
return Provider.of<AbiturCalculatorModel>(context, listen: false); return Provider.of<AbiturCalculatorModel>(context, listen: false);
} }
late SubjectCollection _collection;
int _currentStep = 0; int _currentStep = 0;
SubjectCollection get getSubjects => _collection; SubjectCollection getSubjects({bool update = false}) {
void updateSubjects(void Function(SubjectCollection collection) callback) { if(update) notifyListeners();
callback(_collection); return _collection;
}
void update() {
notifyListeners(); notifyListeners();
} }
int get getStep => _currentStep; int get getCurrentStepIndex => _currentStep;
set setStep(int step) { void increaseStep() {
_currentStep = step; if(getCurrentStep().canGoNextStep(getSubjects())) _currentStep++;
notifyListeners();
}
void decreaseStep() {
if(_currentStep >= 1) _currentStep--;
notifyListeners(); notifyListeners();
} }
void reset() { void reset() {
_currentStep = 0; _currentStep = 0;
_collection = subjects; for (var e in _collection.collection) {
e.reset();
}
notifyListeners(); notifyListeners();
} }

View File

@ -19,24 +19,43 @@ class SubjectCollection {
} }
class Subject { class Subject {
String displayName; final String displayName;
IconData icon; final IconData icon;
bool lkApplicable; final bool canBeLk;
final bool importantLk;
SubjectState _subjectState = SubjectState.none; SubjectState _subjectState = SubjectState.none;
bool isLk() => _subjectState == SubjectState.isLk; bool isLk() => _subjectState == SubjectState.isLk;
bool isGk() => _subjectState == SubjectState.isGk; bool isGk() => _subjectState == SubjectState.isGk;
bool canLk(List<Subject> other) => lkApplicable && !isGk() && other.where((s) => s.isLk()).length < HighEduGraduationConstants.maxLks;
bool canLk(List<Subject> other) {
Subject? otherLk() => other.where((s) => s.isLk()).firstOrNull;
bool hasOtherLk() => otherLk() != null;
return
((hasOtherLk() && otherLk()!.importantLk) || importantLk)
&& canBeLk
&& !isGk()
&& other.where((s) => s.isLk()).length < HighEduGraduationConstants.maxLks;
}
bool canGk(List<Subject> other) => !isLk() && other.where((s) => s.isGk()).length < HighEduGraduationConstants.maxGks; bool canGk(List<Subject> other) => !isLk() && other.where((s) => s.isGk()).length < HighEduGraduationConstants.maxGks;
set unsafeSubjectSet(SubjectState newState) => _subjectState = newState; set unsafeSubjectSet(SubjectState newState) => _subjectState = newState;
set unsafeLkToggle(bool isLk) { set unsafeLkToggle(bool isLk) {
if(isLk) {
}
_subjectState = isLk ? SubjectState.isLk : SubjectState.none; _subjectState = isLk ? SubjectState.isLk : SubjectState.none;
} }
set unsafeGkToggle(bool isGk) { set unsafeGkToggle(bool isGk) {
_subjectState = isGk ? SubjectState.isGk : SubjectState.none; _subjectState = isGk ? SubjectState.isGk : SubjectState.none;
} }
Subject({required this.displayName, required this.icon, required this.lkApplicable}); void reset() {
_subjectState = SubjectState.none;
}
Subject({required this.displayName, required this.icon, required this.canBeLk, required this.importantLk});
} }

View File

@ -1,8 +1,16 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ResultStep extends Step { import '../abiturCalculatorStep.dart';
import '../models/subject.dart';
class ResultStep extends AbiturCalculatorStep {
const ResultStep() : super( const ResultStep() : super(
title: const Text('Ergebnis'), title: const Text('Ergebnis'),
content: const SizedBox.shrink(), content: const SizedBox.shrink(),
); );
@override
bool canGoNextStep(SubjectCollection subjects) {
throw UnimplementedError();
}
} }

View File

@ -1,23 +1,54 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../../../widget/centeredLeading.dart';
import '../../../../../widget/providerBridge.dart';
import '../abiturCalculatorStep.dart';
import '../abiturCalculatorView.dart'; import '../abiturCalculatorView.dart';
import '../models/abiturCalculatorModel.dart'; import '../models/abiturCalculatorModel.dart';
import '../models/subject.dart';
class SelectGkStep extends Step { class SelectGkStep extends AbiturCalculatorStep {
SelectGkStep() : super( SelectGkStep() : super(
title: const Text('Grundkurse'), title: const Text('Grundkurse'),
content: Builder(builder: (context) { content: Builder(builder: (context) {
var gkSubjects = AbiturCalculatorModel.get(context).getSubjects.collection.where((e) => e.isGk()).toList(); var model = AbiturCalculatorModel.get(context);
var gkSubjects = model.getSubjects().collection.where((e) => e.isGk()).toList();
return Column( return Column(
children: [ children: [
AbiturCalculatorView.listSubjects(gkSubjects), AbiturCalculatorView.listSubjects(gkSubjects),
TextButton( TextButton(
onPressed: () { onPressed: () {
// showDialog( ProviderBridge.toDialog(context, model, (context, value) {
// context: context, return AlertDialog(
// barrierDismissible: false, title: const Text('Grundkurse'),
// builder: (context) => const SelectGkDialog(), content: SingleChildScrollView(
// ); child: Column(
mainAxisSize: MainAxisSize.min,
children: model.getSubjects().collection.map((e) {
return ListTile(
leading: CenteredLeading(Icon(e.icon)),
title: Text(e.displayName),
trailing: Checkbox(
value: e.isGk(),
onChanged: e.isGk() || e.canGk(model.getSubjects().collection) ? (value) {
e.unsafeGkToggle = value!;
model.update();
} : null,
),
);
}).toList(),
),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Fertig'),
)
],
);
});
}, },
child: Text( child: Text(
"Grundkurse ${gkSubjects.isEmpty ? "auswählen" : "ändern"}"), "Grundkurse ${gkSubjects.isEmpty ? "auswählen" : "ändern"}"),
@ -26,4 +57,10 @@ class SelectGkStep extends Step {
); );
}), }),
); );
@override
bool canGoNextStep(SubjectCollection subjects) {
// TODO: implement canGoNextStepp
throw UnimplementedError();
}
} }

View File

@ -2,15 +2,17 @@ import 'package:flutter/material.dart';
import '../../../../../widget/providerBridge.dart'; import '../../../../../widget/providerBridge.dart';
import '../../../../../widget/centeredLeading.dart'; import '../../../../../widget/centeredLeading.dart';
import '../abiturCalculatorStep.dart';
import '../abiturCalculatorView.dart'; import '../abiturCalculatorView.dart';
import '../models/abiturCalculatorModel.dart'; import '../models/abiturCalculatorModel.dart';
import '../models/subject.dart';
class SelectLkStep extends Step { class SelectLkStep extends AbiturCalculatorStep {
SelectLkStep() : super( SelectLkStep() : super(
title: const Text('Leistungskurse'), title: const Text('Leistungskurse'),
content: StatefulBuilder(builder: (context, setState) { content: StatefulBuilder(builder: (context, setState) {
var model = AbiturCalculatorModel.get(context); var model = AbiturCalculatorModel.get(context);
var lkSubjects = model.getSubjects.collection.where((e) => e.isLk()).toList(); var lkSubjects = model.getSubjects().collection.where((e) => e.isLk()).toList();
return Column( return Column(
children: [ children: [
AbiturCalculatorView.listSubjects(lkSubjects), AbiturCalculatorView.listSubjects(lkSubjects),
@ -22,16 +24,15 @@ class SelectLkStep extends Step {
content: SingleChildScrollView( content: SingleChildScrollView(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: model.getSubjects.collection.map((e) { children: model.getSubjects().collection.map((e) {
return ListTile( return ListTile(
leading: CenteredLeading(Icon(e.icon)), leading: CenteredLeading(Icon(e.icon)),
title: Text(e.displayName), title: Text(e.displayName),
trailing: Checkbox( trailing: Checkbox(
value: e.isLk(), value: e.isLk(),
onChanged: e.isLk() || e.canLk(model.getSubjects.collection) ? (value) { onChanged: e.isLk() || e.canLk(model.getSubjects().collection) ? (value) {
model.updateSubjects((collection) { e.unsafeLkToggle = value!;
e.unsafeLkToggle = value!; model.update();
});
} : null, } : null,
), ),
); );
@ -55,6 +56,11 @@ class SelectLkStep extends Step {
); );
}), }),
); );
@override
bool canGoNextStep(SubjectCollection subjects) {
return subjects.collection.where((element) => element.isLk()).length >= 2;
}
} }
class SelectLkStepTest extends StatelessWidget { class SelectLkStepTest extends StatelessWidget {
const SelectLkStepTest({super.key}); const SelectLkStepTest({super.key});

View File

@ -1,8 +1,17 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class SelectPfStep extends Step { import '../abiturCalculatorStep.dart';
import '../models/subject.dart';
class SelectPfStep extends AbiturCalculatorStep {
const SelectPfStep() : super( const SelectPfStep() : super(
title: const Text('Prüfungsfächer'), title: const Text('Prüfungsfächer'),
content: const SizedBox.shrink(), content: const SizedBox.shrink(),
); );
@override
bool canGoNextStep(SubjectCollection subjects) {
// TODO: implement canGoNextStepp
throw UnimplementedError();
}
} }

View File

@ -1,8 +1,16 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class WelcomeStep extends Step { import '../abiturCalculatorStep.dart';
import '../models/subject.dart';
class WelcomeStep extends AbiturCalculatorStep {
const WelcomeStep() : super( const WelcomeStep() : super(
title: const Text('Willkommen'), title: const Text('Willkommen'),
content: const Text('In den folgenden Schritten werden alle nötigen Informationen zur Ermittlung deiner Abiturzulassung abgefragt.') content: const Text('In den folgenden Schritten werden alle nötigen Informationen zur Ermittlung deiner Abiturzulassung abgefragt.')
); );
@override
bool canGoNextStep(SubjectCollection subjects) {
return true;
}
} }