replaced milestone images with emojis and added some details

main
henryhdr 2024-05-20 16:34:31 +02:00
parent 7f05993257
commit b791a4b69f
11 changed files with 110 additions and 73 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 927 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 641 KiB

View File

@ -201,50 +201,52 @@ class _MyHomePageState extends State<MyHomePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: SingleChildScrollView( body: SafeArea(
child: Padding( child: SingleChildScrollView(
padding: const EdgeInsets.all(20.0), physics: const BouncingScrollPhysics(),
child: Column( child: Padding(
crossAxisAlignment: CrossAxisAlignment.stretch, padding: const EdgeInsets.all(20.0),
children: <Widget>[ child: Column(
buildInputWidget('Anfangskapital', _initialCapitalController, _initialCapitalFocusNode, _isInitialCapitalEntered, '', 'Das Anfangskapital ist der Betrag, den Sie zu Beginn Ihrer Anlage haben.'), crossAxisAlignment: CrossAxisAlignment.stretch,
buildInputWidget('Monatliche Sparrate', _monthlySavingsRateController, _monthlySavingsRateFocusNode, _isMonthlySavingsRateEntered, '', 'Die monatliche Sparrate ist der Betrag, den Sie jeden Monat zu Ihrer Investition hinzufügen.'), children: <Widget>[
buildInputWidget('Jährlicher Zinssatz', _interestRateController, _interestRateFocusNode, _isInterestRateEntered, '%', 'Der jährliche Zinssatz ist der Prozentsatz, zu dem Ihr investiertes Kapital jedes Jahr wächst.'), buildInputWidget('Anfangskapital', _initialCapitalController, _initialCapitalFocusNode, _isInitialCapitalEntered, '', 'Das Anfangskapital ist der Betrag, den Sie zu Beginn Ihrer Anlage haben.'),
buildInputWidget('Anlagezeitraum', _timeController, _timeFocusNode, _isTimeEntered, 'Jahre', 'Der Anlagezeitraum ist die Zeitspanne, für die Sie planen, Ihr Geld anzulegen.'), buildInputWidget('Monatliche Sparrate', _monthlySavingsRateController, _monthlySavingsRateFocusNode, _isMonthlySavingsRateEntered, '', 'Die monatliche Sparrate ist der Betrag, den Sie jeden Monat zu Ihrer Investition hinzufügen.'),
IntervalWidget( buildInputWidget('Jährlicher Zinssatz', _interestRateController, _interestRateFocusNode, _isInterestRateEntered, '%', 'Der jährliche Zinssatz ist der Prozentsatz, zu dem Ihr investiertes Kapital jedes Jahr wächst.'),
selectedInterval: translateInterval(_payoutInterval), buildInputWidget('Anlagezeitraum', _timeController, _timeFocusNode, _isTimeEntered, 'Jahre', 'Der Anlagezeitraum ist die Zeitspanne, für die Sie planen, Ihr Geld anzulegen.'),
onChanged: (newInterval) { IntervalWidget(
setState(() { selectedInterval: translateInterval(_payoutInterval),
_payoutInterval = newInterval == 'jährlich' ? PayoutInterval.yearly : PayoutInterval.monthly; onChanged: (newInterval) {
}); setState(() {
}, _payoutInterval = newInterval == 'jährlich' ? PayoutInterval.yearly : PayoutInterval.monthly;
), });
ElevatedButton( },
onPressed: () {
setInitialCapital();
setMonthlySavingsRate();
setInterestRate();
setTime();
_calculateInvestedMoney();
_calculateCompoundInterest();
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(CupertinoColors.black),
foregroundColor: MaterialStateProperty.all<Color>(CupertinoColors.white),
), ),
child: const Text( ElevatedButton(
'Berechnen', onPressed: () {
style: TextStyle( setInitialCapital();
fontWeight: FontWeight.bold, setMonthlySavingsRate();
setInterestRate();
setTime();
_calculateInvestedMoney();
_calculateCompoundInterest();
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(CupertinoColors.black),
foregroundColor: MaterialStateProperty.all<Color>(CupertinoColors.white),
),
child: const Text(
'Berechnen',
style: TextStyle(
fontWeight: FontWeight.bold,
),
), ),
), ),
), const SizedBox(height: 20),
const SizedBox(height: 20), if(_calculationPerformed)
if(_calculationPerformed) buildResultWidget('$_compoundInterest', '$_investedMoney', '$_time', '$_monthlySavingsRate', '$_interestRate', _payoutInterval, _investedMoneyList, _compoundInterestList),
buildResultWidget('$_compoundInterest', '$_investedMoney', '$_time', '$_monthlySavingsRate', '$_interestRate', _payoutInterval, _investedMoneyList, _compoundInterestList), ],
),
], )
),
) )
) )
); );

