feat: allow setting a color for a category
This commit is contained in:
parent
2d43ad5886
commit
4b035e0724
11 changed files with 188 additions and 41 deletions
|
@ -2,6 +2,7 @@
|
|||
- Add settings view for editing wallet categories
|
||||
- Change code according to more aggressive linting
|
||||
- Create a default "no category" category, mainly to store entries with removed categories
|
||||
- Categories now have changeable colors assigned to them
|
||||
# 1.0.0-alpha+2
|
||||
- Fixed localization issues
|
||||
- Added graphs for expenses and income per month/year
|
||||
|
|
|
@ -11,6 +11,7 @@ class WalletCategory {
|
|||
required this.name,
|
||||
required this.id,
|
||||
required this.icon,
|
||||
required this.color,
|
||||
});
|
||||
|
||||
/// Connects generated fromJson method
|
||||
|
@ -27,6 +28,10 @@ class WalletCategory {
|
|||
@JsonKey(fromJson: _iconDataFromJson, toJson: _iconDataToJson)
|
||||
IconData icon;
|
||||
|
||||
/// The color that will be displayed with entry
|
||||
@JsonKey(fromJson: _colorFromJson, toJson: _colorToJson)
|
||||
Color color;
|
||||
|
||||
/// Connects generated toJson method
|
||||
Map<String, dynamic> toJson() => _$WalletCategoryToJson(this);
|
||||
|
||||
|
@ -42,6 +47,9 @@ Map<String, dynamic> _iconDataToJson(IconData icon) =>
|
|||
IconData _iconDataFromJson(Map<String, dynamic> data) =>
|
||||
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
|
||||
enum EntryType {
|
||||
/// Expense
|
||||
|
|
|
@ -11,6 +11,7 @@ WalletCategory _$WalletCategoryFromJson(Map<String, dynamic> json) =>
|
|||
name: json['name'] as String,
|
||||
id: json['id'] as int,
|
||||
icon: _iconDataFromJson(json['icon'] as Map<String, dynamic>),
|
||||
color: _colorFromJson(json['color'] as int),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$WalletCategoryToJson(WalletCategory instance) =>
|
||||
|
@ -18,4 +19,5 @@ Map<String, dynamic> _$WalletCategoryToJson(WalletCategory instance) =>
|
|||
'name': instance.name,
|
||||
'id': instance.id,
|
||||
'icon': _iconDataToJson(instance.icon),
|
||||
'color': _colorToJson(instance.color),
|
||||
};
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
"editCategories":"Upravit kategorie",
|
||||
"editCategoriesDesc":"Přidat, upravit nebo odebrat kategorii z peněženky",
|
||||
"wallet":"Peněženka",
|
||||
"noCategory":"Žádná kategorie"
|
||||
|
||||
"noCategory":"Žádná kategorie",
|
||||
"done":"Hotovo",
|
||||
"pickColor":"Zvolte barvu"
|
||||
}
|
|
@ -158,5 +158,7 @@
|
|||
"editCategories":"Edit categories",
|
||||
"editCategoriesDesc":"Add, edit or remove categories from a wallet",
|
||||
"wallet":"Wallet",
|
||||
"noCategory":"No category"
|
||||
"noCategory":"No category",
|
||||
"done":"Done",
|
||||
"pickColor":"Pick a color"
|
||||
}
|
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;
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import 'package:prasule/pw/platformbutton.dart';
|
|||
import 'package:prasule/pw/platformdialog.dart';
|
||||
import 'package:prasule/pw/platformroute.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/settings/settings.dart';
|
||||
import 'package:prasule/views/settings/tessdata_list.dart';
|
||||
|
@ -358,14 +359,14 @@ class _HomeViewState extends State<HomeView> {
|
|||
leading: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
color: element.category.color,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Icon(
|
||||
element.category.icon,
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSecondary,
|
||||
element.category.color.calculateTextColor(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
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_gen/gen_l10n/app_localizations.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/walletmanager.dart';
|
||||
import 'package:prasule/main.dart';
|
||||
import 'package:prasule/pw/platformbutton.dart';
|
||||
import 'package:prasule/pw/platformdialog.dart';
|
||||
import 'package:prasule/pw/platformfield.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/setup.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// Allows adding, editing or removing [WalletCategory]s
|
||||
class EditCategoriesView extends StatefulWidget {
|
||||
|
@ -134,24 +139,64 @@ class _EditCategoriesViewState extends State<EditCategoriesView> {
|
|||
await FlutterIconPicker.showIconPicker(
|
||||
context,
|
||||
);
|
||||
if (icon == null) return;
|
||||
selectedWallet!.categories[i].icon = icon;
|
||||
if (icon != null) {
|
||||
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!);
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
color:
|
||||
Theme.of(context).colorScheme.secondary,
|
||||
color: selectedWallet!.categories[i].color,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Icon(
|
||||
selectedWallet!.categories[i].icon,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSecondary,
|
||||
color: selectedWallet!.categories[i].color
|
||||
.calculateTextColor(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -230,6 +275,9 @@ class _EditCategoriesViewState extends State<EditCategoriesView> {
|
|||
Icons.question_mark.codePoint,
|
||||
fontFamily: 'MaterialIcons',
|
||||
),
|
||||
color: Colors.blueGrey.harmonizeWith(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
);
|
||||
await WalletManager.saveWallet(selectedWallet!);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// ignore_for_file: inference_failure_on_function_invocation
|
||||
|
||||
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/services.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/platformfield.dart';
|
||||
import 'package:prasule/pw/platformroute.dart';
|
||||
import 'package:prasule/util/text_color.dart';
|
||||
import 'package:prasule/views/home.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// View that shows on first-time setup
|
||||
class SetupView extends StatefulWidget {
|
||||
|
@ -58,6 +62,7 @@ class _SetupViewState extends State<SetupView> {
|
|||
Icons.payments.codePoint,
|
||||
fontFamily: 'MaterialIcons',
|
||||
),
|
||||
color: Colors.transparent,
|
||||
),
|
||||
WalletCategory(
|
||||
name: AppLocalizations.of(context).categoryHealth,
|
||||
|
@ -66,23 +71,31 @@ class _SetupViewState extends State<SetupView> {
|
|||
Icons.medical_information.codePoint,
|
||||
fontFamily: 'MaterialIcons',
|
||||
),
|
||||
color: Colors.red.shade700
|
||||
.harmonizeWith(Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
WalletCategory(
|
||||
name: AppLocalizations.of(context).categoryCar,
|
||||
id: 2,
|
||||
icon:
|
||||
IconData(Icons.car_repair.codePoint, fontFamily: 'MaterialIcons'),
|
||||
color: Colors.purple
|
||||
.harmonizeWith(Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
WalletCategory(
|
||||
name: AppLocalizations.of(context).categoryFood,
|
||||
id: 3,
|
||||
icon:
|
||||
IconData(Icons.restaurant.codePoint, fontFamily: 'MaterialIcons'),
|
||||
color: Colors.green.shade700
|
||||
.harmonizeWith(Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
WalletCategory(
|
||||
name: AppLocalizations.of(context).categoryTravel,
|
||||
id: 4,
|
||||
icon: IconData(Icons.train.codePoint, fontFamily: 'MaterialIcons'),
|
||||
color: Colors.orange.shade700
|
||||
.harmonizeWith(Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
];
|
||||
setState(() {});
|
||||
|
@ -282,24 +295,61 @@ class _SetupViewState extends State<SetupView> {
|
|||
await FlutterIconPicker.showIconPicker(
|
||||
context,
|
||||
);
|
||||
if (icon == null) return;
|
||||
categories[i].icon = icon;
|
||||
if (icon != null) 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(() {});
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondary,
|
||||
color: categories[i].color,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Icon(
|
||||
categories[i].icon,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSecondary,
|
||||
color: categories[i]
|
||||
.color
|
||||
.calculateTextColor(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -322,8 +372,9 @@ class _SetupViewState extends State<SetupView> {
|
|||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
if (controller.text.isEmpty)
|
||||
if (controller.text.isEmpty) {
|
||||
return;
|
||||
}
|
||||
categories[i].name =
|
||||
controller.text;
|
||||
Navigator.of(context).pop();
|
||||
|
@ -347,7 +398,8 @@ class _SetupViewState extends State<SetupView> {
|
|||
content: SizedBox(
|
||||
width: 400,
|
||||
child: PlatformField(
|
||||
controller: controller),
|
||||
controller: controller,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -355,7 +407,8 @@ class _SetupViewState extends State<SetupView> {
|
|||
child: Text(
|
||||
categories[i].name,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold),
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -379,6 +432,9 @@ class _SetupViewState extends State<SetupView> {
|
|||
Icons.question_mark.codePoint,
|
||||
fontFamily: 'MaterialIcons',
|
||||
),
|
||||
color: Colors.blueGrey.harmonizeWith(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
);
|
||||
setState(() {});
|
||||
|
|
52
pubspec.lock
52
pubspec.lock
|
@ -21,10 +21,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
|
||||
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.9"
|
||||
version: "3.4.10"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -321,6 +321,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -531,26 +547,26 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: image_picker
|
||||
sha256: fc712337719239b0b6e41316aa133350b078fa39b6cbd706b61f3fd421b03c77
|
||||
sha256: "340efe08645537d6b088a30620ee5752298b1630f23a829181172610b868262b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
version: "1.0.6"
|
||||
image_picker_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_android
|
||||
sha256: ecdc963d2aa67af5195e723a40580f802d4392e31457a12a562b3e2bd6a396fe
|
||||
sha256: "1a27bf4cc0330389cebe465bab08fe6dec97e44015b4899637344bb7297759ec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.9+1"
|
||||
version: "0.8.9+2"
|
||||
image_picker_for_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_for_web
|
||||
sha256: "50bc9ae6a77eea3a8b11af5eb6c661eeb858fdd2f734c2a4fd17086922347ef7"
|
||||
sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.0.2"
|
||||
image_picker_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -579,10 +595,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_platform_interface
|
||||
sha256: ed9b00e63977c93b0d2d2b343685bed9c324534ba5abafbb3dfbd6a780b1b514
|
||||
sha256: "0e827c156e3a90edd3bbe7f6de048b39247b16e58173b08a835b7eb00aba239e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.9.1"
|
||||
version: "2.9.2"
|
||||
image_picker_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -760,10 +776,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72
|
||||
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "2.2.2"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -816,10 +832,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.7"
|
||||
version: "2.1.8"
|
||||
pointycastle:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1165,18 +1181,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
|
||||
sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
version: "5.2.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
|
||||
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
version: "1.0.4"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -18,6 +18,7 @@ dependencies:
|
|||
dio: ^5.3.0
|
||||
dynamic_color: ^1.6.6
|
||||
fl_chart: ^0.66.0
|
||||
flex_color_picker: ^3.3.0
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_iconpicker: ^3.2.4
|
||||
|
|
Loading…
Reference in a new issue