Skip to content
This repository has been archived by the owner on Oct 4, 2024. It is now read-only.

Add comments for BalanceOverview Widgets #284

Merged
merged 1 commit into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import 'total_display.dart';
import 'view_option.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'selection.dart';
import 'package:intl/intl.dart';

/// Custom widget for representing the overview of income, balance and expenses
/// Uses different custom widgets that are refractored in own files under
/// balance_overview directory
///
class BalanceOverview extends StatefulWidget {
//function that return List of all Categories depending on the selectedDate
// Widget expects two list of all Categories depending on the selectedDate
final List<CategoryMock> incomes;
final List<CategoryMock> expenses;

Expand All @@ -25,26 +28,27 @@ class BalanceOverview extends StatefulWidget {
}

class _BalanceOverviewState extends State<BalanceOverview> {
/// At the beginning initialise some attributes
Selection _selectedSegment = Selection.balance;
ViewOption _viewOption = ViewOption.month;
DateTime _selectedDate = DateTime.now();

//angezeigter String auf dem Button
// Text which is shown on the center button and displays the value of _selectedDate
late String buttonText;

// initState() function that is called once when the widget is started for the first time
@override
void initState() {
// TODO: implement initState
super.initState();
buttonText = DateFormat.MMMM().format(_selectedDate);
}

//Berechnung der total Values für Incomes und Expenses
//Calculation of total values for incomes und expenses
double get totalIncomes =>
widget.incomes.fold(0, (sum, item) => sum + item.totalValue);
double get totalExpenses =>
widget.expenses.fold(0, (sum, item) => sum + item.totalValue);

// function that returns the total value depening on selected view [income, balance, expenses]
double get totalValue {
if (_selectedSegment == Selection.income) {
return totalIncomes;
Expand All @@ -60,22 +64,35 @@ class _BalanceOverviewState extends State<BalanceOverview> {
Widget build(BuildContext context) {
return Column(
children: [
// Custom widget that shows the SegmentedControl
SegmentedControlWidget(
selectedSegment: _selectedSegment,
onValueChanged: (Selection? value) {
if (value != null) {
setState(() {
_selectedSegment = value;
_selectedSegment =
value; // Update the selected segment and rebuild the widget
});
}
}),
const SizedBox(height: 20),
TotalDisplay(total: totalValue, selectedSegment: _selectedSegment),
const SizedBox(
height: 20,
),
// Custom widget TotalDisplay that displays totalValue as Text
TotalDisplay(
total: totalValue,
selectedSegment: _selectedSegment,
),
// Call a method to build additional content
_buildContent(),
],
);
}

/// Method to build the content based on the selected segment
/// Income -> builds a pie chart with the list of incomes
/// Balance -> builds the chart that shows the balance between incomes and expenses
/// Expense -> builds a pie chart with the list of expenses
Widget _buildContent() {
if (_selectedSegment == Selection.income) {
return _buildPieChart(widget.incomes);
Expand All @@ -86,6 +103,9 @@ class _BalanceOverviewState extends State<BalanceOverview> {
}
}

/// Method to build a pie chart using a list
/// Using the PieChart widget from the imported fl_chart package
/// PieChart widget needs PieChartData for each section in the pie chart
Widget _buildPieChart(List<CategoryMock> items) {
return Container(
margin: const EdgeInsets.all(20),
Expand All @@ -97,26 +117,29 @@ class _BalanceOverviewState extends State<BalanceOverview> {
child: PieChart(
PieChartData(
sectionsSpace: 0,
sections: _generatePieChartSections(items),
sections: _generatePieChartSections(
items), // Generate the sections of the pie chart
centerSpaceRadius: double.infinity,
),
swapAnimationDuration:
const Duration(milliseconds: 150), // Optional
swapAnimationCurve: Curves.linear, // Optional
),
),
// Call a method to the center button
_buildCenterButton()
],
),
);
}

/// Method to build the center button that shows the selected date for the pie chart
Widget _buildCenterButton() {
return ClipOval(
child: AspectRatio(
aspectRatio: 1,
child: CupertinoButton(
onPressed: () => _selectDate(context),
onPressed: () => _selectDate(context), // Calls the
child: FittedBox(
child: Text(
buttonText,
Expand All @@ -131,6 +154,7 @@ class _BalanceOverviewState extends State<BalanceOverview> {
);
}

// Method that generate and return a list of PieChartSectionData which are required for PieChart
List<PieChartSectionData> _generatePieChartSections(
List<CategoryMock> items) {
return items.map((item) {
Expand All @@ -146,10 +170,13 @@ class _BalanceOverviewState extends State<BalanceOverview> {
}).toList();
}

// Async function that processes the date selection with a modal popup from Cupertino
Future<void> _selectDate(BuildContext context) async {
// Show a Cupertino modal popup to select a date
DateTime? picked = await showCupertinoModalPopup<DateTime>(
context: context,
builder: (BuildContext context) {
// Return a custom date selector popup widget
return DateSelectorPopup(
initialDate: _selectedDate,
initialViewOption: _viewOption,
Expand All @@ -161,26 +188,33 @@ class _BalanceOverviewState extends State<BalanceOverview> {
},
);

// If a date is picked (not null), update the state
if (picked != null) {
setState(() {
_selectedDate = picked;

// Update the button text based on the selected view option
switch (_viewOption) {
case ViewOption.day:
buttonText = DateFormat.yMMMMd().format(picked);
buttonText =
DateFormat.yMMMMd().format(picked); // Format date as day
break;
case ViewOption.month:
buttonText = DateFormat.MMMM().format(picked);
buttonText =
DateFormat.MMMM().format(picked); // Format date as month
break;
case ViewOption.year:
buttonText = DateFormat.y().format(picked);
buttonText = DateFormat.y().format(picked); // Format date as year
break;
}
});
}
}

// Method to build a container displaying the balance information
Widget _buildBalanceContainer() {
// Calculation of the fraction for income and expense
// needed to define the size of each container (income/expense)
double total = totalIncomes + totalExpenses;
double incomeFraction = totalIncomes / total;
double expenseFraction = totalExpenses / total;
Expand All @@ -193,6 +227,7 @@ class _BalanceOverviewState extends State<BalanceOverview> {
children: [
Column(
children: [
// Container to display the total incomes
Container(
height: 400 * incomeFraction,
decoration: BoxDecoration(
Expand Down Expand Up @@ -221,6 +256,7 @@ class _BalanceOverviewState extends State<BalanceOverview> {
),
),
),
// Container to display the total expenses
Container(
height: 400 * expenseFraction,
decoration: BoxDecoration(
Expand Down Expand Up @@ -249,12 +285,14 @@ class _BalanceOverviewState extends State<BalanceOverview> {
),
],
),
// Call a method to the center button
_buildBalanceCenterButton()
],
),
);
}

/// Method to build the center button that shows the selected date for the balance overview
Widget _buildBalanceCenterButton() {
return Padding(
padding: const EdgeInsets.all(70.0),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'package:flutter/cupertino.dart';

import 'view_option.dart';
import 'view_segmented_control.dart';

/// Custom Widget for selecting a date with additional options
class DateSelectorPopup extends StatefulWidget {
// Widget required the initial selected date and view
// also a callback for when a date is selected
final DateTime initialDate;
final ViewOption initialViewOption;
final void Function(DateTime, ViewOption) onDateSelected;
Expand Down Expand Up @@ -46,9 +48,11 @@ class _DateSelectorPopupState extends State<DateSelectorPopup> {
selectedSegment: _viewOption,
onValueChanged: (ViewOption? value) {
if (value != null) {
setState(() {
_viewOption = value;
});
setState(
() {
_viewOption = value;
},
);
}
},
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import 'package:flutter/cupertino.dart';
import 'selection.dart';

/// Custom Widget that represents the SegmentedControlWidget for selection view [income, balance, expense]
/// This widget uses CupertinoSlidingSegmentedControl based on Cupertino Design Guidelines
class SegmentedControlWidget extends StatelessWidget {
// Requires the currently selected segment and the callback to handle value changes
final Selection selectedSegment;
final ValueChanged<Selection?> onValueChanged;

Expand All @@ -17,10 +20,12 @@ class SegmentedControlWidget extends StatelessWidget {
margin: const EdgeInsets.all(10.0),
child: CupertinoSlidingSegmentedControl<Selection>(
backgroundColor: CupertinoColors.systemGrey2,
thumbColor: selectedSegment.color,
groupValue: selectedSegment,
thumbColor: selectedSegment
.color, // Set the color of the thumb based on the selected segment
groupValue: selectedSegment, // currently selected value
onValueChanged: onValueChanged,
children: Selection.values.asMap().map(
// Create a map of the children widgets for each segment
(index, selection) => MapEntry(
selection,
Padding(
Expand Down
14 changes: 11 additions & 3 deletions client/cashcompass/lib/widgets/balance_overview/total_display.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import 'package:flutter/cupertino.dart';
import 'selection.dart';

/// Custom Widget that displays the totalValue on the screen
/// Depending on the selected view [income, balance, expenses] the color of the text changes
/// income -> green, balance -> green, expense -> red
class TotalDisplay extends StatelessWidget {
// Widget expects the totalValue and the current view
final double total;
final Selection selectedSegment;

Expand All @@ -13,10 +17,13 @@ class TotalDisplay extends StatelessWidget {

@override
Widget build(BuildContext context) {
//format total String for widget
/// Format totalValue String for the Text widget
/// Adds the prefix depending on current view [+, ,-] and adds the currency at the end
/// (Currently hardcoded on Euro, maybe we support different currencies later, then we have to change that fixed code)
final String formattedTotal =
'${selectedSegment.prefix}${total.toStringAsFixed(2)}€';

// Widget consist of two Text widgets that are presented side by side which is implemented via a Row Widget
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expand All @@ -28,11 +35,12 @@ class TotalDisplay extends StatelessWidget {
),
),
Text(
formattedTotal,
formattedTotal, //uses the totalValue which was formatted above
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: selectedSegment.color,
color: selectedSegment
.color, // loads the color of the current view [green, blue, red]
),
)
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import 'view_option.dart';
import 'package:flutter/cupertino.dart';

/// Custom Widget that represents the SegmentedControlWidget for time view option [day, month, year]
/// This widget uses CupertinoSlidingSegmentedControl based on Cupertino Design Guidelines
class ViewOptionSegmentedControl extends StatelessWidget {
// Requires the currently selected segment and the callback to handle value changes
final ViewOption selectedSegment;
final ValueChanged<ViewOption?> onValueChanged;

Expand All @@ -19,6 +22,7 @@ class ViewOptionSegmentedControl extends StatelessWidget {
onValueChanged: onValueChanged,
thumbColor: CupertinoColors.systemBlue,
children: ViewOption.values.asMap().map(
// Create a map of the children widgets for each segments
(index, viewOption) => MapEntry(
viewOption,
Padding(
Expand Down