Compare commits

..

No commits in common. "d5e94d63d772c7fbf5a5c01cd0dd8d13a9467ccf" and "2d43ad58865bd4a3b01c40b232f2df715e1ca53c" have entirely different histories.

15 changed files with 142 additions and 503 deletions

View file

@ -2,8 +2,6 @@
- Add settings view for editing wallet categories - Add settings view for editing wallet categories
- Change code according to more aggressive linting - Change code according to more aggressive linting
- Create a default "no category" category, mainly to store entries with removed categories - Create a default "no category" category, mainly to store entries with removed categories
- Categories now have changeable colors assigned to them
- Added pie chart for expense/income data per category
# 1.0.0-alpha+2 # 1.0.0-alpha+2
- Fixed localization issues - Fixed localization issues
- Added graphs for expenses and income per month/year - Added graphs for expenses and income per month/year

View file

@ -11,7 +11,6 @@ class WalletCategory {
required this.name, required this.name,
required this.id, required this.id,
required this.icon, required this.icon,
required this.color,
}); });
/// Connects generated fromJson method /// Connects generated fromJson method
@ -28,10 +27,6 @@ class WalletCategory {
@JsonKey(fromJson: _iconDataFromJson, toJson: _iconDataToJson) @JsonKey(fromJson: _iconDataFromJson, toJson: _iconDataToJson)
IconData icon; IconData icon;
/// The color that will be displayed with entry
@JsonKey(fromJson: _colorFromJson, toJson: _colorToJson)
Color color;
/// Connects generated toJson method /// Connects generated toJson method
Map<String, dynamic> toJson() => _$WalletCategoryToJson(this); Map<String, dynamic> toJson() => _$WalletCategoryToJson(this);
@ -47,9 +42,6 @@ Map<String, dynamic> _iconDataToJson(IconData icon) =>
IconData _iconDataFromJson(Map<String, dynamic> data) => IconData _iconDataFromJson(Map<String, dynamic> data) =>
IconData(data['codepoint'] as int, fontFamily: data['family'] as String?); IconData(data['codepoint'] as int, fontFamily: data['family'] as String?);
int _colorToJson(Color color) => color.value;
Color _colorFromJson(int input) => Color(input);
/// Type of entry, either expense or income /// Type of entry, either expense or income
enum EntryType { enum EntryType {
/// Expense /// Expense

View file

@ -11,7 +11,6 @@ WalletCategory _$WalletCategoryFromJson(Map<String, dynamic> json) =>
name: json['name'] as String, name: json['name'] as String,
id: json['id'] as int, id: json['id'] as int,
icon: _iconDataFromJson(json['icon'] as Map<String, dynamic>), icon: _iconDataFromJson(json['icon'] as Map<String, dynamic>),
color: _colorFromJson(json['color'] as int),
); );
Map<String, dynamic> _$WalletCategoryToJson(WalletCategory instance) => Map<String, dynamic> _$WalletCategoryToJson(WalletCategory instance) =>
@ -19,5 +18,4 @@ Map<String, dynamic> _$WalletCategoryToJson(WalletCategory instance) =>
'name': instance.name, 'name': instance.name,
'id': instance.id, 'id': instance.id,
'icon': _iconDataToJson(instance.icon), 'icon': _iconDataToJson(instance.icon),
'color': _colorToJson(instance.color),
}; };

View file

@ -78,8 +78,6 @@
"editCategories":"Upravit kategorie", "editCategories":"Upravit kategorie",
"editCategoriesDesc":"Přidat, upravit nebo odebrat kategorii z peněženky", "editCategoriesDesc":"Přidat, upravit nebo odebrat kategorii z peněženky",
"wallet":"Peněženka", "wallet":"Peněženka",
"noCategory":"Žádná kategorie", "noCategory":"Žádná kategorie"
"done":"Hotovo",
"pickColor":"Zvolte barvu",
"changeDate":"Změnit ze kterého měsíce/roku brát data"
} }

View file

@ -158,8 +158,5 @@
"editCategories":"Edit categories", "editCategories":"Edit categories",
"editCategoriesDesc":"Add, edit or remove categories from a wallet", "editCategoriesDesc":"Add, edit or remove categories from a wallet",
"wallet":"Wallet", "wallet":"Wallet",
"noCategory":"No category", "noCategory":"No category"
"done":"Done",
"pickColor":"Pick a color",
"changeDate":"Change what month/year to pick data from"
} }

View file

