2024-05-06 17:28:10 +02:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/cupertino.dart';
|
2024-06-14 15:54:55 +02:00
|
|
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
|
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
2024-05-24 14:04:26 +02:00
|
|
|
import 'package:flutter_application_1/calculator.dart';
|
2024-05-06 17:28:10 +02:00
|
|
|
import 'package:flutter_application_1/enums.dart';
|
|
|
|
import 'package:flutter_application_1/utils.dart';
|
|
|
|
import 'package:flutter_application_1/widgets/input_widget.dart';
|
|
|
|
import 'package:flutter_application_1/widgets/interval_widget.dart';
|
|
|
|
import 'package:flutter_application_1/widgets/result_widget.dart';
|
2024-06-12 12:48:30 +02:00
|
|
|
import 'package:flutter_application_1/widgets/error_widget.dart';
|
2024-06-14 15:54:55 +02:00
|
|
|
import 'package:flutter_application_1/widgets/language_switcher_widget.dart';
|
2024-05-06 17:28:10 +02:00
|
|
|
|
|
|
|
void main() {
|
|
|
|
runApp(const MyApp());
|
|
|
|
}
|
|
|
|
|
2024-06-14 15:54:55 +02:00
|
|
|
class MyApp extends StatefulWidget {
|
2024-05-06 17:28:10 +02:00
|
|
|
const MyApp({super.key});
|
|
|
|
|
2024-06-14 15:54:55 +02:00
|
|
|
@override
|
|
|
|
MyAppState createState() => MyAppState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class MyAppState extends State<MyApp> {
|
|
|
|
Locale _locale = const Locale('de'); // Standard-Locale
|
|
|
|
|
|
|
|
void _changeLanguage(Locale locale) {
|
|
|
|
setState(() {
|
|
|
|
_locale = locale;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-05-06 17:28:10 +02:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return MaterialApp(
|
2024-06-14 15:54:55 +02:00
|
|
|
title: 'Compound interest calculator',
|
|
|
|
localizationsDelegates: const [
|
|
|
|
AppLocalizations.delegate,
|
|
|
|
GlobalMaterialLocalizations.delegate,
|
|
|
|
GlobalWidgetsLocalizations.delegate,
|
|
|
|
GlobalCupertinoLocalizations.delegate,
|
|
|
|
],
|
|
|
|
supportedLocales: const [
|
|
|
|
Locale('en'), // English
|
|
|
|
Locale('de'), // Deutsch
|
|
|
|
],
|
|
|
|
locale: _locale,
|
2024-05-06 17:28:10 +02:00
|
|
|
theme: ThemeData(
|
|
|
|
colorScheme: ColorScheme.fromSeed(seedColor: CupertinoColors.white, background: CupertinoColors.white),
|
|
|
|
useMaterial3: true,
|
|
|
|
),
|
2024-06-14 15:54:55 +02:00
|
|
|
home: MyHomePage(
|
|
|
|
title: 'Compound interest calculator',
|
|
|
|
onLocaleChanged: _changeLanguage, // Übergibt die Sprachwechsel-Funktion an die Startseite
|
|
|
|
),
|
2024-05-06 17:28:10 +02:00
|
|
|
debugShowCheckedModeBanner: false,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class MyHomePage extends StatefulWidget {
|
2024-06-14 15:54:55 +02:00
|
|
|
const MyHomePage({super.key, required this.title, required this.onLocaleChanged});
|
2024-05-06 17:28:10 +02:00
|
|
|
final String title;
|
2024-06-14 15:54:55 +02:00
|
|
|
final Function(Locale) onLocaleChanged;
|
2024-05-06 17:28:10 +02:00
|
|
|
|
|
|
|
@override
|
|
|
|
State<MyHomePage> createState() => _MyHomePageState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _MyHomePageState extends State<MyHomePage> {
|
2024-05-10 13:37:09 +02:00
|
|
|
// Controller und Fokus-Knoten für die Eingabefelder
|
2024-05-06 17:28:10 +02:00
|
|
|
final TextEditingController _initialCapitalController = TextEditingController();
|
|
|
|
final TextEditingController _monthlySavingsRateController = TextEditingController();
|
|
|
|
final TextEditingController _interestRateController = TextEditingController();
|
|
|
|
final TextEditingController _timeController = TextEditingController();
|
|
|
|
final FocusNode _initialCapitalFocusNode = FocusNode();
|
|
|
|
final FocusNode _monthlySavingsRateFocusNode = FocusNode();
|
|
|
|
final FocusNode _interestRateFocusNode = FocusNode();
|
|
|
|
final FocusNode _timeFocusNode = FocusNode();
|
|
|
|
|
2024-05-10 13:37:09 +02:00
|
|
|
// Zustandsvariablen für die Validierung der Eingaben
|
|
|
|
bool _isInitialCapitalEntered = false;
|
|
|
|
bool _isMonthlySavingsRateEntered = false;
|
|
|
|
bool _isInterestRateEntered = false;
|
|
|
|
bool _isTimeEntered = false;
|
|
|
|
|
2024-05-06 17:28:10 +02:00
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
|
2024-05-10 13:37:09 +02:00
|
|
|
// Listener für die Eingabefelder, um die Validierung durchzuführen und Standardwerte wiederherzustellen
|
2024-05-06 17:28:10 +02:00
|
|
|
_initialCapitalController.addListener(() {
|
|
|
|
setState(() {
|
|
|
|
_isInitialCapitalEntered = _initialCapitalController.text.isNotEmpty &&
|
|
|
|
isNumeric(_initialCapitalController.text);
|
|
|
|
restoreDefaultValuesIfEmpty(_initialCapitalController);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
_monthlySavingsRateController.addListener(() {
|
|
|
|
setState(() {
|
|
|
|
_isMonthlySavingsRateEntered = _monthlySavingsRateController.text.isNotEmpty &&
|
|
|
|
isNumeric(_monthlySavingsRateController.text);
|
|
|
|
restoreDefaultValuesIfEmpty(_monthlySavingsRateController);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
_interestRateController.addListener(() {
|
|
|
|
setState(() {
|
|
|
|
_isInterestRateEntered = _interestRateController.text.isNotEmpty &&
|
|
|
|
isNumeric(_interestRateController.text);
|
|
|
|
restoreDefaultValuesIfEmpty(_interestRateController);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
_timeController.addListener(() {
|
|
|
|
setState(() {
|
|
|
|
_isTimeEntered = _timeController.text.isNotEmpty &&
|
|
|
|
isNumeric(_timeController.text);
|
|
|
|
restoreDefaultValuesIfEmpty(_timeController);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2024-05-10 13:37:09 +02:00
|
|
|
// Listener für die Fokus-Knoten, um die Eingabe zu runden, wenn der Fokus verloren geht
|
2024-05-06 17:28:10 +02:00
|
|
|
_initialCapitalFocusNode.addListener(() {
|
|
|
|
if (!_initialCapitalFocusNode.hasFocus) {
|
|
|
|
roundToInteger(_initialCapitalController);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
_monthlySavingsRateFocusNode.addListener(() {
|
|
|
|
if (!_monthlySavingsRateFocusNode.hasFocus) {
|
|
|
|
roundToInteger(_monthlySavingsRateController);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
_interestRateFocusNode.addListener(() {
|
|
|
|
if (!_interestRateFocusNode.hasFocus) {
|
|
|
|
roundToInteger(_interestRateController);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
_timeFocusNode.addListener(() {
|
|
|
|
if (!_timeFocusNode.hasFocus) {
|
|
|
|
roundToInteger(_timeController);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-05-10 13:37:09 +02:00
|
|
|
// Setzen von Beispielwerten für die Eingabefelder
|
|
|
|
_initialCapitalController.text = '1000';
|
|
|
|
_monthlySavingsRateController.text = '50';
|
|
|
|
_interestRateController.text = '5';
|
|
|
|
_timeController.text = '10';
|
2024-05-06 17:28:10 +02:00
|
|
|
}
|
|
|
|
|
2024-05-24 14:04:26 +02:00
|
|
|
double _initialCapital = 0.0; // Das anfängliche Kapital
|
|
|
|
double _monthlySavingsRate = 0.0; // Der monatliche Sparbetrag
|
|
|
|
double _interestRate = 0.0; // Der jährliche Zinssatz
|
|
|
|
double _time = 0.0; // Der Anlagezeitraum in Jahren
|
|
|
|
double _investedMoney = 0.0; // Das investierte Geld
|
|
|
|
final List<double> _investedMoneyList = []; // Liste, die das investierte Geld pro Jahr speichert
|
|
|
|
PayoutInterval _payoutInterval = PayoutInterval.yearly; // Das Auszahlungsintervall (jährlich oder monatlich)
|
|
|
|
double _compoundInterest = 0.0; // Der Zinseszins
|
|
|
|
final List<double> _compoundInterestList = []; // Liste, die den Zinseszins pro Jahr speichert
|
2024-05-06 17:28:10 +02:00
|
|
|
|
2024-05-10 13:37:09 +02:00
|
|
|
// Methoden zum Festlegen der Werte aus den Eingabefeldern
|
2024-05-06 17:28:10 +02:00
|
|
|
void setInitialCapital() {
|
|
|
|
setState(() {
|
|
|
|
_initialCapital = double.parse(_initialCapitalController.text);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void setMonthlySavingsRate() {
|
|
|
|
setState(() {
|
|
|
|
_monthlySavingsRate = double.parse(_monthlySavingsRateController.text);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void setInterestRate() {
|
|
|
|
setState(() {
|
|
|
|
_interestRate = double.parse(_interestRateController.text);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void setTime() {
|
|
|
|
setState(() {
|
|
|
|
_time = double.parse(_timeController.text);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-06-12 12:48:30 +02:00
|
|
|
CalculationPerformed _isCalculated = CalculationPerformed.noFirstTimeItLoaded;
|
2024-05-06 17:28:10 +02:00
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2024-06-14 15:54:55 +02:00
|
|
|
final localizations = AppLocalizations.of(context)!;
|
|
|
|
|
2024-05-06 17:28:10 +02:00
|
|
|
return Scaffold(
|
2024-05-20 16:34:31 +02:00
|
|
|
body: SafeArea(
|
2024-06-14 15:54:55 +02:00
|
|
|
child: Stack(
|
2024-06-15 13:35:59 +02:00
|
|
|
children: [
|
|
|
|
SingleChildScrollView(
|
|
|
|
physics: const BouncingScrollPhysics(),
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.all(20.0),
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
children: <Widget>[
|
|
|
|
InputWidget(
|
|
|
|
label: localizations.initial_capital,
|
|
|
|
controller: _initialCapitalController,
|
|
|
|
focusNode: _initialCapitalFocusNode,
|
|
|
|
isValid: _isInitialCapitalEntered,
|
|
|
|
suffixText: localizations.currency,
|
|
|
|
tooltipText: localizations.initial_capital_tooltiptext
|
|
|
|
),
|
|
|
|
InputWidget(
|
|
|
|
label: localizations.monthly_savings_rate,
|
|
|
|
controller: _monthlySavingsRateController,
|
|
|
|
focusNode: _monthlySavingsRateFocusNode,
|
|
|
|
isValid: _isMonthlySavingsRateEntered,
|
|
|
|
suffixText: localizations.currency,
|
|
|
|
tooltipText: localizations.monthly_savings_rate_tooltiptext
|
|
|
|
),
|
|
|
|
InputWidget(
|
|
|
|
label: localizations.interest_rate,
|
|
|
|
controller: _interestRateController,
|
|
|
|
focusNode: _interestRateFocusNode,
|
|
|
|
isValid: _isInterestRateEntered,
|
|
|
|
suffixText: '%',
|
|
|
|
tooltipText: localizations.interest_rate_tooltiptext
|
2024-05-24 14:04:26 +02:00
|
|
|
),
|
2024-06-15 13:35:59 +02:00
|
|
|
InputWidget(
|
|
|
|
label: localizations.investment_period,
|
|
|
|
controller: _timeController,
|
|
|
|
focusNode: _timeFocusNode,
|
|
|
|
isValid: _isTimeEntered,
|
|
|
|
suffixText: localizations.years,
|
|
|
|
tooltipText: localizations.investment_period_tooltiptext
|
2024-05-20 16:34:31 +02:00
|
|
|
),
|
2024-06-15 13:35:59 +02:00
|
|
|
IntervalWidget(
|
|
|
|
selectedInterval: _payoutInterval == PayoutInterval.yearly ? localizations.yearly : localizations.monthly,
|
|
|
|
onChanged: (newInterval) {
|
|
|
|
setState(() {
|
|
|
|
_payoutInterval = newInterval == localizations.yearly ? PayoutInterval.yearly : PayoutInterval.monthly;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
),
|
|
|
|
ElevatedButton(
|
|
|
|
onPressed: () {
|
|
|
|
if (_isInitialCapitalEntered &&
|
|
|
|
_isMonthlySavingsRateEntered &&
|
|
|
|
_isInterestRateEntered &&
|
|
|
|
_isTimeEntered) {
|
|
|
|
setInitialCapital();
|
|
|
|
setMonthlySavingsRate();
|
|
|
|
setInterestRate();
|
|
|
|
setTime();
|
|
|
|
_investedMoney = calculateInvestedMoney(_initialCapital, _monthlySavingsRate, _time, _investedMoneyList);
|
|
|
|
_compoundInterest = calculateCompoundInterest(
|
|
|
|
_initialCapital,
|
|
|
|
_monthlySavingsRate,
|
|
|
|
_interestRate,
|
|
|
|
_time,
|
|
|
|
_payoutInterval,
|
|
|
|
_investedMoneyList,
|
|
|
|
_compoundInterestList
|
|
|
|
);
|
|
|
|
_isCalculated = CalculationPerformed.yes;
|
|
|
|
} else {
|
|
|
|
_isCalculated = CalculationPerformed.no;
|
|
|
|
}
|
|
|
|
setState(() {});
|
|
|
|
},
|
|
|
|
style: ButtonStyle(
|
|
|
|
backgroundColor: MaterialStateProperty.all<Color>(CupertinoColors.black),
|
|
|
|
foregroundColor: MaterialStateProperty.all<Color>(CupertinoColors.white),
|
|
|
|
shape: MaterialStateProperty.all<RoundedRectangleBorder>(
|
|
|
|
RoundedRectangleBorder(
|
|
|
|
borderRadius: BorderRadius.circular(5),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
child: Text(
|
|
|
|
localizations.calculate,
|
|
|
|
style: const TextStyle(
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
if(_isCalculated == CalculationPerformed.yes)
|
|
|
|
ResultWidget(
|
|
|
|
compoundInterest: _compoundInterest.toStringAsFixed(0),
|
|
|
|
investedMoney: _investedMoney.toStringAsFixed(0),
|
|
|
|
time: _time.toStringAsFixed(0),
|
|
|
|
monthlySavingsRate: _monthlySavingsRate.toStringAsFixed(0),
|
|
|
|
interestRate: _interestRate.toStringAsFixed(0),
|
|
|
|
payoutInterval: _payoutInterval,
|
|
|
|
investedMoneyList: _investedMoneyList,
|
|
|
|
compoundInterestList: _compoundInterestList,
|
|
|
|
),
|
|
|
|
if(_isCalculated == CalculationPerformed.no)
|
|
|
|
ErrWidget(
|
|
|
|
errorMessage: localizations.invalid_input,
|
|
|
|
),
|
|
|
|
],
|
2024-05-06 17:28:10 +02:00
|
|
|
),
|
2024-06-15 13:35:59 +02:00
|
|
|
),
|
2024-05-20 16:34:31 +02:00
|
|
|
),
|
2024-06-15 13:35:59 +02:00
|
|
|
Positioned(
|
2024-06-14 15:54:55 +02:00
|
|
|
top: MediaQuery.of(context).size.height / 2 - 20,
|
|
|
|
right: 10,
|
|
|
|
child: LanguageSwitcher(
|
|
|
|
currentLocale: Localizations.localeOf(context),
|
|
|
|
onLocaleChanged: widget.onLocaleChanged,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
2024-05-06 17:28:10 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|