View File

@ -15,6 +15,10 @@ class StackedColumnChart extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SfCartesianChart( return SfCartesianChart(
legend: const Legend(
isVisible: true, // Legende anzeigen
position: LegendPosition.top, // Legende unterhalb des Diagramms platzieren
),
primaryXAxis: const CategoryAxis( primaryXAxis: const CategoryAxis(
title: AxisTitle(text: 'Jahr'), title: AxisTitle(text: 'Jahr'),
), ),
@ -27,6 +31,7 @@ class StackedColumnChart extends StatelessWidget {
dataSource: _getLowerChartData(), dataSource: _getLowerChartData(),
xValueMapper: (SalesData sales, _) => sales.year, xValueMapper: (SalesData sales, _) => sales.year,
yValueMapper: (SalesData sales, _) => sales.value, yValueMapper: (SalesData sales, _) => sales.value,
name: 'Einzahlungen',
color: CupertinoColors.systemRed.highContrastColor, color: CupertinoColors.systemRed.highContrastColor,
), ),
// Obere Teil der Säule // Obere Teil der Säule
@ -34,9 +39,17 @@ class StackedColumnChart extends StatelessWidget {
dataSource: _getUpperChartData(), dataSource: _getUpperChartData(),
xValueMapper: (SalesData sales, _) => sales.year, xValueMapper: (SalesData sales, _) => sales.year,
yValueMapper: (SalesData sales, _) => sales.value, yValueMapper: (SalesData sales, _) => sales.value,
name: 'Zinsen',
color: CupertinoColors.systemBlue.highContrastColor, color: CupertinoColors.systemBlue.highContrastColor,
), ),
], ],
// Trackball für Hover-Interaktion
trackballBehavior: TrackballBehavior(
enable: true,
tooltipSettings: const InteractiveTooltip(enable: true),
activationMode: ActivationMode.singleTap,
hideDelay: 2000,
),
); );
} }

View File

@ -24,6 +24,15 @@ Widget buildInputWidget(String label, TextEditingController controller, FocusNod
const SizedBox(width: 5), const SizedBox(width: 5),
Tooltip( Tooltip(
message: tooltipText, message: tooltipText,
triggerMode: TooltipTriggerMode.tap,
decoration: const BoxDecoration(
color: CupertinoColors.black,
borderRadius: BorderRadius.all(Radius.circular(5))
),
textStyle: const TextStyle(
color: CupertinoColors.white,
),
margin: const EdgeInsets.all(20),
child: const Icon(CupertinoIcons.question_circle_fill, size: 15), child: const Icon(CupertinoIcons.question_circle_fill, size: 15),
), ),
], ],

View File

@ -36,6 +36,15 @@ class _IntervalWidgetState extends State<IntervalWidget> {
SizedBox(width: 5), SizedBox(width: 5),
Tooltip( Tooltip(
message: 'Das Ausschüttungsintervall bezieht sich darauf, wie oft die Erträge aus Ihrer Investition ausgeschüttet werden.', message: 'Das Ausschüttungsintervall bezieht sich darauf, wie oft die Erträge aus Ihrer Investition ausgeschüttet werden.',
triggerMode: TooltipTriggerMode.tap,
decoration: BoxDecoration(
color: CupertinoColors.black,
borderRadius: BorderRadius.all(Radius.circular(5))
),
textStyle: TextStyle(
color: CupertinoColors.white,
),
margin: EdgeInsets.all(20),
child: Icon(CupertinoIcons.question_circle_fill, size: 15), child: Icon(CupertinoIcons.question_circle_fill, size: 15),
), ),
] ]