@ -5,8 +5,7 @@ import 'package:flutter/material.dart';
/// Abstract class used to create widgets for the respective platform UI library /// Abstract class used to create widgets for the respective platform UI library
abstract class PlatformWidget<A extends Widget, I extends Widget> abstract class PlatformWidget<A extends Widget, I extends Widget>
extends StatelessWidget { extends StatelessWidget {
/// Abstract class used to create widgets /// Abstract class used to create widgets for the respective platform UI library
/// for the respective platform UI library
const PlatformWidget({super.key}); const PlatformWidget({super.key});
@override @override

View file

@ -1,8 +0,0 @@
/// Extension to get last day of the month
extension LastDay on DateTime {
/// Returns the last day of the month as [int]
int get lastDay {
final d = add(const Duration(days: 31));
return DateTime(d.year, d.month, 0).day;
}
}

View file

@ -3,12 +3,9 @@ import 'package:dynamic_color/dynamic_color.dart';
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:intl/intl.dart';
import 'package:prasule/api/category.dart'; import 'package:prasule/api/category.dart';
import 'package:prasule/api/walletentry.dart'; import 'package:prasule/api/walletentry.dart';
import 'package:prasule/main.dart'; import 'package:intl/intl.dart';
import 'package:prasule/util/get_last_date.dart';
import 'package:prasule/util/text_color.dart';
/// Monthly/Yearly expense/income [LineChart] /// Monthly/Yearly expense/income [LineChart]
class ExpensesLineChart extends StatelessWidget { class ExpensesLineChart extends StatelessWidget {
@ -75,9 +72,7 @@ class ExpensesLineChart extends StatelessWidget {
getTooltipItems: (spots) => List<LineTooltipItem>.generate( getTooltipItems: (spots) => List<LineTooltipItem>.generate(
spots.length, spots.length,
(index) => LineTooltipItem( (index) => LineTooltipItem(
// Changes what's rendered on the tooltip (spots[index].barIndex == 0)
// when clicked in the chart
(spots[index].barIndex == 0) // income chart
? (yearly ? (yearly
? AppLocalizations.of(context).incomeForMonth( ? AppLocalizations.of(context).incomeForMonth(
DateFormat.MMMM(locale).format( DateFormat.MMMM(locale).format(
@ -99,7 +94,7 @@ class ExpensesLineChart extends StatelessWidget {
name: currency.name, name: currency.name,
).format(spots[index].y), ).format(spots[index].y),
)) ))
: (yearly // expense chart : (yearly
? AppLocalizations.of(context).expensesForMonth( ? AppLocalizations.of(context).expensesForMonth(
DateFormat.MMMM(locale).format( DateFormat.MMMM(locale).format(
DateTime( DateTime(
@ -121,25 +116,12 @@ class ExpensesLineChart extends StatelessWidget {
).format(spots[index].y), ).format(spots[index].y),
)), )),
TextStyle(color: spots[index].bar.color), TextStyle(color: spots[index].bar.color),
children: [
TextSpan(
text: "\n${yearly ? DateFormat.MMMM(locale).format(
DateTime(
date.year,
index + 1,
),
) : DateFormat.yMMMMd(locale).format(DateTime(date.year, date.month, spots[index].spotIndex + 1))}",
),
],
), ),
), ),
), ),
), ),
maxY: maxY, maxY: maxY,
maxX: yearly maxX: yearly ? 12 : DateTime(date.year, date.month, 0).day.toDouble(),
? 12
: date.lastDay.toDouble() -
1, // remove 1 because we are indexing from 0
minY: 0, minY: 0,
minX: 0, minX: 0,
backgroundColor: Theme.of(context).colorScheme.background, backgroundColor: Theme.of(context).colorScheme.background,
@ -151,13 +133,10 @@ class ExpensesLineChart extends StatelessWidget {
isStrokeCapRound: true, isStrokeCapRound: true,
dotData: const FlDotData(show: false), dotData: const FlDotData(show: false),
belowBarData: BarAreaData(), belowBarData: BarAreaData(),
color: color: Colors.green
(MediaQuery.of(context).platformBrightness == Brightness.dark) .harmonizeWith(Theme.of(context).colorScheme.secondary),
? Colors.green.shade300
: Colors.green
.harmonizeWith(Theme.of(context).colorScheme.primary),
spots: List.generate( spots: List.generate(
yearly ? 12 : date.lastDay, yearly ? 12 : DateTime(date.year, date.month, 0).day,
(index) => FlSpot(index.toDouble(), incomeData[index]), (index) => FlSpot(index.toDouble(), incomeData[index]),
), ),
), ),
@ -168,37 +147,17 @@ class ExpensesLineChart extends StatelessWidget {
isStrokeCapRound: true, isStrokeCapRound: true,
dotData: const FlDotData(show: false), dotData: const FlDotData(show: false),
belowBarData: BarAreaData(), belowBarData: BarAreaData(),
color: color: Colors.red
(MediaQuery.of(context).platformBrightness == Brightness.dark) .harmonizeWith(Theme.of(context).colorScheme.secondary),
? Colors.red.shade300
: Colors.red
.harmonizeWith(Theme.of(context).colorScheme.primary),
spots: List.generate( spots: List.generate(
yearly yearly ? 12 : DateTime(date.year, date.month, 0).day,
? 12 (index) => FlSpot(index.toDouble() + 1, expenseData[index]),
: date.lastDay, // no -1 because it's the length, not index
(index) => FlSpot(index.toDouble(), expenseData[index]),
), ),
), ),
], // actual data ], // actual data
titlesData: FlTitlesData( titlesData: FlTitlesData(
rightTitles: const AxisTitles(), rightTitles: const AxisTitles(),
topTitles: const AxisTitles(), topTitles: const AxisTitles(),
leftTitles: AxisTitles(
sideTitles: SideTitles(
reservedSize: (NumberFormat.compact()
.format(expenseDataSorted.last)
.length >=
5 ||
NumberFormat.compact()
.format(incomeDataSorted.last)
.length >=
5)
? 50
: 25,
showTitles: true,
),
),
bottomTitles: AxisTitles( bottomTitles: AxisTitles(
sideTitles: SideTitles( sideTitles: SideTitles(
reservedSize: 30, reservedSize: 30,
@ -360,7 +319,7 @@ class ExpensesBarChart extends StatelessWidget {
minY: 0, minY: 0,
maxY: maxY, maxY: maxY,
barGroups: List<BarChartGroupData>.generate( barGroups: List<BarChartGroupData>.generate(
yearly ? 12 : date.lastDay - 1, yearly ? 12 : DateTime(date.year, date.month, 0).day,
(index) => BarChartGroupData( (index) => BarChartGroupData(
x: index, x: index,
barRods: [ barRods: [
@ -368,13 +327,13 @@ class ExpensesBarChart extends StatelessWidget {
BarChartRodData( BarChartRodData(
toY: incomeData[index], toY: incomeData[index],
color: Colors.green color: Colors.green
.harmonizeWith(Theme.of(context).colorScheme.primary), .harmonizeWith(Theme.of(context).colorScheme.secondary),
), ),
if (expenseData.isNotEmpty) if (expenseData.isNotEmpty)
BarChartRodData( BarChartRodData(
toY: expenseData[index], toY: expenseData[index],
color: Colors.red color: Colors.red
.harmonizeWith(Theme.of(context).colorScheme.primary), .harmonizeWith(Theme.of(context).colorScheme.secondary),
), ),
], ],
), ),
@ -383,112 +342,22 @@ class ExpensesBarChart extends StatelessWidget {
); );
} }
/// [PieChart] used to display expenses/income visualized class CategoriesPieChart extends StatelessWidget {
/// under their respective category const CategoriesPieChart(
class CategoriesPieChart extends StatefulWidget { {super.key, required this.entries, required this.categories});
/// [PieChart] used to display expenses/income visualized
/// under their respective category
const CategoriesPieChart({
required this.entries,
required this.categories,
required this.symbol,
super.key,
});
/// Entries to be used
final List<WalletSingleEntry> entries; final List<WalletSingleEntry> entries;
/// Categories to be displayed
final List<WalletCategory> categories; final List<WalletCategory> categories;
/// Currency symbol displayed on the chart
final String symbol;
@override @override
State<CategoriesPieChart> createState() => _CategoriesPieChartState(); Widget build(BuildContext context) => PieChart(
}
class _CategoriesPieChartState extends State<CategoriesPieChart> {
int touchedIndex = -1;
@override
Widget build(BuildContext context) => Column(
children: [
SizedBox(
width: MediaQuery.of(context).size.width,
child: Wrap(
alignment: WrapAlignment.center,
spacing: 4,
children: List<Widget>.generate(
widget.categories.length,
(index) => Padding(
padding: const EdgeInsets.all(8),
child: Indicator(
size: touchedIndex == index ? 18 : 16,
color: widget.categories[index].color,
text: widget.categories[index].name,
textStyle: TextStyle(
fontWeight: (touchedIndex == index)
? FontWeight.bold
: FontWeight.normal,
),
),
),
),
),
),
const SizedBox(
height: 5,
),
Expanded(
child: PieChart(
PieChartData( PieChartData(
centerSpaceRadius: double.infinity,
pieTouchData: PieTouchData(
touchCallback: (event, response) {
// Set touchedIndex so we can highlight
// the corresponding indicator
setState(() {
if (!event.isInterestedForInteractions ||
response == null ||
response.touchedSection == null) {
touchedIndex = -1;
return;
}
touchedIndex =
response.touchedSection!.touchedSectionIndex;
});
},
),
sections: List<PieChartSectionData>.generate( sections: List<PieChartSectionData>.generate(
widget.categories.length, categories.length,
(index) => PieChartSectionData( (index) => PieChartSectionData(
title: NumberFormat.compactCurrency(symbol: widget.symbol) value: entries
.format(
widget.entries
.where( .where(
(element) => (element) => element.category.id == categories[index].id)
element.category.id ==
widget.categories[index].id,
)
.fold<double>(
0,
(previousValue, element) =>
previousValue + element.data.amount,
),
),
titleStyle: TextStyle(
color:
widget.categories[index].color.calculateTextColor(),
fontWeight: FontWeight.bold,
),
color: widget.categories[index].color,
value: widget.entries
.where(
(element) =>
element.category.id ==
widget.categories[index].id,
)
.fold<double>( .fold<double>(
0, 0,
(previousValue, element) => (previousValue, element) =>
@ -497,50 +366,5 @@ class _CategoriesPieChartState extends State<CategoriesPieChart> {
), ),
), ),
), ),
),
),
],
);
}
/// Used to indicate which part of a chart is for what
class Indicator extends StatelessWidget {
/// Used to indicate which part of a chart is for what
const Indicator({
required this.size,
required this.color,
required this.text,
this.textStyle = const TextStyle(),
super.key,
});
/// Size of the indicator circle
final double size;
/// Color of the indicator circle
final Color color;
/// Text shown next to the indicator circle
final String text;
final TextStyle textStyle;
@override
Widget build(BuildContext context) => Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: color,
),
),
const SizedBox(
width: 4,
),
Text(text, style: textStyle),
],
); );
} }

View file

@ -1,11 +0,0 @@
import 'package:flutter/material.dart';
/// Used to add [calculateTextColor] to the [Color] class
extension TextColor on Color {
/// Returns if foreground should be white or dark on this [Color]
Color calculateTextColor() {
return ThemeData.estimateBrightnessForColor(this) == Brightness.light
? Colors.black
: Colors.white;
}
}

View file

@ -40,9 +40,8 @@ class _GraphViewState extends State<GraphView> {
} }
List<double> generateChartData(EntryType type) { List<double> generateChartData(EntryType type) {
final d = _selectedDate.add(const Duration(days: 31));
final data = List<double>.filled( final data = List<double>.filled(
yearly ? 12 : DateTime(d.year, d.month, 0).day, yearly ? 12 : DateTime(_selectedDate.year, _selectedDate.month, 0).day,
0, 0,
); );
if (selectedWallet == null) return []; if (selectedWallet == null) return [];
@ -93,47 +92,6 @@ class _GraphViewState extends State<GraphView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
floatingActionButton: Tooltip(
message: AppLocalizations.of(context).changeDate,
child: PlatformButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Theme.of(context).colorScheme.primary,
),
foregroundColor: MaterialStateProperty.all(
Theme.of(context).colorScheme.onPrimary,
),
),
text: yearly
? DateFormat.y(locale).format(_selectedDate)
: DateFormat.yMMMM(locale).format(_selectedDate),
onPressed: () async {
final firstDate = (selectedWallet!.entries
..sort(
(a, b) => a.date.compareTo(b.date),
))
.first
.date;
final newDate = await showDatePicker(
context: context,
initialDate: DateTime(
_selectedDate.year,
_selectedDate.month,
),
firstDate: firstDate,
lastDate: DateTime.now(),
initialEntryMode: yearly
? DatePickerEntryMode.input
: DatePickerEntryMode.calendar,
initialDatePickerMode:
yearly ? DatePickerMode.year : DatePickerMode.day,
);
if (newDate == null) return;
_selectedDate = newDate;
setState(() {});
},
),
),
appBar: AppBar( appBar: AppBar(
title: DropdownButton<int>( title: DropdownButton<int>(
value: value:
@ -179,19 +137,11 @@ class _GraphViewState extends State<GraphView> {
].map((e) => PopupMenuItem(value: e, child: Text(e))).toList(), ].map((e) => PopupMenuItem(value: e, child: Text(e))).toList(),
onSelected: (value) { onSelected: (value) {
if (value == AppLocalizations.of(context).settings) { if (value == AppLocalizations.of(context).settings) {
Navigator.of(context) Navigator.of(context).push(
.push(
platformRoute( platformRoute(
(context) => const SettingsView(), (context) => const SettingsView(),
), ),
) );
.then((value) async {
selectedWallet =
await WalletManager.loadWallet(selectedWallet!.name);
final s = await SharedPreferences.getInstance();
chartType = s.getInt("monthlygraph") ?? 2;
setState(() {});
});
} else if (value == AppLocalizations.of(context).about) { } else if (value == AppLocalizations.of(context).about) {
showAboutDialog( showAboutDialog(
context: context, context: context,
@ -269,10 +219,58 @@ class _GraphViewState extends State<GraphView> {
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: Column( child: Column(
children: [ children: [
PlatformButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Theme.of(context).colorScheme.primary,
),
foregroundColor: MaterialStateProperty.all(
Theme.of(context).colorScheme.onPrimary,
),
),
text: yearly
? DateFormat.y(locale).format(_selectedDate)
: DateFormat.yMMMM(locale)
.format(_selectedDate),
onPressed: () async {
final firstDate = (selectedWallet!.entries
..sort(
(a, b) => a.date.compareTo(b.date),
))
.first
.date;
final lastDate = (selectedWallet!.entries
..sort(
(a, b) => b.date.compareTo(a.date),
))
.first
.date;
final newDate = await showDatePicker(
context: context,
initialDate: DateTime(
_selectedDate.year,
_selectedDate.month,
),
firstDate: firstDate,
lastDate: lastDate,
initialEntryMode: yearly
? DatePickerEntryMode.input
: DatePickerEntryMode.calendar,
initialDatePickerMode: yearly
? DatePickerMode.year
: DatePickerMode.day,
);
if (newDate == null) return;
_selectedDate = newDate;
setState(() {});
},
),
const SizedBox(
height: 5,
),
SizedBox( SizedBox(
width: MediaQuery.of(context).size.width * 0.9, width: MediaQuery.of(context).size.width * 0.9,
height: height: 300,
MediaQuery.of(context).size.height * 0.35,
child: (chartType == null) child: (chartType == null)
? const CircularProgressIndicator() ? const CircularProgressIndicator()
: (chartType == 1) : (chartType == 1)
@ -294,11 +292,8 @@ class _GraphViewState extends State<GraphView> {
) )
: [], : [],
) )
: Padding( : ExpensesLineChart(
padding: const EdgeInsets.all(8), currency: selectedWallet!.currency,
child: ExpensesLineChart(
currency:
selectedWallet!.currency,
date: _selectedDate, date: _selectedDate,
locale: locale ?? "en", locale: locale ?? "en",
yearly: yearly, yearly: yearly,
@ -316,31 +311,10 @@ class _GraphViewState extends State<GraphView> {
: [], : [],
), ),
), ),
),
], ],
), ),
), ),
), ),
const SizedBox(
height: 25,
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color:
Theme.of(context).colorScheme.secondaryContainer,
),
width: MediaQuery.of(context).size.width * 0.95,
height: MediaQuery.of(context).size.height * 0.35,
child: Padding(
padding: const EdgeInsets.all(8),
child: CategoriesPieChart(
symbol: selectedWallet!.currency.symbol,
entries: selectedWallet!.entries,
categories: selectedWallet!.categories,
),
),
),
], ],
), ),
), ),

