Added internationalization, Updated tests
parent
26c8cd977e
commit
daa79922ea
|
@ -0,0 +1,3 @@
|
||||||
|
arb-dir: lib/l10n
|
||||||
|
template-arb-file: app_de.arb
|
||||||
|
output-localization-file: app_localizations.dart
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"title": "Zinseszinsrechner",
|
||||||
|
"language_english": "Englisch",
|
||||||
|
"language_german": "Deutsch",
|
||||||
|
"choose_language": "Sprache auswählen",
|
||||||
|
"initial_capital": "Anfangskapital",
|
||||||
|
"initial_capital_tooltiptext": "Das Anfangskapital ist der Betrag, den Sie zu Beginn Ihrer Anlage haben.",
|
||||||
|
"currency": "€",
|
||||||
|
"amount": "{value}€",
|
||||||
|
"@amount": {
|
||||||
|
"value": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"monthly_savings_rate": "Monatliche Sparrate",
|
||||||
|
"monthly_savings_rate_tooltiptext": "Die monatliche Sparrate ist der Betrag, den Sie jeden Monat zu Ihrer Investition hinzufügen.",
|
||||||
|
"interest_rate": "Jährlicher Zinssatz",
|
||||||
|
"interest_rate_tooltiptext": "Der jährliche Zinssatz ist der Prozentsatz, zu dem Ihr investiertes Kapital jedes Jahr wächst.",
|
||||||
|
"investment_period": "Anlagezeitraum",
|
||||||
|
"investment_period_tooltiptext": "Der Anlagezeitraum ist die Zeitspanne, für die Sie planen, Ihr Geld anzulegen.",
|
||||||
|
"years": "Jahre",
|
||||||
|
"yearly": "jährlich",
|
||||||
|
"monthly": "monatlich",
|
||||||
|
"calculate": "Berechnen",
|
||||||
|
"invalid_input": "Ungültige Eingabe",
|
||||||
|
"milestone_text1": "Smartphone\nPreis:",
|
||||||
|
"milestone_text2": "eBike\nPreis:",
|
||||||
|
"milestone_text3": "Weltreise\nPreis:",
|
||||||
|
"milestone_text4": "Sportwagen\nPreis:",
|
||||||
|
"milestone_text5": "150qm Einfamilienhaus\nPreis:",
|
||||||
|
"final_capital": "Endkapital",
|
||||||
|
"deposits": "Einzahlungen",
|
||||||
|
"interest_received": "Erhaltene Zinsen",
|
||||||
|
"result_text": "Wenn du über einen Zeitraum von {investment_period} Jahren monatlich {monthly_savings_rate}€ mit einem Zinssatz von {interest_rate}% investierst, erreichst du am Ende ein Endkapital von {final_capital}€. Dieses setzt sich aus Einzahlungen von {invested_money}€ und Zinsen bzw. Kapitalerträgen in Höhe von {compound_interest}€ zusammen.",
|
||||||
|
"@result_text": {
|
||||||
|
"investment_period": {
|
||||||
|
"type" : "int"
|
||||||
|
},
|
||||||
|
"monthly_savings_rate": {
|
||||||
|
"type" : "int"
|
||||||
|
},
|
||||||
|
"interest_rate": {
|
||||||
|
"type" : "int"
|
||||||
|
},
|
||||||
|
"final_capital": {
|
||||||
|
"type" : "int"
|
||||||
|
},
|
||||||
|
"invested_money": {
|
||||||
|
"type" : "int"
|
||||||
|
},
|
||||||
|
"compound_interest": {
|
||||||
|
"type" : "int"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"graphic": "Grafik",
|
||||||
|
"milestones": "Meilensteine",
|
||||||
|
"payout_interval": "Ausschüttungsintervall",
|
||||||
|
"payout_interval_tooltiptext": "Das Ausschüttungsintervall bezieht sich darauf, wie oft die Erträge aus Ihrer Investition ausgeschüttet werden.",
|
||||||
|
"year": "Jahr",
|
||||||
|
"currency_written_out": "Euro"
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"title": "Compound interest calculator",
|
||||||
|
"language_english": "English",
|
||||||
|
"language_german": "German",
|
||||||
|
"choose_language": "Choose language",
|
||||||
|
"initial_capital": "Initial capital",
|
||||||
|
"initial_capital_tooltiptext": "The initial capital is the amount you have at the start of your investment.",
|
||||||
|
"currency": "$",
|
||||||
|
"amount": "${value}",
|
||||||
|
"monthly_savings_rate": "Monthly savings rate",
|
||||||
|
"monthly_savings_rate_tooltiptext": "The monthly savings rate is the amount you add to your investment each month.",
|
||||||
|
"interest_rate": "Annual interest rate",
|
||||||
|
"interest_rate_tooltiptext": "The annual interest rate is the percentage at which your invested capital grows each year.",
|
||||||
|
"investment_period": "Investment period",
|
||||||
|
"investment_period_tooltiptext": "The investment period is the length of time for which you plan to invest your money.",
|
||||||
|
"years": "Years",
|
||||||
|
"yearly": "yearly",
|
||||||
|
"monthly": "monthly",
|
||||||
|
"calculate": "Calculate",
|
||||||
|
"invalid_input": "Invalid input",
|
||||||
|
"milestone_text1": "Smartphone\nPrice: ",
|
||||||
|
"milestone_text2": "eBike\nPrice: ",
|
||||||
|
"milestone_text3": "World Travel\nPrice: ",
|
||||||
|
"milestone_text4": "Sports car\nPrice: ",
|
||||||
|
"milestone_text5": "150sqm single family house\nPrice: ",
|
||||||
|
"final_capital": "Final capital",
|
||||||
|
"deposits": "Deposits",
|
||||||
|
"interest_received": "Interest received",
|
||||||
|
"result_text": "If you invest ${monthly_savings_rate} per month over a period of {investment_period} years at an interest rate of {interest_rate}%, you will end up with a final capital of ${final_capital}. This consists of deposits of ${invested_money} and interest or capital gains of ${compound_interest}.",
|
||||||
|
"graphic": "Graphic",
|
||||||
|
"milestones": "Milestones",
|
||||||
|
"payout_interval": "Payout interval",
|
||||||
|
"payout_interval_tooltiptext": "The payout interval refers to how often the income from your investment is distributed.",
|
||||||
|
"year": "Year",
|
||||||
|
"currency_written_out": "Dollar"
|
||||||
|
}
|
107
lib/main.dart
107
lib/main.dart
|
@ -1,5 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:flutter_application_1/calculator.dart';
|
import 'package:flutter_application_1/calculator.dart';
|
||||||
import 'package:flutter_application_1/enums.dart';
|
import 'package:flutter_application_1/enums.dart';
|
||||||
import 'package:flutter_application_1/utils.dart';
|
import 'package:flutter_application_1/utils.dart';
|
||||||
|
@ -7,31 +9,60 @@ import 'package:flutter_application_1/widgets/input_widget.dart';
|
||||||
import 'package:flutter_application_1/widgets/interval_widget.dart';
|
import 'package:flutter_application_1/widgets/interval_widget.dart';
|
||||||
import 'package:flutter_application_1/widgets/result_widget.dart';
|
import 'package:flutter_application_1/widgets/result_widget.dart';
|
||||||
import 'package:flutter_application_1/widgets/error_widget.dart';
|
import 'package:flutter_application_1/widgets/error_widget.dart';
|
||||||
|
import 'package:flutter_application_1/widgets/language_switcher_widget.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatefulWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
MyAppState createState() => MyAppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyAppState extends State<MyApp> {
|
||||||
|
Locale _locale = const Locale('de'); // Standard-Locale
|
||||||
|
|
||||||
|
void _changeLanguage(Locale locale) {
|
||||||
|
setState(() {
|
||||||
|
_locale = locale;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'Zinseszinsrechner',
|
title: 'Compound interest calculator',
|
||||||
|
localizationsDelegates: const [
|
||||||
|
AppLocalizations.delegate,
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
supportedLocales: const [
|
||||||
|
Locale('en'), // English
|
||||||
|
Locale('de'), // Deutsch
|
||||||
|
],
|
||||||
|
locale: _locale,
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
colorScheme: ColorScheme.fromSeed(seedColor: CupertinoColors.white, background: CupertinoColors.white),
|
colorScheme: ColorScheme.fromSeed(seedColor: CupertinoColors.white, background: CupertinoColors.white),
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
home: const MyHomePage(title: 'Zinseszinsrechner',),
|
home: MyHomePage(
|
||||||
|
title: 'Compound interest calculator',
|
||||||
|
onLocaleChanged: _changeLanguage, // Übergibt die Sprachwechsel-Funktion an die Startseite
|
||||||
|
),
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
class MyHomePage extends StatefulWidget {
|
||||||
const MyHomePage({super.key, required this.title});
|
const MyHomePage({super.key, required this.title, required this.onLocaleChanged});
|
||||||
final String title;
|
final String title;
|
||||||
|
final Function(Locale) onLocaleChanged;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MyHomePage> createState() => _MyHomePageState();
|
State<MyHomePage> createState() => _MyHomePageState();
|
||||||
|
@ -162,9 +193,13 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final localizations = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: SingleChildScrollView(
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
SingleChildScrollView(
|
||||||
physics: const BouncingScrollPhysics(),
|
physics: const BouncingScrollPhysics(),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
@ -172,42 +207,42 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
InputWidget(
|
InputWidget(
|
||||||
label: 'Anfangskapital',
|
label: localizations.initial_capital,
|
||||||
controller: _initialCapitalController,
|
controller: _initialCapitalController,
|
||||||
focusNode: _initialCapitalFocusNode,
|
focusNode: _initialCapitalFocusNode,
|
||||||
isValid: _isInitialCapitalEntered,
|
isValid: _isInitialCapitalEntered,
|
||||||
suffixText: '€',
|
suffixText: localizations.currency,
|
||||||
tooltipText: 'Das Anfangskapital ist der Betrag, den Sie zu Beginn Ihrer Anlage haben.'
|
tooltipText: localizations.initial_capital_tooltiptext
|
||||||
),
|
),
|
||||||
InputWidget(
|
InputWidget(
|
||||||
label: 'Monatliche Sparrate',
|
label: localizations.monthly_savings_rate,
|
||||||
controller: _monthlySavingsRateController,
|
controller: _monthlySavingsRateController,
|
||||||
focusNode: _monthlySavingsRateFocusNode,
|
focusNode: _monthlySavingsRateFocusNode,
|
||||||
isValid: _isMonthlySavingsRateEntered,
|
isValid: _isMonthlySavingsRateEntered,
|
||||||
suffixText: '€',
|
suffixText: localizations.currency,
|
||||||
tooltipText: 'Die monatliche Sparrate ist der Betrag, den Sie jeden Monat zu Ihrer Investition hinzufügen.'
|
tooltipText: localizations.monthly_savings_rate_tooltiptext
|
||||||
),
|
),
|
||||||
InputWidget(
|
InputWidget(
|
||||||
label: 'Jährlicher Zinssatz',
|
label: localizations.interest_rate,
|
||||||
controller: _interestRateController,
|
controller: _interestRateController,
|
||||||
focusNode: _interestRateFocusNode,
|
focusNode: _interestRateFocusNode,
|
||||||
isValid: _isInterestRateEntered,
|
isValid: _isInterestRateEntered,
|
||||||
suffixText: '%',
|
suffixText: '%',
|
||||||
tooltipText: 'Der jährliche Zinssatz ist der Prozentsatz, zu dem Ihr investiertes Kapital jedes Jahr wächst.'
|
tooltipText: localizations.interest_rate_tooltiptext
|
||||||
),
|
),
|
||||||
InputWidget(
|
InputWidget(
|
||||||
label: 'Anlagezeitraum',
|
label: localizations.investment_period,
|
||||||
controller: _timeController,
|
controller: _timeController,
|
||||||
focusNode: _timeFocusNode,
|
focusNode: _timeFocusNode,
|
||||||
isValid: _isTimeEntered,
|
isValid: _isTimeEntered,
|
||||||
suffixText: 'Jahre',
|
suffixText: localizations.years,
|
||||||
tooltipText: 'Der Anlagezeitraum ist die Zeitspanne, für die Sie planen, Ihr Geld anzulegen.'
|
tooltipText: localizations.investment_period_tooltiptext
|
||||||
),
|
),
|
||||||
IntervalWidget(
|
IntervalWidget(
|
||||||
selectedInterval: translateInterval(_payoutInterval),
|
selectedInterval: _payoutInterval == PayoutInterval.yearly ? localizations.yearly : localizations.monthly,
|
||||||
onChanged: (newInterval) {
|
onChanged: (newInterval) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_payoutInterval = newInterval == 'jährlich' ? PayoutInterval.yearly : PayoutInterval.monthly;
|
_payoutInterval = newInterval == localizations.yearly ? PayoutInterval.yearly : PayoutInterval.monthly;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -246,9 +281,9 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: const Text(
|
child: Text(
|
||||||
'Berechnen',
|
localizations.calculate,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -256,24 +291,34 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
if(_isCalculated == CalculationPerformed.yes)
|
if(_isCalculated == CalculationPerformed.yes)
|
||||||
ResultWidget(
|
ResultWidget(
|
||||||
compoundInterest: '$_compoundInterest',
|
compoundInterest: _compoundInterest.toStringAsFixed(0),
|
||||||
investedMoney: '$_investedMoney',
|
investedMoney: _investedMoney.toStringAsFixed(0),
|
||||||
time: '$_time',
|
time: _time.toStringAsFixed(0),
|
||||||
monthlySavingsRate: '$_monthlySavingsRate',
|
monthlySavingsRate: _monthlySavingsRate.toStringAsFixed(0),
|
||||||
interestRate: '$_interestRate',
|
interestRate: _interestRate.toStringAsFixed(0),
|
||||||
payoutInterval: _payoutInterval,
|
payoutInterval: _payoutInterval,
|
||||||
investedMoneyList: _investedMoneyList,
|
investedMoneyList: _investedMoneyList,
|
||||||
compoundInterestList: _compoundInterestList,
|
compoundInterestList: _compoundInterestList,
|
||||||
),
|
),
|
||||||
if(_isCalculated == CalculationPerformed.no)
|
if(_isCalculated == CalculationPerformed.no)
|
||||||
const ErrWidget(
|
ErrWidget(
|
||||||
errorMessage: 'Ungültige Eingabe',
|
errorMessage: localizations.invalid_input,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
Positioned(
|
||||||
|
top: MediaQuery.of(context).size.height / 2 - 20,
|
||||||
|
right: 10,
|
||||||
|
child: LanguageSwitcher(
|
||||||
|
currentLocale: Localizations.localeOf(context),
|
||||||
|
onLocaleChanged: widget.onLocaleChanged,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:flutter_application_1/widgets/chart_widget.dart';
|
import 'package:flutter_application_1/widgets/chart_widget.dart';
|
||||||
|
|
||||||
// Widget für die Seite, die das gestapelte Säulendiagramm anzeigt
|
// Widget für die Seite, die das gestapelte Säulendiagramm anzeigt
|
||||||
|
@ -15,6 +16,8 @@ class ChartPage extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final localizations = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: CustomScrollView(
|
body: CustomScrollView(
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
|
@ -34,9 +37,9 @@ class ChartPage extends StatelessWidget {
|
||||||
Navigator.pop(context); // Zurück zur vorherigen Seite
|
Navigator.pop(context); // Zurück zur vorherigen Seite
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Text(
|
Text(
|
||||||
'Grafik',
|
localizations.graphic,
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 40),
|
const SizedBox(width: 40),
|
||||||
],
|
],
|
||||||
|
@ -78,16 +81,16 @@ class ChartPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
// Tabellenkopf
|
// Tabellenkopf
|
||||||
const TableRow(
|
TableRow(
|
||||||
decoration: BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: CupertinoColors.darkBackgroundGray,
|
color: CupertinoColors.black,
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Jahr',
|
localizations.year,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
color: CupertinoColors.white,
|
color: CupertinoColors.white,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
|
@ -95,10 +98,10 @@ class ChartPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Einzahlungen',
|
localizations.deposits,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
color: CupertinoColors.white,
|
color: CupertinoColors.white,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
|
@ -106,10 +109,10 @@ class ChartPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Zinsen',
|
localizations.interest_received,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
color: CupertinoColors.white,
|
color: CupertinoColors.white,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
|
@ -117,10 +120,10 @@ class ChartPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Endkapital',
|
localizations.final_capital,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
color: CupertinoColors.white,
|
color: CupertinoColors.white,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
|
@ -144,7 +147,7 @@ class ChartPage extends StatelessWidget {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'€${investedMoneyList[i]}',
|
localizations.amount(investedMoneyList[i]),
|
||||||
style: const TextStyle(color: CupertinoColors.white),
|
style: const TextStyle(color: CupertinoColors.white),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
|
@ -152,7 +155,7 @@ class ChartPage extends StatelessWidget {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'€${compoundInterestList[i]}',
|
localizations.amount(compoundInterestList[i]),
|
||||||
style: const TextStyle(color: CupertinoColors.white),
|
style: const TextStyle(color: CupertinoColors.white),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
|
@ -160,7 +163,7 @@ class ChartPage extends StatelessWidget {
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'€${compoundInterestList[i] + investedMoneyList[i]}',
|
localizations.amount(compoundInterestList[i] + investedMoneyList[i]),
|
||||||
style: const TextStyle(color: CupertinoColors.white),
|
style: const TextStyle(color: CupertinoColors.white),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:flutter_application_1/widgets/milestone_timeline_widget.dart';
|
import 'package:flutter_application_1/widgets/milestone_timeline_widget.dart';
|
||||||
|
|
||||||
// Widget für die Seite, die die Meilenstein-Timeline anzeigt
|
// Widget für die Seite, die die Meilenstein-Timeline anzeigt
|
||||||
|
@ -32,9 +33,9 @@ class MilestonePage extends StatelessWidget {
|
||||||
Navigator.pop(context); // Zurück zur vorherigen Seite
|
Navigator.pop(context); // Zurück zur vorherigen Seite
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Text(
|
Text(
|
||||||
'Meilensteine',
|
AppLocalizations.of(context)!.milestones,
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 40),
|
const SizedBox(width: 40),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_application_1/enums.dart';
|
|
||||||
|
|
||||||
// Rundet den Wert im Textfeld-Controller auf die nächste ganze Zahl
|
// Rundet den Wert im Textfeld-Controller auf die nächste ganze Zahl
|
||||||
void roundToInteger(TextEditingController controller) {
|
void roundToInteger(TextEditingController controller) {
|
||||||
|
@ -21,13 +20,3 @@ void restoreDefaultValuesIfEmpty(TextEditingController controller) {
|
||||||
controller.text = '0';
|
controller.text = '0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Übersetzt das Auszahlungsintervall
|
|
||||||
String translateInterval(PayoutInterval interval) {
|
|
||||||
switch (interval) {
|
|
||||||
case PayoutInterval.yearly:
|
|
||||||
return 'jährlich';
|
|
||||||
case PayoutInterval.monthly:
|
|
||||||
return 'monatlich';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:syncfusion_flutter_charts/charts.dart';
|
import 'package:syncfusion_flutter_charts/charts.dart';
|
||||||
|
|
||||||
// Widget für die Erstellung eines gestapelten Säulendiagramms
|
// Widget für die Erstellung eines gestapelten Säulendiagramms
|
||||||
|
@ -14,16 +15,18 @@ class StackedColumnChart extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final localizations = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
return SfCartesianChart(
|
return SfCartesianChart(
|
||||||
legend: const Legend(
|
legend: const Legend(
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
position: LegendPosition.top,
|
position: LegendPosition.top,
|
||||||
),
|
),
|
||||||
primaryXAxis: const CategoryAxis(
|
primaryXAxis: CategoryAxis(
|
||||||
title: AxisTitle(text: 'Jahr'),
|
title: AxisTitle(text: localizations.year),
|
||||||
),
|
),
|
||||||
primaryYAxis: const NumericAxis(
|
primaryYAxis: NumericAxis(
|
||||||
title: AxisTitle(text: 'Euro'),
|
title: AxisTitle(text: localizations.currency_written_out),
|
||||||
),
|
),
|
||||||
series: <CartesianSeries>[
|
series: <CartesianSeries>[
|
||||||
// Serie für die Einzahlungen (untere Werte)
|
// Serie für die Einzahlungen (untere Werte)
|
||||||
|
@ -31,7 +34,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',
|
name: localizations.deposits,
|
||||||
color: CupertinoColors.systemRed.highContrastColor,
|
color: CupertinoColors.systemRed.highContrastColor,
|
||||||
),
|
),
|
||||||
// Serie für die Zinsen (obere Werte)
|
// Serie für die Zinsen (obere Werte)
|
||||||
|
@ -39,7 +42,7 @@ 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',
|
name: localizations.interest_received,
|
||||||
color: CupertinoColors.systemBlue.highContrastColor,
|
color: CupertinoColors.systemBlue.highContrastColor,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_application_1/enums.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:flutter_application_1/utils.dart';
|
|
||||||
|
|
||||||
// Widget zur Auswahl des Ausschüttungsintervalls
|
// Widget zur Auswahl des Ausschüttungsintervalls
|
||||||
class IntervalWidget extends StatefulWidget {
|
class IntervalWidget extends StatefulWidget {
|
||||||
|
@ -41,13 +40,16 @@ class IntervalWidgetState extends State<IntervalWidget> {
|
||||||
}
|
}
|
||||||
|
|
||||||
OverlayEntry _createOverlayEntry() {
|
OverlayEntry _createOverlayEntry() {
|
||||||
|
final localizations = AppLocalizations.of(context)!;
|
||||||
|
final isMobile = Theme.of(context).platform == TargetPlatform.iOS || Theme.of(context).platform == TargetPlatform.android;
|
||||||
|
|
||||||
return OverlayEntry(
|
return OverlayEntry(
|
||||||
builder: (context) => Positioned(
|
builder: (context) => Positioned(
|
||||||
width: 160,
|
width: 160,
|
||||||
child: CompositedTransformFollower(
|
child: CompositedTransformFollower(
|
||||||
link: _layerLink,
|
link: _layerLink,
|
||||||
showWhenUnlinked: false,
|
showWhenUnlinked: false,
|
||||||
offset: const Offset(0.0, 60),
|
offset: Offset(0.0, isMobile ? 70 : 60),
|
||||||
child: Material(
|
child: Material(
|
||||||
color: CupertinoColors.extraLightBackgroundGray,
|
color: CupertinoColors.extraLightBackgroundGray,
|
||||||
borderRadius: BorderRadius.circular(5),
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
@ -57,34 +59,37 @@ class IntervalWidgetState extends State<IntervalWidget> {
|
||||||
children: [
|
children: [
|
||||||
// ListTile für jährliches Ausschüttungsintervall
|
// ListTile für jährliches Ausschüttungsintervall
|
||||||
ListTile(
|
ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 15.0),
|
||||||
title: Text(
|
title: Text(
|
||||||
translateInterval(PayoutInterval.yearly),
|
localizations.yearly,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: CupertinoColors.black,
|
color: CupertinoColors.black,
|
||||||
fontSize: 14,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: widget.selectedInterval == translateInterval(PayoutInterval.yearly)
|
trailing: widget.selectedInterval == localizations.yearly
|
||||||
? const Icon(CupertinoIcons.checkmark_alt, color: CupertinoColors.black)
|
? const Icon(CupertinoIcons.checkmark_alt, color: CupertinoColors.black, size: 20,)
|
||||||
: null,
|
: null,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
widget.onChanged(translateInterval(PayoutInterval.yearly));
|
widget.onChanged(localizations.yearly);
|
||||||
_toggleDropdown();
|
_toggleDropdown();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// ListTile für monatliches Ausschüttungsintervall
|
// ListTile für monatliches Ausschüttungsintervall
|
||||||
ListTile(
|
ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 15.0),
|
||||||
title: Text(
|
title: Text(
|
||||||
translateInterval(PayoutInterval.monthly),
|
localizations.monthly,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: CupertinoColors.black,
|
color: CupertinoColors.black,
|
||||||
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: widget.selectedInterval == translateInterval(PayoutInterval.monthly)
|
trailing: widget.selectedInterval == localizations.monthly
|
||||||
? const Icon(CupertinoIcons.checkmark_alt, color: CupertinoColors.black)
|
? const Icon(CupertinoIcons.checkmark_alt, color: CupertinoColors.black, size: 20,)
|
||||||
: null,
|
: null,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
widget.onChanged(translateInterval(PayoutInterval.monthly));
|
widget.onChanged(localizations.monthly);
|
||||||
_toggleDropdown();
|
_toggleDropdown();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -98,34 +103,36 @@ class IntervalWidgetState extends State<IntervalWidget> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final localizations = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
return CompositedTransformTarget(
|
return CompositedTransformTarget(
|
||||||
link: _layerLink,
|
link: _layerLink,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Ausschüttungsintervall',
|
localizations.payout_interval,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 5),
|
const SizedBox(width: 5),
|
||||||
// Tooltip mit Erklärung zum Ausschüttungsintervall
|
// Tooltip mit Erklärung zum Ausschüttungsintervall
|
||||||
Tooltip(
|
Tooltip(
|
||||||
message: 'Das Ausschüttungsintervall bezieht sich darauf, wie oft die Erträge aus Ihrer Investition ausgeschüttet werden.',
|
message: localizations.payout_interval_tooltiptext,
|
||||||
triggerMode: TooltipTriggerMode.tap,
|
triggerMode: TooltipTriggerMode.tap,
|
||||||
decoration: BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: CupertinoColors.black,
|
color: CupertinoColors.black,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(5))
|
borderRadius: BorderRadius.all(Radius.circular(5))
|
||||||
),
|
),
|
||||||
textStyle: TextStyle(
|
textStyle: const TextStyle(
|
||||||
color: CupertinoColors.white,
|
color: CupertinoColors.white,
|
||||||
),
|
),
|
||||||
margin: EdgeInsets.all(20),
|
margin: const EdgeInsets.all(20),
|
||||||
child: Icon(CupertinoIcons.question_circle_fill, size: 15),
|
child: const Icon(CupertinoIcons.question_circle_fill, size: 15),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
@ -135,6 +142,9 @@ class IntervalWidgetState extends State<IntervalWidget> {
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: _toggleDropdown,
|
onPressed: _toggleDropdown,
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
|
padding: MaterialStateProperty.all<EdgeInsetsGeometry>(
|
||||||
|
const EdgeInsets.symmetric(horizontal: 15.0),
|
||||||
|
),
|
||||||
backgroundColor: MaterialStateProperty.all<Color>(CupertinoColors.extraLightBackgroundGray),
|
backgroundColor: MaterialStateProperty.all<Color>(CupertinoColors.extraLightBackgroundGray),
|
||||||
foregroundColor: MaterialStateProperty.all<Color>(CupertinoColors.black),
|
foregroundColor: MaterialStateProperty.all<Color>(CupertinoColors.black),
|
||||||
overlayColor: MaterialStateProperty.all<Color>(CupertinoColors.extraLightBackgroundGray),
|
overlayColor: MaterialStateProperty.all<Color>(CupertinoColors.extraLightBackgroundGray),
|
||||||
|
@ -150,9 +160,13 @@ class IntervalWidgetState extends State<IntervalWidget> {
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.selectedInterval, // Text des ausgewählten Intervalls
|
widget.selectedInterval, // Text des ausgewählten Intervalls
|
||||||
),
|
style: const TextStyle(
|
||||||
|
color: CupertinoColors.black,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Icon(isExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down),
|
Icon(isExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down, size: 20,),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
class LanguageSwitcher extends StatefulWidget {
|
||||||
|
final Locale currentLocale;
|
||||||
|
final Function(Locale) onLocaleChanged;
|
||||||
|
|
||||||
|
const LanguageSwitcher({
|
||||||
|
super.key,
|
||||||
|
required this.currentLocale,
|
||||||
|
required this.onLocaleChanged,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
LanguageSwitcherState createState() => LanguageSwitcherState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class LanguageSwitcherState extends State<LanguageSwitcher> {
|
||||||
|
late Locale _selectedLocale;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_selectedLocale = widget.currentLocale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final localizations = AppLocalizations.of(context)!;
|
||||||
|
final isMobile = Theme.of(context).platform == TargetPlatform.iOS || Theme.of(context).platform == TargetPlatform.android;
|
||||||
|
|
||||||
|
return Theme(
|
||||||
|
data: ThemeData.light().copyWith(
|
||||||
|
popupMenuTheme: PopupMenuThemeData(
|
||||||
|
color: CupertinoColors.extraLightBackgroundGray,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
textStyle: const TextStyle(color: CupertinoColors.black),
|
||||||
|
),
|
||||||
|
tooltipTheme: TooltipThemeData(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: CupertinoColors.black,
|
||||||
|
borderRadius: BorderRadius.circular(4.0),
|
||||||
|
),
|
||||||
|
textStyle: const TextStyle(color: CupertinoColors.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: CupertinoColors.extraLightBackgroundGray,
|
||||||
|
borderRadius: BorderRadius.circular(100.0),
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black26,
|
||||||
|
blurRadius: 8.0,
|
||||||
|
offset: Offset(0, 4),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: PopupMenuButton<Locale>(
|
||||||
|
offset: Offset(0.0, isMobile ? 55 : 45),
|
||||||
|
onSelected: (Locale locale) {
|
||||||
|
setState(() {
|
||||||
|
_selectedLocale = locale;
|
||||||
|
});
|
||||||
|
widget.onLocaleChanged(locale);
|
||||||
|
},
|
||||||
|
tooltip: localizations.choose_language,
|
||||||
|
icon: const Icon(
|
||||||
|
CupertinoIcons.globe,
|
||||||
|
color: CupertinoColors.black,
|
||||||
|
),
|
||||||
|
itemBuilder: (BuildContext context) {
|
||||||
|
return [
|
||||||
|
PopupMenuItem<Locale>(
|
||||||
|
value: const Locale('en'),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(AppLocalizations.of(context)!.language_english),
|
||||||
|
if (_selectedLocale.languageCode == 'en') const Icon(CupertinoIcons.checkmark_alt, size: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem<Locale>(
|
||||||
|
value: const Locale('de'),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(AppLocalizations.of(context)!.language_german),
|
||||||
|
if (_selectedLocale.languageCode == 'de') const Icon(CupertinoIcons.checkmark_alt, size: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,7 +39,7 @@ class MilestoneTimeline extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
milestoneEmoji,
|
milestoneEmoji,
|
||||||
style: const TextStyle(fontSize: 20),
|
style: const TextStyle(fontSize: 30),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 5),
|
const SizedBox(height: 5),
|
||||||
Text(
|
Text(
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:flutter_application_1/enums.dart';
|
import 'package:flutter_application_1/enums.dart';
|
||||||
import 'package:flutter_application_1/pages/chart_page.dart';
|
import 'package:flutter_application_1/pages/chart_page.dart';
|
||||||
import 'package:flutter_application_1/pages/milestone_page.dart';
|
import 'package:flutter_application_1/pages/milestone_page.dart';
|
||||||
import 'package:flutter_application_1/utils.dart';
|
|
||||||
import 'package:flutter_application_1/widgets/custom_image_button_widget.dart';
|
import 'package:flutter_application_1/widgets/custom_image_button_widget.dart';
|
||||||
|
|
||||||
// Widget zur Anzeige der Ergebnisse der Berechnungen und zur Navigation zu anderen Seiten
|
// Widget zur Anzeige der Ergebnisse der Berechnungen und zur Navigation zu anderen Seiten
|
||||||
|
@ -31,22 +31,29 @@ class ResultWidget extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final localizations = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
List<Map<String, dynamic>> milestoneList = [
|
List<Map<String, dynamic>> milestoneList = [
|
||||||
{'value': 700.0, 'emoji': '📱', 'text': 'Smartphone\nPreis: 700€'},
|
{'value': 700.0, 'emoji': '📱', 'text': localizations.milestone_text1 + localizations.amount(700)},
|
||||||
{'value': 3250.0, 'emoji': '🚲', 'text': 'eBike\nPreis: 3250€'},
|
{'value': 3250.0, 'emoji': '🚲', 'text': localizations.milestone_text2 + localizations.amount(3250)},
|
||||||
{'value': 20000.0, 'emoji': '🌎', 'text': 'Weltreise\nPreis: 20000€'},
|
{'value': 20000.0, 'emoji': '🌎', 'text': localizations.milestone_text3 + localizations.amount(20000)},
|
||||||
{'value': 100000.0, 'emoji': '🏎️', 'text': 'Sportwagen\nPreis: 100000€'},
|
{'value': 100000.0, 'emoji': '🏎️', 'text': localizations.milestone_text4 + localizations.amount(100000)},
|
||||||
{'value': 350000.0, 'emoji': '🏡', 'text': '150qm Einfamilienhaus\nPreis: 350000€'},
|
{'value': 350000.0, 'emoji': '🏡', 'text': localizations.milestone_text5 + localizations.amount(350000)},
|
||||||
];
|
];
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
_buildResultRow('Endkapital:', '$compoundInterest€'),
|
Row(
|
||||||
_buildResultRow('Einzahlungen:', '$investedMoney€'),
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
_buildResultRow('Erhaltene Zinsen:', '${double.parse(compoundInterest) + double.parse(investedMoney)}€'),
|
children: [
|
||||||
|
Expanded(child: _buildResultBox(localizations.final_capital, localizations.amount(compoundInterest))),
|
||||||
|
Expanded(child: _buildResultBox(localizations.deposits, localizations.amount(investedMoney))),
|
||||||
|
Expanded(child: _buildResultBox(localizations.interest_received, localizations.amount(double.parse(compoundInterest) - double.parse(investedMoney)))),
|
||||||
|
],
|
||||||
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
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.'
|
localizations.result_text(double.parse(compoundInterest) - double.parse(investedMoney), compoundInterest, interestRate, investedMoney, time, monthlySavingsRate)
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
CustomImageButton(
|
CustomImageButton(
|
||||||
|
@ -62,9 +69,9 @@ class ResultWidget extends StatelessWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
backgroundImage: 'assets/images/button_bg1.jpg',
|
backgroundImage: 'assets/images/button_bg1.jpg',
|
||||||
child: const Text(
|
child: Text(
|
||||||
'Grafik',
|
localizations.graphic,
|
||||||
style: TextStyle(color: CupertinoColors.white, fontSize: 20),
|
style: const TextStyle(color: CupertinoColors.white, fontSize: 20),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
@ -82,39 +89,39 @@ class ResultWidget extends StatelessWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
backgroundImage: 'assets/images/button_bg2.jpg',
|
backgroundImage: 'assets/images/button_bg2.jpg',
|
||||||
child: const Text(
|
child: Text(
|
||||||
'Meilensteine',
|
localizations.milestones,
|
||||||
style: TextStyle(color: CupertinoColors.white, fontSize: 20),
|
style: const TextStyle(color: CupertinoColors.white, fontSize: 20),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Widget zum Aufbau einer Ergebniszeile
|
// Widget zum Aufbau einer Ergebnisbox
|
||||||
Widget _buildResultRow(String label, String value) {
|
Widget _buildResultBox(String label, String value) {
|
||||||
return Padding(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Row(
|
margin: const EdgeInsets.symmetric(horizontal: 5.0),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
decoration: BoxDecoration(
|
||||||
|
color: CupertinoColors.extraLightBackgroundGray,
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
Text(
|
||||||
child: Text(
|
label,
|
||||||
label,
|
style: const TextStyle(
|
||||||
textAlign: TextAlign.start,
|
fontWeight: FontWeight.bold,
|
||||||
style: const TextStyle(
|
fontSize: 10,
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 20),
|
const SizedBox(height: 5),
|
||||||
Flexible(
|
Text(
|
||||||
child: Text(
|
value,
|
||||||
value,
|
style: const TextStyle(
|
||||||
textAlign: TextAlign.end,
|
fontWeight: FontWeight.bold,
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
11
pubspec.lock
11
pubspec.lock
|
@ -70,19 +70,24 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.2"
|
||||||
|
flutter_localizations:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: intl
|
name: intl
|
||||||
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.19.0"
|
version: "0.18.1"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -36,6 +36,9 @@ dependencies:
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.6
|
cupertino_icons: ^1.0.6
|
||||||
syncfusion_flutter_charts: ^25.1.42+1
|
syncfusion_flutter_charts: ^25.1.42+1
|
||||||
|
flutter_localizations:
|
||||||
|
sdk: flutter
|
||||||
|
intl: any
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -53,7 +56,7 @@ dev_dependencies:
|
||||||
|
|
||||||
# The following section is specific to Flutter packages.
|
# The following section is specific to Flutter packages.
|
||||||
flutter:
|
flutter:
|
||||||
|
generate: true
|
||||||
# The following line ensures that the Material Icons font is
|
# The following line ensures that the Material Icons font is
|
||||||
# included with your application, so that you can use the icons in
|
# included with your application, so that you can use the icons in
|
||||||
# the material Icons class.
|
# the material Icons class.
|
||||||
|
|
|
@ -68,10 +68,5 @@ void main() {
|
||||||
restoreDefaultValuesIfEmpty(controller);
|
restoreDefaultValuesIfEmpty(controller);
|
||||||
expect(controller.text, '5');
|
expect(controller.text, '5');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('translateInterval should return correct translation', () {
|
|
||||||
expect(translateInterval(PayoutInterval.yearly), 'jährlich');
|
|
||||||
expect(translateInterval(PayoutInterval.monthly), 'monatlich');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_application_1/enums.dart';
|
|
||||||
import 'package:flutter_application_1/pages/chart_page.dart';
|
import 'package:flutter_application_1/pages/chart_page.dart';
|
||||||
import 'package:flutter_application_1/pages/milestone_page.dart';
|
import 'package:flutter_application_1/pages/milestone_page.dart';
|
||||||
import 'package:flutter_application_1/widgets/chart_widget.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
|
import 'package:flutter_application_1/enums.dart';
|
||||||
import 'package:flutter_application_1/widgets/error_widget.dart';
|
import 'package:flutter_application_1/widgets/error_widget.dart';
|
||||||
import 'package:flutter_application_1/widgets/input_widget.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/interval_widget.dart';
|
||||||
|
@ -104,103 +105,46 @@ void main() {
|
||||||
expect(controller.text, '12345');
|
expect(controller.text, '12345');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
group('Interal Widget Tests', () {
|
group('Interval Widget Tests', () {
|
||||||
testWidgets('Initial state', (WidgetTester tester) async {
|
testWidgets('Shows correct localized texts and handles dropdown selection', (WidgetTester tester) async {
|
||||||
const selectedInterval = 'jährlich';
|
const Locale testLocale = Locale('en');
|
||||||
|
final localizations = await AppLocalizations.delegate.load(testLocale);
|
||||||
|
|
||||||
|
String selectedInterval = localizations.yearly;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
locale: testLocale,
|
||||||
|
localizationsDelegates: const [
|
||||||
|
AppLocalizations.delegate,
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: IntervalWidget(
|
body: IntervalWidget(
|
||||||
selectedInterval: selectedInterval,
|
selectedInterval: selectedInterval,
|
||||||
onChanged: (newInterval) {},
|
onChanged: (String newValue) {
|
||||||
),
|
selectedInterval = newValue;
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(find.text('Ausschüttungsintervall'), findsOneWidget);
|
|
||||||
expect(find.text(selectedInterval), findsOneWidget);
|
|
||||||
expect(find.byIcon(Icons.keyboard_arrow_down), findsOneWidget);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Tapping the button expands the dropdown', (WidgetTester tester) async {
|
|
||||||
const selectedInterval = 'jährlich';
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: IntervalWidget(
|
|
||||||
selectedInterval: selectedInterval,
|
|
||||||
onChanged: (newInterval) {
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.tap(find.byType(ElevatedButton));
|
expect(find.text(localizations.payout_interval), findsOneWidget);
|
||||||
await tester.pump();
|
expect(find.text(localizations.payout_interval_tooltiptext), findsNothing);
|
||||||
|
|
||||||
expect(find.byType(ListTile), findsNWidgets(2));
|
expect(find.text(localizations.yearly), findsOneWidget);
|
||||||
expect(find.byIcon(CupertinoIcons.checkmark_alt), findsOneWidget);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Selecting an interval updates the state', (WidgetTester tester) async {
|
|
||||||
const selectedInterval = 'jährlich';
|
|
||||||
var currentInterval = selectedInterval;
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: IntervalWidget(
|
|
||||||
selectedInterval: selectedInterval,
|
|
||||||
onChanged: (newInterval) {
|
|
||||||
currentInterval = newInterval;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.tap(find.byType(ElevatedButton));
|
await tester.tap(find.byType(ElevatedButton));
|
||||||
await tester.pump();
|
|
||||||
|
|
||||||
await tester.tap(find.text('monatlich'));
|
|
||||||
await tester.pump();
|
|
||||||
|
|
||||||
expect(currentInterval, 'monatlich');
|
|
||||||
expect(find.byType(ListTile), findsNothing);
|
|
||||||
expect(find.byIcon(Icons.keyboard_arrow_down), findsOneWidget);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
group('Chart Widget Tests', () {
|
|
||||||
testWidgets('Displays chart correctly', (WidgetTester tester) async {
|
|
||||||
final List<double> lowerValues = [100, 200, 300, 400];
|
|
||||||
final List<double> upperValues = [50, 150, 250, 350];
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Scaffold(
|
|
||||||
body: StackedColumnChart(
|
|
||||||
lowerValues: lowerValues,
|
|
||||||
upperValues: upperValues,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(find.byType(SfCartesianChart), findsOneWidget);
|
|
||||||
|
|
||||||
expect(find.text('Einzahlungen'), findsOneWidget);
|
|
||||||
expect(find.text('Zinsen'), findsOneWidget);
|
|
||||||
|
|
||||||
expect(find.byType(CategoryAxis), findsOneWidget);
|
|
||||||
expect(find.byType(NumericAxis), findsOneWidget);
|
|
||||||
|
|
||||||
expect(find.text('Einzahlungen'), findsOneWidget);
|
|
||||||
expect(find.text('Zinsen'), findsOneWidget);
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text(localizations.yearly), findsNWidgets(2)); // Eins im Button und eins im Dropdown-Menü
|
||||||
|
expect(find.text(localizations.monthly), findsOneWidget);
|
||||||
|
|
||||||
|
await tester.tap(find.text(localizations.monthly).last);
|
||||||
|
expect(selectedInterval, localizations.monthly);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
group('Milestone Timeline Widget Tests', () {
|
group('Milestone Timeline Widget Tests', () {
|
||||||
|
@ -232,93 +176,65 @@ void main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
group('Result Widget Tests', () {
|
group('Result Widget Tests', () {
|
||||||
testWidgets('Displays result values correctly', (WidgetTester tester) async {
|
testWidgets('Shows correct localized texts', (WidgetTester tester) async {
|
||||||
const compoundInterest = '1000';
|
const Locale testLocale = Locale('en');
|
||||||
const investedMoney = '500';
|
final localizations = await AppLocalizations.delegate.load(testLocale);
|
||||||
const time = '5';
|
|
||||||
const monthlySavingsRate = '100';
|
|
||||||
const interestRate = '5';
|
|
||||||
const payoutInterval = PayoutInterval.monthly;
|
|
||||||
final List<double> investedMoneyList = [100, 200, 300, 400, 500];
|
|
||||||
final List<double> compoundInterestList = [100, 200, 300, 400, 1000];
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
const compoundInterest = '12000';
|
||||||
MaterialApp(
|
const investedMoney = '10000';
|
||||||
home: Material(
|
const time = '10';
|
||||||
child: ResultWidget(
|
const monthlySavingsRate = '100';
|
||||||
compoundInterest: compoundInterest,
|
const interestRate = '5';
|
||||||
investedMoney: investedMoney,
|
const payoutInterval = PayoutInterval.yearly;
|
||||||
time: time,
|
final List<double> investedMoneyList = [1000, 2000, 3000, 4000, 5000];
|
||||||
monthlySavingsRate: monthlySavingsRate,
|
final List<double> compoundInterestList = [1100, 2300, 3600, 5000, 6600];
|
||||||
interestRate: interestRate,
|
|
||||||
payoutInterval: payoutInterval,
|
await tester.pumpWidget(
|
||||||
investedMoneyList: investedMoneyList,
|
MaterialApp(
|
||||||
compoundInterestList: compoundInterestList,
|
locale: testLocale,
|
||||||
|
localizationsDelegates: const [
|
||||||
|
AppLocalizations.delegate,
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
home: Scaffold(
|
||||||
|
body: ResultWidget(
|
||||||
|
compoundInterest: compoundInterest,
|
||||||
|
investedMoney: investedMoney,
|
||||||
|
time: time,
|
||||||
|
monthlySavingsRate: monthlySavingsRate,
|
||||||
|
interestRate: interestRate,
|
||||||
|
payoutInterval: payoutInterval,
|
||||||
|
investedMoneyList: investedMoneyList,
|
||||||
|
compoundInterestList: compoundInterestList,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
expect(find.text('Endkapital:'), findsOneWidget);
|
expect(find.text(localizations.final_capital), findsOneWidget);
|
||||||
expect(find.text('$compoundInterest€'), findsOneWidget);
|
expect(find.text(localizations.amount(compoundInterest)), findsOneWidget);
|
||||||
expect(find.text('Einzahlungen:'), findsOneWidget);
|
expect(find.text(localizations.deposits), findsOneWidget);
|
||||||
expect(find.text('$investedMoney€'), findsOneWidget);
|
expect(find.text(localizations.amount(investedMoney)), findsOneWidget);
|
||||||
expect(find.text('Erhaltene Zinsen:'), findsOneWidget);
|
expect(find.text(localizations.interest_received), findsOneWidget);
|
||||||
expect(find.text('${double.parse(compoundInterest) + double.parse(investedMoney)}€'), findsOneWidget);
|
expect(find.text(localizations.amount(double.parse(compoundInterest) - double.parse(investedMoney))), findsOneWidget);
|
||||||
|
|
||||||
|
final resultText = localizations.result_text(
|
||||||
|
double.parse(compoundInterest) - double.parse(investedMoney),
|
||||||
|
compoundInterest,
|
||||||
|
interestRate,
|
||||||
|
investedMoney,
|
||||||
|
time,
|
||||||
|
monthlySavingsRate,
|
||||||
|
);
|
||||||
|
expect(find.text(resultText), findsOneWidget);
|
||||||
|
|
||||||
|
expect(find.text(localizations.graphic), findsOneWidget);
|
||||||
|
expect(find.text(localizations.milestones), findsOneWidget);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Navigates to ChartPage on "Grafik" button press', (WidgetTester tester) async {
|
|
||||||
final List<double> investedMoneyList = [100, 200, 300, 400, 500];
|
|
||||||
final List<double> compoundInterestList = [100, 200, 300, 400, 1000];
|
|
||||||
|
|
||||||
await tester.pumpWidget(
|
|
||||||
MaterialApp(
|
|
||||||
home: Material(
|
|
||||||
child: ResultWidget(
|
|
||||||
compoundInterest: '1000',
|
|
||||||
investedMoney: '500',
|
|
||||||
time: '5',
|
|
||||||
monthlySavingsRate: '100',
|
|
||||||
interestRate: '5',
|
|
||||||
payoutInterval: PayoutInterval.monthly,
|
|
||||||
investedMoneyList: investedMoneyList,
|
|
||||||
compoundInterestList: compoundInterestList,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.tap(find.text('Grafik'));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(find.byType(ChartPage), findsOneWidget);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('Navigates to MilestonePage on "Meilensteine" button press', (WidgetTester tester) async {
|
|
||||||
await tester.pumpWidget(
|
|
||||||
const MaterialApp(
|
|
||||||
home: Material(
|
|
||||||
child: ResultWidget(
|
|
||||||
compoundInterest: '1000',
|
|
||||||
investedMoney: '500',
|
|
||||||
time: '5',
|
|
||||||
monthlySavingsRate: '100',
|
|
||||||
interestRate: '5',
|
|
||||||
payoutInterval: PayoutInterval.monthly,
|
|
||||||
investedMoneyList: [],
|
|
||||||
compoundInterestList: [],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
await tester.tap(find.text('Meilensteine'));
|
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
|
||||||
expect(find.byType(MilestonePage), findsOneWidget);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('Error Widget Tests', () {
|
group('Error Widget Tests', () {
|
||||||
testWidgets('Displays error message correctly', (WidgetTester tester) async {
|
testWidgets('Displays error message correctly', (WidgetTester tester) async {
|
||||||
const String errorMessage = 'This is an error message';
|
const String errorMessage = 'This is an error message';
|
||||||
|
@ -337,4 +253,97 @@ void main() {
|
||||||
expect(find.text(errorMessage), findsOneWidget);
|
expect(find.text(errorMessage), findsOneWidget);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
group('Chart Page Tests', () {
|
||||||
|
testWidgets('Shows correct localized texts and table data', (WidgetTester tester) async {
|
||||||
|
const Locale testLocale = Locale('en');
|
||||||
|
final localizations = await AppLocalizations.delegate.load(testLocale);
|
||||||
|
|
||||||
|
final investedMoneyList = [1000.0, 2000.0, 3000.0, 4000.0, 5000.0];
|
||||||
|
final compoundInterestList = [1100.0, 2200.0, 3300.0, 4400.0, 5500.0];
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
locale: testLocale,
|
||||||
|
localizationsDelegates: const [
|
||||||
|
AppLocalizations.delegate,
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
home: ChartPage(
|
||||||
|
investedMoneyList: investedMoneyList,
|
||||||
|
compoundInterestList: compoundInterestList,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text(localizations.graphic), findsOneWidget);
|
||||||
|
|
||||||
|
expect(find.byType(SfCartesianChart), findsOneWidget);
|
||||||
|
|
||||||
|
expect(find.text(localizations.year), findsOneWidget);
|
||||||
|
expect(find.text(localizations.deposits), findsNWidgets(2));
|
||||||
|
expect(find.text(localizations.interest_received), findsNWidgets(2));
|
||||||
|
expect(find.text(localizations.final_capital), findsOneWidget);
|
||||||
|
|
||||||
|
for (int i = 0; i < investedMoneyList.length; i++) {
|
||||||
|
expect(find.text('${i + 1}'), findsOneWidget);
|
||||||
|
expect(find.text(localizations.amount(investedMoneyList[i])), findsOneWidget);
|
||||||
|
expect(find.text(localizations.amount(compoundInterestList[i])), findsOneWidget);
|
||||||
|
expect(find.text(localizations.amount(compoundInterestList[i] + investedMoneyList[i])), findsOneWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.tap(find.byIcon(CupertinoIcons.chevron_left));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.byIcon(CupertinoIcons.chevron_left), findsNothing);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
group('Milestone Page Tests', () {
|
||||||
|
testWidgets('Shows correct localized texts and milestone data', (WidgetTester tester) async {
|
||||||
|
const Locale testLocale = Locale('en');
|
||||||
|
final localizations = await AppLocalizations.delegate.load(testLocale);
|
||||||
|
|
||||||
|
const compoundInterest = '50000';
|
||||||
|
const investedMoney = '30000';
|
||||||
|
List<Map<String, dynamic>> milestoneList = [
|
||||||
|
{'value': 700.0, 'emoji': '📱', 'text': localizations.milestone_text1 + localizations.amount(700)},
|
||||||
|
{'value': 3250.0, 'emoji': '🚲', 'text': localizations.milestone_text2 + localizations.amount(3250)},
|
||||||
|
{'value': 20000.0, 'emoji': '🌎', 'text': localizations.milestone_text3 + localizations.amount(20000)},
|
||||||
|
{'value': 100000.0, 'emoji': '🏎️', 'text': localizations.milestone_text4 + localizations.amount(100000)},
|
||||||
|
{'value': 350000.0, 'emoji': '🏡', 'text': localizations.milestone_text5 + localizations.amount(350000)},
|
||||||
|
];
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
locale: testLocale,
|
||||||
|
localizationsDelegates: const [
|
||||||
|
AppLocalizations.delegate,
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
home: MilestonePage(
|
||||||
|
compoundInterest: compoundInterest,
|
||||||
|
investedMoney: investedMoney,
|
||||||
|
milestoneList: milestoneList,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text(localizations.milestones), findsOneWidget);
|
||||||
|
|
||||||
|
expect(find.byType(MilestoneTimeline), findsOneWidget);
|
||||||
|
|
||||||
|
for (var milestone in milestoneList) {
|
||||||
|
expect(find.text(milestone['emoji']), findsOneWidget);
|
||||||
|
expect(find.text(milestone['text']), findsOneWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.tap(find.byIcon(CupertinoIcons.chevron_left));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.byIcon(CupertinoIcons.chevron_left), findsNothing);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
Loading…
Reference in New Issue