View File

@ -8,16 +8,16 @@ import 'package:flutter_application_1/widgets/chart_widget.dart';
Widget buildResultWidget(String compoundInterest, String investedMoney, String time, String monthlySavingsRate, String interestRate, PayoutInterval payoutInterval, List<double> investedMoneyList, List<double> compoundInterestList) { Widget buildResultWidget(String compoundInterest, String investedMoney, String time, String monthlySavingsRate, String interestRate, PayoutInterval payoutInterval, List<double> investedMoneyList, List<double> compoundInterestList) {
// Liste von Meilensteinen mit Werten, Bildern und Texten. // Liste von Meilensteinen mit Werten, Bildern und Texten.
List<Map<String, dynamic>> milestoneList = [ List<Map<String, dynamic>> milestoneList = [
{'value': 1329, 'image': 'iphone-15-pro-model.png', 'text': 'iPhone 15 Pro Max\nPreis: 1329€', 'size': 20}, {'value': 1329.0, 'emoji': '📱', 'text': 'iPhone 15 Pro Max\nPreis: 1329€'},
{'value': 3071, 'image': 'flyer-gotour6-model.png', 'text': 'Flyer Gotour6 3.40\nPreis: 3071€', 'size': 70}, {'value': 3071.0, 'emoji': '🚲', 'text': 'Flyer Gotour6 3.40\nPreis: 3071€'},
{'value': 248157, 'image': 'porsche-model.png', 'text': 'Porsche 992 GT3 RS\nPreis: 248157€', 'size': 100}, {'value': 248157.0, 'emoji': '🏎️', 'text': 'Porsche 992 GT3 RS\nPreis: 248157€'},
{'value': 450000, 'image': 'real-estate-model.png', 'text': '150qm Einfamilienhaus\nPreis: ca. 450000€', 'size': 100}, {'value': 450000.0, 'emoji': '🏡', 'text': '150qm Einfamilienhaus\nPreis: ca. 450000€'},
]; ];
return Column( return Column(
children: <Widget>[ children: <Widget>[
_buildResultRow('Endkapital:', '$compoundInterest'), _buildResultRow('Endkapital:', '$compoundInterest'),
_buildResultRow('Gesamte Einzahlungen:', '$investedMoney'), _buildResultRow('Einzahlungen:', '$investedMoney'),
_buildResultRow('Erhaltene Zinszahlungen:', '${double.parse(compoundInterest) - double.parse(investedMoney)}'), _buildResultRow('Erhaltene Zinsen:', '${double.parse(compoundInterest) - double.parse(investedMoney)}'),
const SizedBox(height: 20), const SizedBox(height: 20),
Text( Text(
'Wenn du über einen Zeitraum von $time Jahren ${translateInterval(payoutInterval)} $monthlySavingsRate€ mit einem Zinssatz von $interestRate% investierst, erreichst du am Ende ein Endkapital von $compoundInterest€. Dieses setzt sich aus Einzahlungen von $investedMoney€ und Zinsen bzw. Kapitalerträgen in Höhe von ${double.parse(compoundInterest) - double.parse(investedMoney)}€ zusammen.' 'Wenn du über einen Zeitraum von $time Jahren ${translateInterval(payoutInterval)} $monthlySavingsRate€ mit einem Zinssatz von $interestRate% investierst, erreichst du am Ende ein Endkapital von $compoundInterest€. Dieses setzt sich aus Einzahlungen von $investedMoney€ und Zinsen bzw. Kapitalerträgen in Höhe von ${double.parse(compoundInterest) - double.parse(investedMoney)}€ zusammen.'
@ -41,20 +41,27 @@ Widget buildResultWidget(String compoundInterest, String investedMoney, String t
Widget _buildResultRow(String label, String value) { Widget _buildResultRow(String label, String value) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 100), padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Flexible(
label, child: Text(
style: const TextStyle( label,
fontWeight: FontWeight.bold, textAlign: TextAlign.start,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
), ),
), ),
Text( const SizedBox(width: 20),
value, Flexible(
style: const TextStyle( child: Text(
fontWeight: FontWeight.bold, value,
textAlign: TextAlign.end,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
), ),
), ),
], ],
@ -62,6 +69,8 @@ Widget _buildResultRow(String label, String value) {
); );
} }
// Erstellt die Meilenstein-Timeline. // Erstellt die Meilenstein-Timeline.
Widget buildMilestoneTimeline(List<Map<String, dynamic>> milestones, double totalInterest) { Widget buildMilestoneTimeline(List<Map<String, dynamic>> milestones, double totalInterest) {
return SizedBox( return SizedBox(
@ -71,13 +80,12 @@ Widget buildMilestoneTimeline(List<Map<String, dynamic>> milestones, double tota
children: List.generate(milestones.length, (index) { children: List.generate(milestones.length, (index) {
// Werte aus dem Meilenstein-Objekt extrahieren // Werte aus dem Meilenstein-Objekt extrahieren
double milestoneValue = milestones[index]['value']; double milestoneValue = milestones[index]['value'];
String milestoneImage = milestones[index]['image']; String milestoneEmoji = milestones[index]['emoji'];
String milestoneText = milestones[index]['text']; String milestoneText = milestones[index]['text'];
double milestoneSize = milestones[index]['size'];
bool milestoneReached = totalInterest >= milestoneValue; bool milestoneReached = totalInterest >= milestoneValue;
return Column( return Column(
children: [ children: [
if (index < milestones.length && index > 0) // Zeigt eine vertikale Linie an zwischen den Meilensteinen if (index > 0) // Zeigt eine vertikale Linie an zwischen den Meilensteinen
Container( Container(
height: 50, height: 50,
width: 2, width: 2,
@ -87,10 +95,9 @@ Widget buildMilestoneTimeline(List<Map<String, dynamic>> milestones, double tota
padding: const EdgeInsets.symmetric(vertical: 4.0), padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Column( child: Column(
children: [ children: [
Image.asset( Text(
'assets/images/$milestoneImage', milestoneEmoji,
width: milestoneSize, style: const TextStyle(fontSize: 25),
fit: BoxFit.cover,
), ),
const SizedBox(height: 5), const SizedBox(height: 5),
Text( Text(

View File

@ -188,18 +188,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: syncfusion_flutter_charts name: syncfusion_flutter_charts
sha256: "75253e2d32ce2e739c8efe0aec52668334d880e2a96f6d0d2b4422f49af7c608" sha256: "01ff26c73725cb4f9d04492daae4d0365c2ed2cbf43cd83132a24f47d4d4931c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "25.1.42+1" version: "25.2.4"
syncfusion_flutter_core: syncfusion_flutter_core:
dependency: transitive dependency: transitive
description: description:
name: syncfusion_flutter_core name: syncfusion_flutter_core
sha256: fc61878342caee6da7979ca0ca9bb178f1eb5ca9b836dada34b83caa39e4bfed sha256: "9e8bac2367d03bd902844f59e866c0e7b1e10b0b8fa5a6a926c0d8ae8c344533"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "25.1.42" version: "25.2.4"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:

View File

@ -60,11 +60,8 @@ flutter:
uses-material-design: true uses-material-design: true
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
assets: # assets:
- assets/images/iphone-15-pro-model.png # - assets/images/
- assets/images/flyer-gotour6-model.png
- assets/images/porsche-model.png
- assets/images/real-estate-model.png
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware # https://flutter.dev/assets-and-images/#resolution-aware