View file

@ -24,7 +24,6 @@ import 'package:prasule/pw/platformbutton.dart';
import 'package:prasule/pw/platformdialog.dart'; import 'package:prasule/pw/platformdialog.dart';
import 'package:prasule/pw/platformroute.dart'; import 'package:prasule/pw/platformroute.dart';
import 'package:prasule/util/drawer.dart'; import 'package:prasule/util/drawer.dart';
import 'package:prasule/util/text_color.dart';
import 'package:prasule/views/create_entry.dart'; import 'package:prasule/views/create_entry.dart';
import 'package:prasule/views/settings/settings.dart'; import 'package:prasule/views/settings/settings.dart';
import 'package:prasule/views/settings/tessdata_list.dart'; import 'package:prasule/views/settings/tessdata_list.dart';
@ -359,14 +358,14 @@ class _HomeViewState extends State<HomeView> {
leading: Container( leading: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
color: element.category.color, color: Theme.of(context).colorScheme.secondary,
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: Icon( child: Icon(
element.category.icon, element.category.icon,
color: color:
element.category.color.calculateTextColor(), Theme.of(context).colorScheme.onSecondary,
), ),
), ),
), ),

View file

@ -2,8 +2,6 @@
import 'dart:async'; import 'dart:async';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:flex_color_picker/flex_color_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_iconpicker/flutter_iconpicker.dart'; import 'package:flutter_iconpicker/flutter_iconpicker.dart';
@ -11,14 +9,11 @@ import 'package:prasule/api/category.dart';
import 'package:prasule/api/wallet.dart'; import 'package:prasule/api/wallet.dart';
import 'package:prasule/api/walletmanager.dart'; import 'package:prasule/api/walletmanager.dart';
import 'package:prasule/main.dart'; import 'package:prasule/main.dart';
import 'package:prasule/pw/platformbutton.dart';
import 'package:prasule/pw/platformdialog.dart'; import 'package:prasule/pw/platformdialog.dart';
import 'package:prasule/pw/platformfield.dart'; import 'package:prasule/pw/platformfield.dart';
import 'package:prasule/pw/platformroute.dart'; import 'package:prasule/pw/platformroute.dart';
import 'package:prasule/util/text_color.dart';
import 'package:prasule/views/settings/settings.dart'; import 'package:prasule/views/settings/settings.dart';
import 'package:prasule/views/setup.dart'; import 'package:prasule/views/setup.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// Allows adding, editing or removing [WalletCategory]s /// Allows adding, editing or removing [WalletCategory]s
class EditCategoriesView extends StatefulWidget { class EditCategoriesView extends StatefulWidget {
@ -139,64 +134,24 @@ class _EditCategoriesViewState extends State<EditCategoriesView> {
await FlutterIconPicker.showIconPicker( await FlutterIconPicker.showIconPicker(
context, context,
); );
if (icon != null) { if (icon == null) return;
selectedWallet!.categories[i].icon = icon; selectedWallet!.categories[i].icon = icon;
}
final materialEnabled =
(await SharedPreferences.getInstance())
.getBool("useMaterialYou") ??
false;
if (!mounted) return;
await showDialog(
context: context,
builder: (c) => PlatformDialog(
actions: [
PlatformButton(
text: AppLocalizations.of(context).done,
onPressed: () {
Navigator.of(c).pop();
},
),
],
title:
AppLocalizations.of(context).pickColor,
content: Column(
children: [
ColorPicker(
pickersEnabled: {
ColorPickerType.wheel: true,
ColorPickerType.primary: false,
ColorPickerType.custom: false,
ColorPickerType.bw: false,
ColorPickerType.accent:
materialEnabled,
},
color: selectedWallet!
.categories[i].color,
onColorChanged: (color) {
selectedWallet!
.categories[i].color = color;
setState(() {});
},
),
],
),
),
);
await WalletManager.saveWallet(selectedWallet!); await WalletManager.saveWallet(selectedWallet!);
setState(() {}); setState(() {});
}, },
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
color: selectedWallet!.categories[i].color, color:
Theme.of(context).colorScheme.secondary,
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: Icon( child: Icon(
selectedWallet!.categories[i].icon, selectedWallet!.categories[i].icon,
color: selectedWallet!.categories[i].color color: Theme.of(context)
.calculateTextColor(), .colorScheme
.onSecondary,
), ),
), ),
), ),
@ -275,9 +230,6 @@ class _EditCategoriesViewState extends State<EditCategoriesView> {
Icons.question_mark.codePoint, Icons.question_mark.codePoint,
fontFamily: 'MaterialIcons', fontFamily: 'MaterialIcons',
), ),
color: Colors.blueGrey.harmonizeWith(
Theme.of(context).colorScheme.primary,
),
), ),
); );
await WalletManager.saveWallet(selectedWallet!); await WalletManager.saveWallet(selectedWallet!);

