Compare commits
2 commits
2d43ad5886
...
d5e94d63d7
Author | SHA1 | Date | |
---|---|---|---|
|
d5e94d63d7 | ||
|
4b035e0724 |
15 changed files with 503 additions and 142 deletions
|
@ -2,6 +2,8 @@
|
||||||
- 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
|
||||||
|
|
|
@ -11,6 +11,7 @@ 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
|
||||||
|
@ -27,6 +28,10 @@ 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);
|
||||||
|
|
||||||
|
@ -42,6 +47,9 @@ 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
|
||||||
|
|
|
@ -11,6 +11,7 @@ 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) =>
|
||||||
|
@ -18,4 +19,5 @@ 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),
|
||||||
};
|
};
|
||||||
|
|
|
@ -78,6 +78,8 @@
|
||||||
"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"
|
||||||
}
|
}
|
|
@ -158,5 +158,8 @@
|
||||||
"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"
|
||||||
}
|
}
|
|
@ -5,7 +5,8 @@ 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 for the respective platform UI library
|
/// Abstract class used to create widgets
|
||||||
|
/// for the respective platform UI library
|
||||||
const PlatformWidget({super.key});
|
const PlatformWidget({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
8
lib/util/get_last_date.dart
Normal file
8
lib/util/get_last_date.dart
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/// 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,9 +3,12 @@ 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:intl/intl.dart';
|
import 'package:prasule/main.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 {
|
||||||
|
@ -72,7 +75,9 @@ class ExpensesLineChart extends StatelessWidget {
|
||||||
getTooltipItems: (spots) => List<LineTooltipItem>.generate(
|
getTooltipItems: (spots) => List<LineTooltipItem>.generate(
|
||||||
spots.length,
|
spots.length,
|
||||||
(index) => LineTooltipItem(
|
(index) => LineTooltipItem(
|
||||||
(spots[index].barIndex == 0)
|
// Changes what's rendered on the tooltip
|
||||||
|
// 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(
|
||||||
|
@ -94,7 +99,7 @@ class ExpensesLineChart extends StatelessWidget {
|
||||||
name: currency.name,
|
name: currency.name,
|
||||||
).format(spots[index].y),
|
).format(spots[index].y),
|
||||||
))
|
))
|
||||||
: (yearly
|
: (yearly // expense chart
|
||||||
? AppLocalizations.of(context).expensesForMonth(
|
? AppLocalizations.of(context).expensesForMonth(
|
||||||
DateFormat.MMMM(locale).format(
|
DateFormat.MMMM(locale).format(
|
||||||
DateTime(
|
DateTime(
|
||||||
|
@ -116,12 +121,25 @@ 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 ? 12 : DateTime(date.year, date.month, 0).day.toDouble(),
|
maxX: yearly
|
||||||
|
? 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,
|
||||||
|
@ -133,10 +151,13 @@ class ExpensesLineChart extends StatelessWidget {
|
||||||
isStrokeCapRound: true,
|
isStrokeCapRound: true,
|
||||||
dotData: const FlDotData(show: false),
|
dotData: const FlDotData(show: false),
|
||||||
belowBarData: BarAreaData(),
|
belowBarData: BarAreaData(),
|
||||||
color: Colors.green
|
color:
|
||||||
.harmonizeWith(Theme.of(context).colorScheme.secondary),
|
(MediaQuery.of(context).platformBrightness == Brightness.dark)
|
||||||
|
? Colors.green.shade300
|
||||||
|
: Colors.green
|
||||||
|
.harmonizeWith(Theme.of(context).colorScheme.primary),
|
||||||
spots: List.generate(
|
spots: List.generate(
|
||||||
yearly ? 12 : DateTime(date.year, date.month, 0).day,
|
yearly ? 12 : date.lastDay,
|
||||||
(index) => FlSpot(index.toDouble(), incomeData[index]),
|
(index) => FlSpot(index.toDouble(), incomeData[index]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -147,17 +168,37 @@ class ExpensesLineChart extends StatelessWidget {
|
||||||
isStrokeCapRound: true,
|
isStrokeCapRound: true,
|
||||||
dotData: const FlDotData(show: false),
|
dotData: const FlDotData(show: false),
|
||||||
belowBarData: BarAreaData(),
|
belowBarData: BarAreaData(),
|
||||||
color: Colors.red
|
color:
|
||||||
.harmonizeWith(Theme.of(context).colorScheme.secondary),
|
(MediaQuery.of(context).platformBrightness == Brightness.dark)
|
||||||
|
? Colors.red.shade300
|
||||||
|
: Colors.red
|
||||||
|
.harmonizeWith(Theme.of(context).colorScheme.primary),
|
||||||
spots: List.generate(
|
spots: List.generate(
|
||||||
yearly ? 12 : DateTime(date.year, date.month, 0).day,
|
yearly
|
||||||
(index) => FlSpot(index.toDouble() + 1, expenseData[index]),
|
? 12
|
||||||
|
: 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,
|
||||||
|
@ -319,7 +360,7 @@ class ExpensesBarChart extends StatelessWidget {
|
||||||
minY: 0,
|
minY: 0,
|
||||||
maxY: maxY,
|
maxY: maxY,
|
||||||
barGroups: List<BarChartGroupData>.generate(
|
barGroups: List<BarChartGroupData>.generate(
|
||||||
yearly ? 12 : DateTime(date.year, date.month, 0).day,
|
yearly ? 12 : date.lastDay - 1,
|
||||||
(index) => BarChartGroupData(
|
(index) => BarChartGroupData(
|
||||||
x: index,
|
x: index,
|
||||||
barRods: [
|
barRods: [
|
||||||
|
@ -327,13 +368,13 @@ class ExpensesBarChart extends StatelessWidget {
|
||||||
BarChartRodData(
|
BarChartRodData(
|
||||||
toY: incomeData[index],
|
toY: incomeData[index],
|
||||||
color: Colors.green
|
color: Colors.green
|
||||||
.harmonizeWith(Theme.of(context).colorScheme.secondary),
|
.harmonizeWith(Theme.of(context).colorScheme.primary),
|
||||||
),
|
),
|
||||||
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.secondary),
|
.harmonizeWith(Theme.of(context).colorScheme.primary),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -342,29 +383,164 @@ class ExpensesBarChart extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class CategoriesPieChart extends StatelessWidget {
|
/// [PieChart] used to display expenses/income visualized
|
||||||
const CategoriesPieChart(
|
/// under their respective category
|
||||||
{super.key, required this.entries, required this.categories});
|
class CategoriesPieChart extends StatefulWidget {
|
||||||
|
/// [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
|
||||||
Widget build(BuildContext context) => PieChart(
|
State<CategoriesPieChart> createState() => _CategoriesPieChartState();
|
||||||
PieChartData(
|
}
|
||||||
sections: List<PieChartSectionData>.generate(
|
|
||||||
categories.length,
|
class _CategoriesPieChartState extends State<CategoriesPieChart> {
|
||||||
(index) => PieChartSectionData(
|
int touchedIndex = -1;
|
||||||
value: entries
|
|
||||||
.where(
|
@override
|
||||||
(element) => element.category.id == categories[index].id)
|
Widget build(BuildContext context) => Column(
|
||||||
.fold<double>(
|
children: [
|
||||||
0,
|
SizedBox(
|
||||||
(previousValue, element) =>
|
width: MediaQuery.of(context).size.width,
|
||||||
previousValue + element.data.amount,
|
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(
|
||||||
|
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(
|
||||||
|
widget.categories.length,
|
||||||
|
(index) => PieChartSectionData(
|
||||||
|
title: NumberFormat.compactCurrency(symbol: widget.symbol)
|
||||||
|
.format(
|
||||||
|
widget.entries
|
||||||
|
.where(
|
||||||
|
(element) =>
|
||||||
|
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>(
|
||||||
|
0,
|
||||||
|
(previousValue, element) =>
|
||||||
|
previousValue + element.data.amount,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
11
lib/util/text_color.dart
Normal file
11
lib/util/text_color.dart
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,8 +40,9 @@ 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(_selectedDate.year, _selectedDate.month, 0).day,
|
yearly ? 12 : DateTime(d.year, d.month, 0).day,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
if (selectedWallet == null) return [];
|
if (selectedWallet == null) return [];
|
||||||
|
@ -92,6 +93,47 @@ 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:
|
||||||
|
@ -137,11 +179,19 @@ 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).push(
|
Navigator.of(context)
|
||||||
|
.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,
|
||||||
|
@ -219,58 +269,10 @@ 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: 300,
|
height:
|
||||||
|
MediaQuery.of(context).size.height * 0.35,
|
||||||
child: (chartType == null)
|
child: (chartType == null)
|
||||||
? const CircularProgressIndicator()
|
? const CircularProgressIndicator()
|
||||||
: (chartType == 1)
|
: (chartType == 1)
|
||||||
|
@ -292,29 +294,53 @@ class _GraphViewState extends State<GraphView> {
|
||||||
)
|
)
|
||||||
: [],
|
: [],
|
||||||
)
|
)
|
||||||
: ExpensesLineChart(
|
: Padding(
|
||||||
currency: selectedWallet!.currency,
|
padding: const EdgeInsets.all(8),
|
||||||
date: _selectedDate,
|
child: ExpensesLineChart(
|
||||||
locale: locale ?? "en",
|
currency:
|
||||||
yearly: yearly,
|
selectedWallet!.currency,
|
||||||
expenseData: (graphTypeSet
|
date: _selectedDate,
|
||||||
.contains("expense"))
|
locale: locale ?? "en",
|
||||||
? generateChartData(
|
yearly: yearly,
|
||||||
EntryType.expense,
|
expenseData: (graphTypeSet
|
||||||
)
|
.contains("expense"))
|
||||||
: [],
|
? generateChartData(
|
||||||
incomeData: (graphTypeSet
|
EntryType.expense,
|
||||||
.contains("income"))
|
)
|
||||||
? generateChartData(
|
: [],
|
||||||
EntryType.income,
|
incomeData: (graphTypeSet
|
||||||
)
|
.contains("income"))
|
||||||
: [],
|
? generateChartData(
|
||||||
|
EntryType.income,
|
||||||
|
)
|
||||||
|
: [],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -24,6 +24,7 @@ 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';
|
||||||
|
@ -358,14 +359,14 @@ class _HomeViewState extends State<HomeView> {
|
||||||
leading: Container(
|
leading: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
color: element.category.color,
|
||||||
),
|
),
|
||||||
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:
|
||||||
Theme.of(context).colorScheme.onSecondary,
|
element.category.color.calculateTextColor(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
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';
|
||||||
|
@ -9,11 +11,14 @@ 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 {
|
||||||
|
@ -134,24 +139,64 @@ class _EditCategoriesViewState extends State<EditCategoriesView> {
|
||||||
await FlutterIconPicker.showIconPicker(
|
await FlutterIconPicker.showIconPicker(
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
if (icon == null) return;
|
if (icon != null) {
|
||||||
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:
|
color: selectedWallet!.categories[i].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: Theme.of(context)
|
color: selectedWallet!.categories[i].color
|
||||||
.colorScheme
|
.calculateTextColor(),
|
||||||
.onSecondary,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -230,6 +275,9 @@ 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!);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// 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';
|
||||||
|
@ -13,7 +15,9 @@ 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 {
|
||||||
|
@ -58,6 +62,7 @@ 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,
|
||||||
|
@ -66,23 +71,31 @@ 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(() {});
|
||||||
|
@ -282,24 +295,61 @@ class _SetupViewState extends State<SetupView> {
|
||||||
await FlutterIconPicker.showIconPicker(
|
await FlutterIconPicker.showIconPicker(
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
if (icon == null) return;
|
if (icon != null) categories[i].icon = icon;
|
||||||
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: 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: Theme.of(context)
|
color: categories[i].color,
|
||||||
.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: Theme.of(context)
|
color: categories[i]
|
||||||
.colorScheme
|
.color
|
||||||
.onSecondary,
|
.calculateTextColor(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -322,8 +372,9 @@ 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();
|
||||||
|
@ -347,7 +398,8 @@ class _SetupViewState extends State<SetupView> {
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
width: 400,
|
width: 400,
|
||||||
child: PlatformField(
|
child: PlatformField(
|
||||||
controller: controller),
|
controller: controller,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -355,7 +407,8 @@ 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,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -379,6 +432,9 @@ 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(() {});
|
||||||
|
|
52
pubspec.lock
52
pubspec.lock
|
@ -21,10 +21,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
|
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.9"
|
version: "3.4.10"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -321,6 +321,22 @@ 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
|
||||||
|
@ -531,26 +547,26 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: image_picker
|
name: image_picker
|
||||||
sha256: fc712337719239b0b6e41316aa133350b078fa39b6cbd706b61f3fd421b03c77
|
sha256: "340efe08645537d6b088a30620ee5752298b1630f23a829181172610b868262b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.6"
|
||||||
image_picker_android:
|
image_picker_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_android
|
name: image_picker_android
|
||||||
sha256: ecdc963d2aa67af5195e723a40580f802d4392e31457a12a562b3e2bd6a396fe
|
sha256: "1a27bf4cc0330389cebe465bab08fe6dec97e44015b4899637344bb7297759ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.9+1"
|
version: "0.8.9+2"
|
||||||
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: "50bc9ae6a77eea3a8b11af5eb6c661eeb858fdd2f734c2a4fd17086922347ef7"
|
sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.2"
|
||||||
image_picker_ios:
|
image_picker_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -579,10 +595,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_platform_interface
|
name: image_picker_platform_interface
|
||||||
sha256: ed9b00e63977c93b0d2d2b343685bed9c324534ba5abafbb3dfbd6a780b1b514
|
sha256: "0e827c156e3a90edd3bbe7f6de048b39247b16e58173b08a835b7eb00aba239e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.9.1"
|
version: "2.9.2"
|
||||||
image_picker_windows:
|
image_picker_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -760,10 +776,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
|
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.2.2"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -816,10 +832,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: plugin_platform_interface
|
name: plugin_platform_interface
|
||||||
sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
|
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.7"
|
version: "2.1.8"
|
||||||
pointycastle:
|
pointycastle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1165,18 +1181,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
|
sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.1.1"
|
version: "5.2.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
|
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "1.0.4"
|
||||||
xml:
|
xml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -18,6 +18,7 @@ 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
|
||||||
|
|
Loading…
Reference in a new issue