View file

@ -1,8 +1,6 @@
// ignore_for_file: inference_failure_on_function_invocation // ignore_for_file: inference_failure_on_function_invocation
import 'package:currency_picker/currency_picker.dart'; import 'package:currency_picker/currency_picker.dart';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:flex_color_picker/flex_color_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@ -15,9 +13,7 @@ import 'package:prasule/pw/platformbutton.dart';
import 'package:prasule/pw/platformdialog.dart'; import 'package:prasule/pw/platformdialog.dart';
import 'package:prasule/pw/platformfield.dart'; import 'package:prasule/pw/platformfield.dart';
import 'package:prasule/pw/platformroute.dart'; import 'package:prasule/pw/platformroute.dart';
import 'package:prasule/util/text_color.dart';
import 'package:prasule/views/home.dart'; import 'package:prasule/views/home.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// View that shows on first-time setup /// View that shows on first-time setup
class SetupView extends StatefulWidget { class SetupView extends StatefulWidget {
@ -62,7 +58,6 @@ class _SetupViewState extends State<SetupView> {
Icons.payments.codePoint, Icons.payments.codePoint,
fontFamily: 'MaterialIcons', fontFamily: 'MaterialIcons',
), ),
color: Theme.of(context).colorScheme.secondary,
), ),
WalletCategory( WalletCategory(
name: AppLocalizations.of(context).categoryHealth, name: AppLocalizations.of(context).categoryHealth,
@ -71,31 +66,23 @@ class _SetupViewState extends State<SetupView> {
Icons.medical_information.codePoint, Icons.medical_information.codePoint,
fontFamily: 'MaterialIcons', fontFamily: 'MaterialIcons',
), ),
color: Colors.red.shade700
.harmonizeWith(Theme.of(context).colorScheme.primary),
), ),
WalletCategory( WalletCategory(
name: AppLocalizations.of(context).categoryCar, name: AppLocalizations.of(context).categoryCar,
id: 2, id: 2,
icon: icon:
IconData(Icons.car_repair.codePoint, fontFamily: 'MaterialIcons'), IconData(Icons.car_repair.codePoint, fontFamily: 'MaterialIcons'),
color: Colors.purple
.harmonizeWith(Theme.of(context).colorScheme.primary),
), ),
WalletCategory( WalletCategory(
name: AppLocalizations.of(context).categoryFood, name: AppLocalizations.of(context).categoryFood,
id: 3, id: 3,
icon: icon:
IconData(Icons.restaurant.codePoint, fontFamily: 'MaterialIcons'), IconData(Icons.restaurant.codePoint, fontFamily: 'MaterialIcons'),
color: Colors.green.shade700
.harmonizeWith(Theme.of(context).colorScheme.primary),
), ),
WalletCategory( WalletCategory(
name: AppLocalizations.of(context).categoryTravel, name: AppLocalizations.of(context).categoryTravel,
id: 4, id: 4,
icon: IconData(Icons.train.codePoint, fontFamily: 'MaterialIcons'), icon: IconData(Icons.train.codePoint, fontFamily: 'MaterialIcons'),
color: Colors.orange.shade700
.harmonizeWith(Theme.of(context).colorScheme.primary),
), ),
]; ];
setState(() {}); setState(() {});
@ -295,61 +282,24 @@ class _SetupViewState extends State<SetupView> {
await FlutterIconPicker.showIconPicker( await FlutterIconPicker.showIconPicker(
context, context,
); );
if (icon != null) categories[i].icon = icon; if (icon == null) return;
final materialEnabled = categories[i].icon = icon;
(await SharedPreferences.getInstance())
.getBool("useMaterialYou") ??
false;
if (!mounted) return;
await showDialog(
context: context,
builder: (c) => PlatformDialog(
actions: [
PlatformButton(
text: AppLocalizations.of(context)
.done,
onPressed: () {
Navigator.of(c).pop();
},
),
],
title: AppLocalizations.of(context)
.pickColor,
content: Column(
children: [
ColorPicker(
pickersEnabled: {
ColorPickerType.wheel: true,
ColorPickerType.primary: false,
ColorPickerType.custom: false,
ColorPickerType.bw: false,
ColorPickerType.accent:
materialEnabled,
},
color: categories[i].color,
onColorChanged: (color) {
categories[i].color = color;
setState(() {});
},
),
],
),
),
);
setState(() {}); setState(() {});
}, },
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
color: categories[i].color, color: Theme.of(context)
.colorScheme
.secondary,
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: Icon( child: Icon(
categories[i].icon, categories[i].icon,
color: categories[i] color: Theme.of(context)
.color .colorScheme
.calculateTextColor(), .onSecondary,
), ),
), ),
), ),
@ -372,9 +322,8 @@ class _SetupViewState extends State<SetupView> {
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
if (controller.text.isEmpty) { if (controller.text.isEmpty)
return; return;
}
categories[i].name = categories[i].name =
controller.text; controller.text;
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -398,8 +347,7 @@ class _SetupViewState extends State<SetupView> {
content: SizedBox( content: SizedBox(
width: 400, width: 400,
child: PlatformField( child: PlatformField(
controller: controller, controller: controller),
),
), ),
), ),
); );
@ -407,8 +355,7 @@ class _SetupViewState extends State<SetupView> {
child: Text( child: Text(
categories[i].name, categories[i].name,
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold),
),
), ),
), ),
), ),
@ -432,9 +379,6 @@ class _SetupViewState extends State<SetupView> {
Icons.question_mark.codePoint, Icons.question_mark.codePoint,
fontFamily: 'MaterialIcons', fontFamily: 'MaterialIcons',
), ),
color: Colors.blueGrey.harmonizeWith(
Theme.of(context).colorScheme.primary,
),
), ),
); );
setState(() {}); setState(() {});

View file

@ -21,10 +21,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: archive name: archive
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.4.10" version: "3.4.9"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -321,22 +321,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.66.0" version: "0.66.0"
flex_color_picker:
dependency: "direct main"
description:
name: flex_color_picker
sha256: f37476ab3e80dcaca94e428e159944d465dd16312fda9ff41e07e86f04bfa51c
url: "https://pub.dev"
source: hosted
version: "3.3.0"
flex_seed_scheme:
dependency: transitive
description:
name: flex_seed_scheme
sha256: "29c12aba221eb8a368a119685371381f8035011d18de5ba277ad11d7dfb8657f"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -547,26 +531,26 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: image_picker name: image_picker
sha256: "340efe08645537d6b088a30620ee5752298b1630f23a829181172610b868262b" sha256: fc712337719239b0b6e41316aa133350b078fa39b6cbd706b61f3fd421b03c77
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.6" version: "1.0.5"
image_picker_android: image_picker_android:
dependency: transitive dependency: transitive
description: description:
name: image_picker_android name: image_picker_android
sha256: "1a27bf4cc0330389cebe465bab08fe6dec97e44015b4899637344bb7297759ec" sha256: ecdc963d2aa67af5195e723a40580f802d4392e31457a12a562b3e2bd6a396fe
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.9+2" version: "0.8.9+1"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
name: image_picker_for_web name: image_picker_for_web
sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3 sha256: "50bc9ae6a77eea3a8b11af5eb6c661eeb858fdd2f734c2a4fd17086922347ef7"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.2" version: "3.0.1"
image_picker_ios: image_picker_ios:
dependency: transitive dependency: transitive
description: description:
@ -595,10 +579,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image_picker_platform_interface name: image_picker_platform_interface
sha256: "0e827c156e3a90edd3bbe7f6de048b39247b16e58173b08a835b7eb00aba239e" sha256: ed9b00e63977c93b0d2d2b343685bed9c324534ba5abafbb3dfbd6a780b1b514
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.9.2" version: "2.9.1"
image_picker_windows: image_picker_windows:
dependency: transitive dependency: transitive
description: description:
@ -776,10 +760,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: path_provider_android name: path_provider_android
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.2" version: "2.2.1"
path_provider_foundation: path_provider_foundation:
dependency: transitive dependency: transitive
description: description:
@ -832,10 +816,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: plugin_platform_interface name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.8" version: "2.1.7"
pointycastle: pointycastle:
dependency: transitive dependency: transitive
description: description:
@ -1181,18 +1165,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.2.0" version: "5.1.1"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
name: xdg_directories name: xdg_directories
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.4" version: "1.0.3"
xml: xml:
dependency: transitive dependency: transitive
description: description:

View file

@ -18,7 +18,6 @@ dependencies:
dio: ^5.3.0 dio: ^5.3.0
dynamic_color: ^1.6.6 dynamic_color: ^1.6.6
fl_chart: ^0.66.0 fl_chart: ^0.66.0
flex_color_picker: ^3.3.0
flutter: flutter:
sdk: flutter sdk: flutter
flutter_iconpicker: ^3.2.4 flutter_iconpicker: ^3.2.4