5 Commits

Author SHA1 Message Date
n8r 1f39653594 Added package rename file 2024-06-02 21:53:53 -06:00
n8r e9aad1cdf5 Added 512x512 icon for flatpak 2024-06-02 18:56:37 -06:00
n8r 2c0ae00103 Added flatpak packaging stuffs 2024-06-02 17:58:51 -06:00
n8r 000f409052 Removed helpers lib and updated to flutter 3.22 2024-05-30 20:44:06 -06:00
n8r 64baa04a47 Added gen files 2024-05-30 19:11:46 -06:00
44 changed files with 636 additions and 601 deletions
+2 -1
View File
@@ -44,5 +44,6 @@ app.*.map.json
/android/app/release
#generated files
lib/**/*.g.dart
# lib/**/*.g.dart
.direnv/
*.tar.gz
+1 -1
View File
@@ -5,4 +5,4 @@ linux
.metadata
analysis_options.yaml
pubspec.lock
// lib/**/*.g.dart
lib/**/*.g.dart
+1 -1
View File
@@ -44,7 +44,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.rluv"
applicationId "com.fosscat.rluv"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
+1 -1
View File
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.rluv">
package="com.fosscat.rluv">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
+1 -1
View File
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.rluv">
package="com.fosscat.rluv">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
@@ -1,4 +1,4 @@
package com.example.rluv
package com.fosscat.rluv
import io.flutter.embedding.android.FlutterActivity
+1 -1
View File
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.rluv">
package="com.fosscat.rluv">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
# Build the Flutter app and package into an archive.
# Exit if any command fails
set -e
# Echo all commands for debug purposes
set -x
projectName=rluvApp
archiveName=$projectName-Linux-Portable.tar.gz
baseDir=$(pwd)
# ----------------------------- Build Flutter app ---------------------------- #
flutter pub get
flutter build linux
cd build/linux/arm64/release/bundle || exit
tar -czaf $archiveName ./*
mv $archiveName "$baseDir"/
Generated
+4 -4
View File
@@ -114,11 +114,11 @@
},
"nixpkgs_2": {
"locked": {
"lastModified": 1712608508,
"narHash": "sha256-vMZ5603yU0wxgyQeHJryOI+O61yrX2AHwY6LOFyV1gM=",
"lastModified": 1716948383,
"narHash": "sha256-SzDKxseEcHR5KzPXLwsemyTR/kaM9whxeiJohbL04rs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4cba8b53da471aea2ab2b0c1f30a81e7c451f4b6",
"rev": "ad57eef4ef0659193044870c731987a6df5cf56b",
"type": "github"
},
"original": {
@@ -183,4 +183,4 @@
},
"root": "root",
"version": 7
}
}
-22
View File
@@ -32,21 +32,6 @@
platforms-android-34
emulator
]);
# See below link for configuring ruby env with bundix
# https://github.com/the-nix-way/nix-flake-dev-environments/tree/main/ruby-on-rails
rubyEnv = pkgs.bundlerEnv {
name = "ruby-env";
inherit (pkgs) ruby;
gemdir = ./android;
};
# buildNodeJs = pkgs.callPackage "${nixpkgs}/pkgs/development/web/nodejs/nodejs.nix" {
# python = pkgs.python3;
# };
# nodejs = buildNodeJs {
# enableNpm = true;
# version = "20.0.0";
# sha256 = "sha256-Q5xxqi84woYWV7+lOOmRkaVxJYBmy/1FSFhgScgTQZA=";
# };
in
pkgs.mkShell {
# Fix an issue with Flutter using an older version of aapt2, which does not know
@@ -60,13 +45,6 @@
# pkg-config
jdk17
android-sdk
rubyEnv
rubyEnv.wrappedRuby
# fastlane
bundix
# gems
firebase-tools
# nodejs
];
shellHook = ''
export PATH="$PATH":"$HOME/.pub-cache/bin"
+3 -3
View File
@@ -296,7 +296,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.rluv;
PRODUCT_BUNDLE_IDENTIFIER = com.fosscat.rluv;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -424,7 +424,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.rluv;
PRODUCT_BUNDLE_IDENTIFIER = com.fosscat.rluv;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -446,7 +446,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.rluv;
PRODUCT_BUNDLE_IDENTIFIER = com.fosscat.rluv;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
+2 -2
View File
@@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Rluv</string>
<string>rluv</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@@ -13,7 +13,7 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>rluv</string>
<string>fosscatrluv</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:helpers/helpers/misc_build/build_media.dart';
import 'package:rluv/features/account/signup.dart';
import 'package:rluv/global/styles.dart';
@@ -13,8 +12,7 @@ class AccountCreateScreen extends ConsumerStatefulWidget {
const AccountCreateScreen({super.key});
@override
ConsumerState<AccountCreateScreen> createState() =>
_AccountCreateScreenState();
ConsumerState<AccountCreateScreen> createState() => _AccountCreateScreenState();
}
enum _AccountScreen { options, login, signup }
@@ -40,7 +38,7 @@ class _AccountCreateScreenState extends ConsumerState<AccountCreateScreen> {
},
);
}
final screen = BuildMedia(context).size;
final screen = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: Styles.purpleNurple,
body: SafeArea(
@@ -60,11 +58,8 @@ class _AccountCreateScreenState extends ConsumerState<AccountCreateScreen> {
Padding(
padding: const EdgeInsets.symmetric(vertical: 40.0),
child: Image.asset("assets/app_icon.png",
height:
screen.width > 500 ? 250 : screen.width * 0.5,
width: screen.width > 500
? 250
: screen.width * 0.5),
height: screen.width > 500 ? 250 : screen.width * 0.5,
width: screen.width > 500 ? 250 : screen.width * 0.5),
),
UiButton(
color: Styles.sunflower,
+12 -27
View File
@@ -1,7 +1,6 @@
import 'package:colorful_print/colorful_print.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:helpers/helpers/misc_build/build_media.dart';
import 'package:helpers/helpers/print.dart';
import '../../global/api.dart';
import '../../global/styles.dart';
@@ -19,7 +18,7 @@ class Login extends ConsumerStatefulWidget {
}
class _LoginState extends ConsumerState<Login> {
bool usingUsername = false;
bool usingUsername = true;
final emailController = TextEditingController();
final usernameController = TextEditingController();
final passwordController = TextEditingController();
@@ -32,7 +31,7 @@ class _LoginState extends ConsumerState<Login> {
@override
Widget build(BuildContext context) {
final screen = BuildMedia(context).size;
final screen = MediaQuery.of(context).size;
return Form(
key: widget.formKey,
child: Stack(
@@ -134,7 +133,7 @@ class _LoginState extends ConsumerState<Login> {
text: 'Password: ',
validatorFunc: (s) {
if (s != null && s.length < 6) {
return 'Please do a better password (you have to)';
return 'Please do a better password, at least 6 characters (you have to)';
}
return null;
},
@@ -205,35 +204,21 @@ class _LoginState extends ConsumerState<Login> {
Future login() async {
try {
if (widget.formKey.currentState != null &&
!widget.formKey.currentState!.validate()) {
if (widget.formKey.currentState != null && !widget.formKey.currentState!.validate()) {
return;
}
final data =
await ref.read(apiProvider.notifier).post(path: 'auth/login', data: {
'username':
usernameController.text.isEmpty ? null : usernameController.text,
final Map<String, dynamic>? data = await ref.read(apiProvider.notifier).post(path: 'auth/login', data: {
'username': usernameController.text.isEmpty ? null : usernameController.text,
'email': emailController.text.isEmpty ? null : emailController.text,
'password': passwordController.text,
});
String message = 'Login unsuccessful';
bool success = data?['success'] == 'false';
if (data != null) {
if (data['message'] != null) {
message = data['message'];
} else if (success) {
message = 'Logged in!';
}
}
// final bool success = data?['success'] ?? false;
printAmber(data);
showSnack(
ref: ref,
text: message,
type: !success ? SnackType.error : SnackType.success);
bool success = data?['success'] ?? false;
String message = data?['message'] ?? (success ? 'Login success!' : 'Login unsuccessful.');
printColor(data, textColor: TextColor.yellow);
showSnack(ref: ref, text: message, type: success ? SnackType.success : SnackType.error);
} catch (err, st) {
printRed('Error in login: $err\n$st');
printColor('Error in login: $err\n$st', textColor: TextColor.red);
}
}
}
+12 -24
View File
@@ -1,7 +1,6 @@
import 'package:colorful_print/colorful_print.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:helpers/helpers/misc_build/build_media.dart';
import 'package:helpers/helpers/print.dart';
import '../../global/api.dart';
import '../../global/styles.dart';
@@ -36,7 +35,7 @@ class _SignupState extends ConsumerState<Signup> {
@override
Widget build(BuildContext context) {
final screen = BuildMedia(context).size;
final screen = MediaQuery.of(context).size;
return Form(
key: widget.formKey,
child: Stack(
@@ -126,8 +125,7 @@ class _SignupState extends ConsumerState<Signup> {
),
UiButton(
width: screen.width * 0.4,
icon: const Icon(Icons.cancel,
color: Styles.washedStone, size: 20),
icon: const Icon(Icons.cancel, color: Styles.washedStone, size: 20),
onPressed: () {
setState(
() => hasFamilyCode = false,
@@ -140,9 +138,7 @@ class _SignupState extends ConsumerState<Signup> {
: Padding(
padding: const EdgeInsets.all(8.0),
child: UiButton(
width: screen.width * 0.5 > 400
? 400
: screen.width * 0.5,
width: screen.width * 0.5 > 400 ? 400 : screen.width * 0.5,
text: 'JOIN FAMILY',
// color: Styles.flounderBlue,
onPressed: () {
@@ -190,8 +186,7 @@ class _SignupState extends ConsumerState<Signup> {
text,
style: TextStyle(fontSize: size.width < 350 ? 16 : 20),
),
if (subtext != null)
Text(subtext, style: const TextStyle(fontSize: 12)),
if (subtext != null) Text(subtext, style: const TextStyle(fontSize: 12)),
],
),
),
@@ -223,36 +218,29 @@ class _SignupState extends ConsumerState<Signup> {
Future signup() async {
try {
if (widget.formKey.currentState != null &&
!widget.formKey.currentState!.validate()) {
if (widget.formKey.currentState != null && !widget.formKey.currentState!.validate()) {
return;
}
final data =
await ref.read(apiProvider.notifier).post(path: 'auth/signup', data: {
final data = await ref.read(apiProvider.notifier).post(path: 'auth/signup', data: {
'name': nameCotroller.text,
'username':
usernameController.text.isEmpty ? null : usernameController.text,
'username': usernameController.text.isEmpty ? null : usernameController.text,
'email': emailController.text.isEmpty ? null : emailController.text,
'password': passwordController.text,
'family_code': familyCodeController.text.isEmpty
? null
: familyCodeController.text.toUpperCase(),
'family_code': familyCodeController.text.isEmpty ? null : familyCodeController.text.toUpperCase(),
'budget_name': 'Budget'
});
final success = data?['success'] ?? false;
showSnack(
ref: ref,
text: data?['message'] ?? success
? 'Login successful'
: 'Login unsuccessful',
text: data?['message'] ?? success ? 'Login successful' : 'Login unsuccessful',
type: !success ? SnackType.error : SnackType.success);
printAmber(data);
printColor(data, textColor: TextColor.yellow);
// final user = User.fromJson(data?['user']);
// ref.read(tokenProvider.notifier).state =
} catch (err) {
printRed(err);
printColor(err, textColor: TextColor.red);
}
}
}
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:helpers/helpers.dart';
import 'package:rluv/features/budget/screens/transactions_listview.dart';
import 'package:rluv/features/budget/widgets/add_transaction_dialog.dart';
import 'package:rluv/features/budget/widgets/budget_category_bar.dart';
@@ -19,8 +18,7 @@ class BudgetOverviewScreen extends ConsumerStatefulWidget {
final Map<String, dynamic> initialData;
@override
ConsumerState<BudgetOverviewScreen> createState() =>
_BudgetOverviewScreenState();
ConsumerState<BudgetOverviewScreen> createState() => _BudgetOverviewScreenState();
}
class _BudgetOverviewScreenState extends ConsumerState<BudgetOverviewScreen> {
@@ -30,23 +28,18 @@ class _BudgetOverviewScreenState extends ConsumerState<BudgetOverviewScreen> {
final budget = ref.watch(budgetProvider);
final budgetCategories = ref.watch(budgetCategoriesProvider);
final transactions = ref.watch(transactionsProvider);
final screen = BuildMedia(context).size;
final screen = MediaQuery.of(context).size;
double netExpense = 0.0;
double netIncome = 0.0;
double expectedExpenses = 0.0;
Map<int, double> budgetCategoryNetMap = {};
netExpense = transactions
.where((t) => t.type == TransactionType.expense)
.fold(netExpense, (net, t) => net + t.amount);
netIncome = transactions
.where((t) => t.type == TransactionType.income)
.fold(netIncome, (net, t) => net + t.amount);
netExpense =
transactions.where((t) => t.type == TransactionType.expense).fold(netExpense, (net, t) => net + t.amount);
netIncome = transactions.where((t) => t.type == TransactionType.income).fold(netIncome, (net, t) => net + t.amount);
for (final bud in budgetCategories) {
double net = 0.0;
expectedExpenses += bud.amount;
net = transactions
.where((t) => t.budgetCategoryId == bud.id)
.fold(net, (net, t) => net + t.amount);
net = transactions.where((t) => t.budgetCategoryId == bud.id).fold(net, (net, t) => net + t.amount);
budgetCategoryNetMap[bud.id!] = net;
}
return Stack(
@@ -64,23 +57,14 @@ class _BudgetOverviewScreenState extends ConsumerState<BudgetOverviewScreen> {
const Spacer(flex: 2),
Text(
formatDate(DateTime.now()),
style: const TextStyle(
fontSize: 16, color: Styles.electricBlue),
style: const TextStyle(fontSize: 16, color: Styles.electricBlue),
),
const Spacer(),
const Text('MONTHLY',
style:
TextStyle(fontSize: 42, color: Styles.electricBlue)),
const Text('MONTHLY', style: TextStyle(fontSize: 42, color: Styles.electricBlue)),
const Spacer(flex: 2),
BudgetNetBar(
isPositive: true,
net: netIncome,
expected: budget?.expectedIncome ?? 0.0),
BudgetNetBar(isPositive: true, net: netIncome, expected: budget?.expectedIncome ?? 0.0),
const Spacer(),
BudgetNetBar(
isPositive: false,
net: netExpense,
expected: expectedExpenses),
BudgetNetBar(isPositive: false, net: netExpense, expected: expectedExpenses),
const Spacer(),
],
),
@@ -107,8 +91,7 @@ class _BudgetOverviewScreenState extends ConsumerState<BudgetOverviewScreen> {
padding: EdgeInsets.all(8.0),
child: Text(
'BUDGET',
style: TextStyle(
fontSize: 28, color: Styles.electricBlue),
style: TextStyle(fontSize: 28, color: Styles.electricBlue),
),
),
budgetCategories.isEmpty
@@ -120,25 +103,18 @@ class _BudgetOverviewScreenState extends ConsumerState<BudgetOverviewScreen> {
children: [
const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'No budget categories created yet, add some!'),
child: Text('No budget categories created yet, add some!'),
),
UiButton(
onPressed:
ref.watch(budgetProvider) ==
null
? null
: () => showDialog(
context: context,
builder: (context) => Dialog(
shape: Styles
.dialogShape,
backgroundColor:
Styles
.dialogColor,
child:
const BudgetCategoryDialog()),
),
onPressed: ref.watch(budgetProvider) == null
? null
: () => showDialog(
context: context,
builder: (context) => Dialog(
shape: Styles.dialogShape,
backgroundColor: Styles.dialogColor,
child: const BudgetCategoryDialog()),
),
text: 'Add Category',
),
],
@@ -146,55 +122,42 @@ class _BudgetOverviewScreenState extends ConsumerState<BudgetOverviewScreen> {
),
)
: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 2.0, vertical: 4.0),
padding: const EdgeInsets.symmetric(horizontal: 2.0, vertical: 4.0),
child: SizedBox(
height: screen.height * 0.36,
child: Scrollbar(
controller: budgetListScrollController,
thumbVisibility: true,
child: ListView(
physics:
const BouncingScrollPhysics(),
controller:
budgetListScrollController,
physics: const BouncingScrollPhysics(),
controller: budgetListScrollController,
shrinkWrap: true,
children: [
...budgetCategories
.mapIndexed((i, category) {
...budgetCategories.map((category) {
final int i = budgetCategories.indexOf(category);
return BudgetCategoryBar(
budgetCategory: category,
currentAmount:
budgetCategoryNetMap[
category.id]!,
currentAmount: budgetCategoryNetMap[category.id]!,
index: i,
);
}).toList(),
const SizedBox(height: 20),
Row(
mainAxisAlignment:
MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 140,
child: ElevatedButton(
onPressed: ref.watch(
budgetProvider) ==
null
onPressed: ref.watch(budgetProvider) == null
? null
: () => showDialog(
context: context,
builder: (context) => Dialog(
shape: Styles
.dialogShape,
backgroundColor:
Styles
.dialogColor,
child:
const BudgetCategoryDialog()),
shape: Styles.dialogShape,
backgroundColor: Styles.dialogColor,
child: const BudgetCategoryDialog()),
),
child: const Text(
'Add Category'),
child: const Text('Add Category'),
),
),
],
@@ -214,8 +177,7 @@ class _BudgetOverviewScreenState extends ConsumerState<BudgetOverviewScreen> {
children: [
Expanded(
child: Padding(
padding:
const EdgeInsets.only(left: 20.0, right: 15.0),
padding: const EdgeInsets.only(left: 20.0, right: 15.0),
child: Container(
height: 70,
decoration: BoxDecoration(
@@ -225,8 +187,7 @@ class _BudgetOverviewScreenState extends ConsumerState<BudgetOverviewScreen> {
child: InkWell(
child: const Center(
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 20.0, vertical: 14.0),
padding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 14.0),
child: FittedBox(
child: Text(
'Transaction History',
@@ -238,10 +199,7 @@ class _BudgetOverviewScreenState extends ConsumerState<BudgetOverviewScreen> {
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const TransactionsListview()));
context, MaterialPageRoute(builder: (context) => const TransactionsListview()));
},
),
),
@@ -292,8 +250,7 @@ class _BudgetOverviewScreenState extends ConsumerState<BudgetOverviewScreen> {
child: IconButton(
icon: const Icon(Icons.loop),
color: Styles.seaweedGreen,
onPressed: () =>
ref.read(dashboardProvider.notifier).fetchDashboard(),
onPressed: () => ref.read(dashboardProvider.notifier).fetchDashboard(),
),
),
),
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:helpers/helpers/misc_build/build_media.dart';
import 'package:rluv/features/budget/widgets/transaction_list_item.dart';
import 'package:rluv/global/styles.dart';
import 'package:rluv/models/transaction_model.dart';
@@ -12,8 +11,7 @@ class TransactionsListview extends ConsumerStatefulWidget {
const TransactionsListview({super.key});
@override
ConsumerState<TransactionsListview> createState() =>
_TransactionsListviewState();
ConsumerState<TransactionsListview> createState() => _TransactionsListviewState();
}
class _TransactionsListviewState extends ConsumerState<TransactionsListview> {
@@ -23,8 +21,7 @@ class _TransactionsListviewState extends ConsumerState<TransactionsListview> {
void initState() {
// TODO: implement initState
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(selectedTransactionSortProvider.notifier).state =
TransactionSort.all;
ref.read(selectedTransactionSortProvider.notifier).state = TransactionSort.all;
ref.read(selectedSortDateProvider.notifier).state = SortDate.decending;
});
super.initState();
@@ -34,8 +31,8 @@ class _TransactionsListviewState extends ConsumerState<TransactionsListview> {
Widget build(BuildContext context) {
final budgetCategories = ref.watch(budgetCategoriesProvider);
if (ref.read(selectedCategory) == null && budgetCategories.isNotEmpty) {
WidgetsBinding.instance.addPostFrameCallback((_) =>
ref.read(selectedCategory.notifier).state = budgetCategories.first);
WidgetsBinding.instance
.addPostFrameCallback((_) => ref.read(selectedCategory.notifier).state = budgetCategories.first);
}
final sortType = ref.watch(selectedTransactionSortProvider);
final sortDate = ref.watch(selectedSortDateProvider);
@@ -60,8 +57,7 @@ class _TransactionsListviewState extends ConsumerState<TransactionsListview> {
if (ref.read(selectedCategory) != null) {
transactions = transactions
.where(
(element) =>
element.budgetCategoryId == ref.read(selectedCategory)!.id,
(element) => element.budgetCategoryId == ref.read(selectedCategory)!.id,
)
.toList();
}
@@ -79,15 +75,15 @@ class _TransactionsListviewState extends ConsumerState<TransactionsListview> {
break;
}
WidgetsBinding.instance.addPostFrameCallback((_) =>
ref.read(transactionHistoryListProvider.notifier).state = transactions);
WidgetsBinding.instance
.addPostFrameCallback((_) => ref.read(transactionHistoryListProvider.notifier).state = transactions);
return Scaffold(
endDrawer: Drawer(
backgroundColor: Styles.purpleNurple,
child: SafeArea(
child: Column(
children: [
SizedBox(height: BuildMedia(context).height * 0.15),
SizedBox(height: MediaQuery.of(context).size.height * 0.15),
DropdownButton<TransactionSort>(
dropdownColor: Styles.lavender,
value: ref.watch(selectedTransactionSortProvider),
@@ -108,8 +104,7 @@ class _TransactionsListviewState extends ConsumerState<TransactionsListview> {
.toList(),
onChanged: (TransactionSort? value) {
if (value != null) {
ref.read(selectedTransactionSortProvider.notifier).state =
value;
ref.read(selectedTransactionSortProvider.notifier).state = value;
}
}),
const SizedBox(height: 20),
@@ -137,8 +132,7 @@ class _TransactionsListviewState extends ConsumerState<TransactionsListview> {
}
}),
const SizedBox(height: 20),
if (ref.read(selectedTransactionSortProvider) ==
TransactionSort.category)
if (ref.read(selectedTransactionSortProvider) == TransactionSort.category)
DropdownButton<BudgetCategory>(
dropdownColor: Styles.lavender,
value: ref.watch(selectedCategory),
@@ -154,8 +148,7 @@ class _TransactionsListviewState extends ConsumerState<TransactionsListview> {
width: 18,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.black, width: 1.5),
border: Border.all(color: Colors.black, width: 1.5),
color: e.color,
),
),
@@ -186,9 +179,7 @@ class _TransactionsListviewState extends ConsumerState<TransactionsListview> {
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.only(left: 8.0, top: 16.0),
child: IconButton(
icon: const Icon(Icons.chevron_left),
onPressed: () => Navigator.pop(context)),
child: IconButton(icon: const Icon(Icons.chevron_left), onPressed: () => Navigator.pop(context)),
),
),
const Center(
@@ -201,14 +192,11 @@ class _TransactionsListviewState extends ConsumerState<TransactionsListview> {
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 16.0),
child: IconButton(
icon: const Icon(Icons.chevron_left),
onPressed: () => Navigator.pop(context)),
child: IconButton(icon: const Icon(Icons.chevron_left), onPressed: () => Navigator.pop(context)),
),
const Spacer(),
const Padding(
padding:
EdgeInsets.symmetric(vertical: 18.0, horizontal: 0.0),
padding: EdgeInsets.symmetric(vertical: 18.0, horizontal: 0.0),
child: Text(
'Transaction History',
style: TextStyle(fontSize: 28),
@@ -245,8 +233,7 @@ class _TransactionsListviewState extends ConsumerState<TransactionsListview> {
}
void toggleDrawer() {
if (scaffoldKey.currentState != null &&
scaffoldKey.currentState!.isDrawerOpen) {
if (scaffoldKey.currentState != null && scaffoldKey.currentState!.isDrawerOpen) {
scaffoldKey.currentState!.openDrawer();
} else if (scaffoldKey.currentState != null) {
scaffoldKey.currentState!.openEndDrawer();
@@ -292,8 +279,7 @@ final selectedTransactionSortProvider = StateProvider<TransactionSort>(
(ref) => TransactionSort.all,
);
final selectedSortDateProvider =
StateProvider<SortDate>((ref) => SortDate.decending);
final selectedSortDateProvider = StateProvider<SortDate>((ref) => SortDate.decending);
final transactionHistoryListProvider = StateProvider<List<Transaction>>(
(ref) => [],
@@ -1,7 +1,6 @@
import 'package:colorful_print/colorful_print.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:helpers/helpers/misc_build/build_media.dart';
import 'package:helpers/helpers/print.dart';
import 'package:rluv/global/styles.dart';
import 'package:rluv/models/budget.dart';
import 'package:rluv/models/budget_category_model.dart';
@@ -17,12 +16,10 @@ class BudgetCategoryDialog extends ConsumerStatefulWidget {
final BudgetCategory? category;
@override
ConsumerState<BudgetCategoryDialog> createState() =>
_AddBudgetCategoryDialogState();
ConsumerState<BudgetCategoryDialog> createState() => _AddBudgetCategoryDialogState();
}
class _AddBudgetCategoryDialogState
extends ConsumerState<BudgetCategoryDialog> {
class _AddBudgetCategoryDialogState extends ConsumerState<BudgetCategoryDialog> {
late final Budget? budget;
final categoryNameController = TextEditingController();
final amountController = TextEditingController();
@@ -65,12 +62,11 @@ class _AddBudgetCategoryDialogState
return Container();
}
return SizedBox(
width: BuildMedia(context).width * Styles.dialogScreenWidthFactor,
width: MediaQuery.of(context).size.width * Styles.dialogScreenWidthFactor,
child: Stack(
children: [
Padding(
padding:
const EdgeInsets.symmetric(vertical: 18.0, horizontal: 32.0),
padding: const EdgeInsets.symmetric(vertical: 18.0, horizontal: 32.0),
child: Form(
key: formKey,
child: Column(
@@ -78,9 +74,7 @@ class _AddBudgetCategoryDialogState
children: [
Padding(
padding: const EdgeInsets.only(bottom: 18.0),
child: Text(widget.category == null
? 'Add Category:'
: 'Edit Category'),
child: Text(widget.category == null ? 'Add Category:' : 'Edit Category'),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
@@ -149,8 +143,7 @@ class _AddBudgetCategoryDialogState
],
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 6.0, horizontal: 16.0),
padding: const EdgeInsets.symmetric(vertical: 6.0, horizontal: 16.0),
child: SizedBox(
height: 76,
child: ListView.builder(
@@ -164,23 +157,21 @@ class _AddBudgetCategoryDialogState
} else {
setState(() => selectedColorIndex = -1);
}
printBlue(selectedColorIndex);
printColor(selectedColorIndex, textColor: TextColor.blue);
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: AnimatedContainer(
decoration: selectedColorIndex == index
? BoxDecoration(
borderRadius:
BorderRadius.circular(15.0),
borderRadius: BorderRadius.circular(15.0),
border: Border.all(
color: Styles.washedStone,
width: 2.0,
),
)
: BoxDecoration(
borderRadius:
BorderRadius.circular(5.0),
borderRadius: BorderRadius.circular(5.0),
border: Border.all(
color: Colors.transparent,
width: 2.0,
@@ -205,8 +196,7 @@ class _AddBudgetCategoryDialogState
decoration: BoxDecoration(
color: colors[index],
shape: BoxShape.circle,
border: Border.all(
color: Colors.black, width: 1.5),
border: Border.all(color: Colors.black, width: 1.5),
),
),
],
@@ -227,8 +217,7 @@ class _AddBudgetCategoryDialogState
color: Styles.expensesRed,
text: 'DELETE',
onPressed: () {
if (formKey.currentState != null &&
formKey.currentState!.validate()) {
if (formKey.currentState != null && formKey.currentState!.validate()) {
removeCategory().then((success) {
Navigator.pop(context);
if (success) {
@@ -254,8 +243,7 @@ class _AddBudgetCategoryDialogState
color: Styles.lavender,
text: 'SAVE',
onPressed: () {
if (formKey.currentState != null &&
formKey.currentState!.validate()) {
if (formKey.currentState != null && formKey.currentState!.validate()) {
submitCategory(colors[selectedColorIndex]).then((_) {
Navigator.pop(context);
showSnack(
@@ -282,7 +270,7 @@ class _AddBudgetCategoryDialogState
Future submitCategory(Color categoryColor) async {
if (formKey.currentState != null && !formKey.currentState!.validate()) {
printPink('Failed validation');
printColor('Failed validation', textColor: TextColor.red);
return;
}
Map<String, dynamic>? budgetData;
@@ -296,58 +284,41 @@ class _AddBudgetCategoryDialogState
name: categoryNameController.text,
);
budgetData = await ref
.read(apiProvider.notifier)
.post(path: 'budget_category', data: newBudget.toJson());
budgetData = await ref.read(apiProvider.notifier).post(path: 'budget_category', data: newBudget.toJson());
success = budgetData != null ? budgetData['success'] as bool : false;
if (success) {
ref
.read(dashboardProvider.notifier)
.add({'budget_categories': budgetData});
ref.read(dashboardProvider.notifier).add({'budget_categories': budgetData});
}
} else {
final newBudget =
BudgetCategory.copyWith(category: widget.category!, data: {
final newBudget = BudgetCategory.copyWith(category: widget.category!, data: {
'amount': double.parse(amountController.text),
'color': categoryColor,
'name': categoryNameController.text
});
budgetData = await ref
.read(apiProvider.notifier)
.put(path: 'budget_category', data: newBudget.toJson());
budgetData = await ref.read(apiProvider.notifier).put(path: 'budget_category', data: newBudget.toJson());
success = budgetData != null ? budgetData['success'] as bool : false;
if (success) {
ref
.read(dashboardProvider.notifier)
.update({'budget_categories': budgetData});
ref.read(dashboardProvider.notifier).update({'budget_categories': budgetData});
}
}
if (success) {
showSnack(
ref: ref,
text: widget.category == null
? 'Added budget category!'
: 'Updated category!',
text: widget.category == null ? 'Added budget category!' : 'Updated category!',
type: SnackType.error);
} else {
showSnack(
ref: ref,
text: widget.category == null
? 'Could not add budget category'
: 'Could not update category',
text: widget.category == null ? 'Could not add budget category' : 'Could not update category',
type: SnackType.error);
}
}
Future<bool> removeCategory() async {
final res = await ref
.read(apiProvider.notifier)
.delete(path: 'budget_category', data: {'id': widget.category!.id});
final res = await ref.read(apiProvider.notifier).delete(path: 'budget_category', data: {'id': widget.category!.id});
final success = res != null ? res['success'] as bool : false;
if (success) {
ref
.read(dashboardProvider.notifier)
.removeWithId('budget_categories', widget.category!.id!);
ref.read(dashboardProvider.notifier).removeWithId('budget_categories', widget.category!.id!);
}
return success;
}
@@ -1,7 +1,6 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:helpers/helpers/misc_build/build_media.dart';
import 'package:rluv/global/utils.dart';
import '../../../global/api.dart';
@@ -18,8 +17,7 @@ class TransactionDialog extends ConsumerStatefulWidget {
final Transaction? transaction;
@override
ConsumerState<TransactionDialog> createState() =>
_AddTransactionDialogState();
ConsumerState<TransactionDialog> createState() => _AddTransactionDialogState();
}
class _AddTransactionDialogState extends ConsumerState<TransactionDialog> {
@@ -37,8 +35,7 @@ class _AddTransactionDialogState extends ConsumerState<TransactionDialog> {
@override
void initState() {
if (widget.transaction != null) {
amountController =
TextEditingController(text: widget.transaction!.amount.toString());
amountController = TextEditingController(text: widget.transaction!.amount.toString());
memoController = TextEditingController(text: widget.transaction!.memo);
transactionType = widget.transaction!.type;
selectedDate = widget.transaction!.date;
@@ -53,8 +50,7 @@ class _AddTransactionDialogState extends ConsumerState<TransactionDialog> {
}
final u = ref.read(userProvider);
if (u == null) {
WidgetsBinding.instance.addPostFrameCallback(
(_) => ref.read(jwtProvider.notifier).revokeToken());
WidgetsBinding.instance.addPostFrameCallback((_) => ref.read(jwtProvider.notifier).revokeToken());
} else {
user = u;
}
@@ -65,10 +61,9 @@ class _AddTransactionDialogState extends ConsumerState<TransactionDialog> {
@override
Widget build(BuildContext context) {
if (user == null) return Container();
final List<BudgetCategory> budgetCategories =
ref.read(budgetCategoriesProvider);
final List<BudgetCategory> budgetCategories = ref.read(budgetCategoriesProvider);
return SizedBox(
width: BuildMedia(context).width * Styles.dialogScreenWidthFactor,
width: MediaQuery.of(context).size.width * Styles.dialogScreenWidthFactor,
child: budgetCategories.isEmpty
? const Padding(
padding: EdgeInsets.all(8.0),
@@ -91,25 +86,21 @@ class _AddTransactionDialogState extends ConsumerState<TransactionDialog> {
onTap: transactionType == TransactionType.expense
? null
: () {
setState(() => transactionType =
TransactionType.expense);
setState(() => transactionType = TransactionType.expense);
},
child: AnimatedContainer(
height: 38,
width: 80,
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(8.0),
topRight: Radius.circular(8.0)),
color: transactionType ==
TransactionType.expense
topLeft: Radius.circular(8.0), topRight: Radius.circular(8.0)),
color: transactionType == TransactionType.expense
? Styles.lavender
: Styles.washedStone),
duration: const Duration(milliseconds: 300),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text('Expense',
style: TextStyle(fontSize: 16)),
child: Text('Expense', style: TextStyle(fontSize: 16)),
),
),
),
@@ -117,25 +108,20 @@ class _AddTransactionDialogState extends ConsumerState<TransactionDialog> {
onTap: transactionType == TransactionType.income
? null
: () {
setState(() => transactionType =
TransactionType.income);
setState(() => transactionType = TransactionType.income);
},
child: AnimatedContainer(
height: 38,
width: 80,
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(8.0),
topRight: Radius.circular(8.0)),
topLeft: Radius.circular(8.0), topRight: Radius.circular(8.0)),
color:
transactionType == TransactionType.income
? Styles.lavender
: Styles.washedStone),
transactionType == TransactionType.income ? Styles.lavender : Styles.washedStone),
duration: const Duration(milliseconds: 300),
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Text('Income',
style: TextStyle(fontSize: 16)),
child: Text('Income', style: TextStyle(fontSize: 16)),
),
),
),
@@ -208,9 +194,7 @@ class _AddTransactionDialogState extends ConsumerState<TransactionDialog> {
width: 18,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.black,
width: 1.5),
border: Border.all(color: Colors.black, width: 1.5),
color: e.color,
),
),
@@ -226,8 +210,7 @@ class _AddTransactionDialogState extends ConsumerState<TransactionDialog> {
if (kDebugMode) {
print('${value.name} selected');
}
setState(() =>
selectedBudgetCategory = value);
setState(() => selectedBudgetCategory = value);
}
},
),
@@ -247,7 +230,7 @@ class _AddTransactionDialogState extends ConsumerState<TransactionDialog> {
),
),
Container(
width: BuildMedia(context).width * 0.65,
width: MediaQuery.of(context).size.width * 0.65,
height: 100,
decoration: Styles.boxLavenderBubble,
child: TextFormField(
@@ -269,9 +252,7 @@ class _AddTransactionDialogState extends ConsumerState<TransactionDialog> {
Navigator.pop(context);
showSnack(
ref: ref,
text: widget.transaction != null
? 'Transaction updated!'
: 'Transaction added!',
text: widget.transaction != null ? 'Transaction updated!' : 'Transaction added!',
type: SnackType.success,
);
}),
@@ -289,9 +270,7 @@ class _AddTransactionDialogState extends ConsumerState<TransactionDialog> {
data: Transaction.copyWith(widget.transaction!, {
'memo': memoController.text.isNotEmpty ? memoController.text : null,
'amount': double.parse(amountController.text),
'budget_category_id': transactionType == TransactionType.income
? null
: selectedBudgetCategory.id,
'budget_category_id': transactionType == TransactionType.income ? null : selectedBudgetCategory.id,
}).toJson());
} else {
data = await ref.read(apiProvider.notifier).post(
@@ -299,15 +278,11 @@ class _AddTransactionDialogState extends ConsumerState<TransactionDialog> {
data: Transaction(
amount: double.parse(amountController.text),
createdByUserId: user!.id!,
budgetCategoryId: transactionType == TransactionType.income
? null
: selectedBudgetCategory.id,
budgetCategoryId: transactionType == TransactionType.income ? null : selectedBudgetCategory.id,
budgetId: user!.budgetId,
date: DateTime.now(),
type: transactionType,
memo: memoController.text.isNotEmpty
? memoController.text
: null)
memo: memoController.text.isNotEmpty ? memoController.text : null)
.toJson());
}
final success = data != null ? data['success'] as bool : false;
@@ -320,9 +295,7 @@ class _AddTransactionDialogState extends ConsumerState<TransactionDialog> {
} else {
showSnack(
ref: ref,
text: widget.transaction != null
? 'Failed to edit transaction'
: 'Failed to add transaction',
text: widget.transaction != null ? 'Failed to edit transaction' : 'Failed to add transaction',
type: SnackType.error);
}
}
@@ -1,7 +1,6 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:helpers/helpers/print.dart';
import 'package:rluv/features/budget/widgets/add_budget_category_dialog.dart';
import 'package:rluv/global/utils.dart';
import 'package:rluv/models/budget_category_model.dart';
@@ -45,8 +44,7 @@ class _BudgetCategoryBarState extends State<BudgetCategoryBar> {
}
});
final innerHeight = widget.height - widget.innerPadding * 2;
final isBright =
getBrightness(widget.budgetCategory.color) == Brightness.light;
final isBright = getBrightness(widget.budgetCategory.color) == Brightness.light;
final textStyle = TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
@@ -155,7 +153,6 @@ class _BudgetCategoryBarState extends State<BudgetCategoryBar> {
if (!name.contains(' ') || name.indexOf(' ') == name.length - 1) {
return name;
}
printLime('here');
final words = name.split(' ');
int index = 0;
String firstLine = words[index];
@@ -1,14 +1,9 @@
import 'package:flutter/material.dart';
import 'package:helpers/helpers.dart';
import 'package:rluv/global/styles.dart';
import 'package:rluv/global/utils.dart';
class BudgetNetBar extends StatelessWidget {
const BudgetNetBar(
{super.key,
required this.isPositive,
required this.net,
required this.expected});
const BudgetNetBar({super.key, required this.isPositive, required this.net, required this.expected});
final bool isPositive;
final double net;
@@ -16,7 +11,7 @@ class BudgetNetBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final screenWidth = BuildMedia(context).width;
final screenWidth = MediaQuery.of(context).size.width;
return Container(
width: screenWidth * 0.85,
decoration: BoxDecoration(
@@ -25,8 +20,7 @@ class BudgetNetBar extends StatelessWidget {
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 12.0),
child:
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Text(
isPositive ? 'Income' : 'Expenses',
style: const TextStyle(
@@ -35,9 +29,7 @@ class BudgetNetBar extends StatelessWidget {
),
Text(
'${net.currency()} / ${expected.currency()}',
style: TextStyle(
fontSize: 20,
color: isPositive ? Styles.incomeGreen : Styles.expensesRed),
style: TextStyle(fontSize: 20, color: isPositive ? Styles.incomeGreen : Styles.expensesRed),
),
]),
),
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:helpers/helpers/misc_build/build_media.dart';
import 'package:intl/intl.dart';
import 'package:rluv/features/budget/screens/transactions_listview.dart';
import 'package:rluv/features/budget/widgets/add_transaction_dialog.dart';
@@ -19,8 +18,7 @@ class TransactionListItem extends ConsumerStatefulWidget {
final int index;
@override
ConsumerState<TransactionListItem> createState() =>
_TransactionListItemState();
ConsumerState<TransactionListItem> createState() => _TransactionListItemState();
}
class _TransactionListItemState extends ConsumerState<TransactionListItem> {
@@ -54,17 +52,13 @@ class _TransactionListItemState extends ConsumerState<TransactionListItem> {
child: Padding(
padding: const EdgeInsets.only(top: 8.0, left: 8.0, bottom: 8.0),
child: ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10.0),
bottomLeft: Radius.circular(10.0)),
borderRadius: const BorderRadius.only(topLeft: Radius.circular(10.0), bottomLeft: Radius.circular(10.0)),
child: AnimatedContainer(
curve: Curves.easeOut,
duration: const Duration(milliseconds: 200),
height: cardHeight,
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
bottomLeft: Radius.circular(10.0)),
borderRadius: BorderRadius.only(topLeft: Radius.circular(10.0), bottomLeft: Radius.circular(10.0)),
color: Styles.washedStone,
),
child: Row(
@@ -74,19 +68,16 @@ class _TransactionListItemState extends ConsumerState<TransactionListItem> {
duration: const Duration(milliseconds: 200),
height: cardHeight,
width: 6,
color: transaction!.type == TransactionType.income
? Styles.incomeBlue
: Styles.expensesOrange),
color: transaction!.type == TransactionType.income ? Styles.incomeBlue : Styles.expensesOrange),
SizedBox(
width: BuildMedia(context).width * 0.65,
width: MediaQuery.of(context).size.width * 0.65,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(DateFormat('EEE MMM d, h:mm a')
.format(transaction!.date)),
Text(DateFormat('EEE MMM d, h:mm a').format(transaction!.date)),
Row(
children: [
const SizedBox(
@@ -101,9 +92,7 @@ class _TransactionListItemState extends ConsumerState<TransactionListItem> {
),
],
Text(
transaction!.type == TransactionType.income
? 'Income'
: budgetCategory!.name,
transaction!.type == TransactionType.income ? 'Income' : budgetCategory!.name,
style: const TextStyle(fontSize: 20),
),
],
@@ -125,9 +114,7 @@ class _TransactionListItemState extends ConsumerState<TransactionListItem> {
transaction!.amount.currency(),
style: TextStyle(
fontSize: 24,
color: transaction!.type == TransactionType.income
? Styles.incomeGreen
: Styles.expensesRed),
color: transaction!.type == TransactionType.income ? Styles.incomeGreen : Styles.expensesRed),
),
if (showDetails) ...[
IconButton(
@@ -140,8 +127,7 @@ class _TransactionListItemState extends ConsumerState<TransactionListItem> {
builder: (context) => Dialog(
backgroundColor: Styles.dialogColor,
shape: Styles.dialogShape,
child:
TransactionDialog(transaction: transaction),
child: TransactionDialog(transaction: transaction),
),
);
},
@@ -182,21 +168,14 @@ class _TransactionListItemState extends ConsumerState<TransactionListItem> {
}
Future deleteTransaction() async {
final res = await ref
.read(apiProvider.notifier)
.delete(path: 'transaction', data: {'id': transaction!.id});
final res = await ref.read(apiProvider.notifier).delete(path: 'transaction', data: {'id': transaction!.id});
final success = res != null ? res['success'] as bool : false;
if (success) {
ref
.read(dashboardProvider.notifier)
.removeWithId('transactions', transaction!.id!);
ref.read(dashboardProvider.notifier).removeWithId('transactions', transaction!.id!);
showSnack(ref: ref, text: 'Transaction removed', type: SnackType.success);
} else {
showSnack(
ref: ref,
text: 'Could not delete transaction',
type: SnackType.error);
showSnack(ref: ref, text: 'Could not delete transaction', type: SnackType.error);
}
}
}
+20 -53
View File
@@ -1,8 +1,7 @@
import 'package:colorful_print/colorful_print.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:helpers/helpers/misc_build/build_media.dart';
import 'package:helpers/helpers/print.dart';
import 'package:rluv/global/utils.dart';
import 'package:rluv/models/shared_note.dart';
@@ -22,7 +21,7 @@ class SharedNotesScreen extends ConsumerStatefulWidget {
class _SharedNotesScreenState extends ConsumerState<SharedNotesScreen> {
@override
Widget build(BuildContext context) {
final screen = BuildMedia(context).size;
final screen = MediaQuery.of(context).size;
final sharedNotes = ref.watch(sharedNotesProvider);
return Column(
children: [
@@ -47,14 +46,10 @@ class _SharedNotesScreenState extends ConsumerState<SharedNotesScreen> {
),
itemBuilder: (BuildContext context, int index) {
final note = sharedNotes[index];
final colorBrightness = note.color == null
? Brightness.light
: getBrightness(note.color!);
final colorBrightness = note.color == null ? Brightness.light : getBrightness(note.color!);
final textStyle = TextStyle(
fontSize: 16.0,
color: colorBrightness == Brightness.light
? Colors.black54
: Colors.white70,
color: colorBrightness == Brightness.light ? Colors.black54 : Colors.white70,
);
return Padding(
padding: const EdgeInsets.all(8.0),
@@ -67,14 +62,10 @@ class _SharedNotesScreenState extends ConsumerState<SharedNotesScreen> {
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
FittedBox(
child: Text(note.title,
style: const TextStyle(fontSize: 20))),
FittedBox(child: Text(note.title, style: const TextStyle(fontSize: 20))),
Flexible(
child: Text(
note.content.length > 80
? "${note.content.substring(0, 75)}..."
: note.content,
note.content.length > 80 ? "${note.content.substring(0, 75)}..." : note.content,
style: textStyle,
),
),
@@ -93,9 +84,7 @@ class _SharedNotesScreenState extends ConsumerState<SharedNotesScreen> {
context: context,
builder: (BuildContext context) {
return Padding(
padding: EdgeInsets.only(
bottom:
MediaQuery.of(context).viewInsets.bottom),
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: NoteBottomSheet(
index: index,
),
@@ -121,8 +110,7 @@ class _SharedNotesScreenState extends ConsumerState<SharedNotesScreen> {
context: context,
builder: (BuildContext context) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: const NoteBottomSheet(
index: null,
),
@@ -182,19 +170,14 @@ class _NoteBottomSheetState extends ConsumerState<NoteBottomSheet> {
@override
Widget build(BuildContext context) {
final screen = BuildMedia(context).size;
final screen = MediaQuery.of(context).size;
return Container(
width: screen.width,
decoration: BoxDecoration(
color: newNote.color ?? Styles.washedStone,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20.0), topRight: Radius.circular(20.0)),
borderRadius: const BorderRadius.only(topLeft: Radius.circular(20.0), topRight: Radius.circular(20.0)),
boxShadow: const [
BoxShadow(
color: Colors.black26,
spreadRadius: 5.0,
blurRadius: 2.0,
offset: Offset(0, -2))
BoxShadow(color: Colors.black26, spreadRadius: 5.0, blurRadius: 2.0, offset: Offset(0, -2))
]),
child: Padding(
padding: const EdgeInsets.all(16.0),
@@ -218,8 +201,7 @@ class _NoteBottomSheetState extends ConsumerState<NoteBottomSheet> {
// decoration: Styles.inputLavenderBubble(),
),
Padding(
padding:
const EdgeInsets.symmetric(vertical: 28.0, horizontal: 36.0),
padding: const EdgeInsets.symmetric(vertical: 28.0, horizontal: 36.0),
child: UiButton(
onPressed: !noteChanged(oldNote, newNote)
? null
@@ -229,41 +211,26 @@ class _NoteBottomSheetState extends ConsumerState<NoteBottomSheet> {
try {
Response? res;
if (widget.index == null) {
res = await ref
.read(apiProvider)
.post('shared_note', data: newNote.toJson());
res = await ref.read(apiProvider).post('shared_note', data: newNote.toJson());
} else {
res = await ref
.read(apiProvider)
.put('shared_note', data: newNote.toJson());
res = await ref.read(apiProvider).put('shared_note', data: newNote.toJson());
}
if (res.data != null && res.data['success']) {
if (widget.index == null) {
ref
.read(dashboardProvider.notifier)
.add({'shared_notes': res.data});
ref.read(dashboardProvider.notifier).add({'shared_notes': res.data});
} else {
ref
.read(dashboardProvider.notifier)
.update({'shared_notes': res.data});
ref.read(dashboardProvider.notifier).update({'shared_notes': res.data});
}
showSnack(
ref: ref,
text: 'Added note',
type: SnackType.success);
showSnack(ref: ref, text: 'Added note', type: SnackType.success);
} else {
showSnack(
ref: ref,
text: res.data['message'] ??
'Unexpected error occurred',
text: res.data['message'] ?? 'Unexpected error occurred',
type: SnackType.error);
}
} catch (err) {
showSnack(
ref: ref,
text: 'Unexpected error occurred',
type: SnackType.error);
printRed(err);
showSnack(ref: ref, text: 'Unexpected error occurred', type: SnackType.error);
printColor(err, textColor: TextColor.red);
}
// ignore: use_build_context_synchronously
Navigator.pop(context);
+19 -27
View File
@@ -1,9 +1,9 @@
import 'dart:convert';
import 'package:colorful_print/colorful_print.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:helpers/helpers/print.dart';
import 'package:jwt_decoder/jwt_decoder.dart';
import 'package:rluv/global/store.dart';
@@ -11,7 +11,7 @@ import '../models/token.dart';
final tokenProvider = StateProvider<Token?>((ref) {
final jwt = ref.watch(jwtProvider);
printLime('Current token: $jwt');
printColor('Current token: $jwt', textColor: TextColor.green);
if (jwt == null) return null;
try {
return Token.fromJson(JwtDecoder.decode(jwt));
@@ -37,12 +37,12 @@ class _JwtNotifier extends StateNotifier<String?> {
void setToken(String jwt) {
state = jwt;
printCyan('Loaded jwt into client: $jwt');
printColor('Loaded jwt into client: $jwt', textColor: TextColor.cyan);
ref.read(prefsProvider)?.setString('jwt', jwt);
}
void revokeToken() {
printCyan('jwt token revoked');
printColor('jwt token revoked', textColor: TextColor.cyan);
state = null;
ref.read(prefsProvider)?.remove('jwt');
}
@@ -58,8 +58,7 @@ class _ApiNotifier extends StateNotifier<Dio> {
// dio.options.baseUrl = "http://localhost:8081/";
dio.options.baseUrl = "https://rluv.fosscat.com/";
dio.interceptors.addAll([
InterceptorsWrapper(onRequest:
(RequestOptions options, RequestInterceptorHandler handler) {
InterceptorsWrapper(onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
final jwt = ref.read(jwtProvider);
if (jwt != null) {
options.headers['token'] = jwt;
@@ -69,12 +68,10 @@ class _ApiNotifier extends StateNotifier<Dio> {
if (response.statusCode != null) {
if (response.statusCode == 200) {
try {
if ((response.data as Map<String, dynamic>)
.containsKey('success')) {
if ((response.data as Map<String, dynamic>).containsKey('success')) {
if (!response.data['success']) return handler.next(response);
}
if ((response.data as Map<String, dynamic>)
.containsKey('token')) {
if ((response.data as Map<String, dynamic>).containsKey('token')) {
final jwt = response.data['token'];
if (jwt != null) {
ref.read(jwtProvider.notifier).setToken(jwt);
@@ -83,7 +80,7 @@ class _ApiNotifier extends StateNotifier<Dio> {
}
}
} catch (err) {
printRed('Error in interceptor for token: $err');
printColor('Error in interceptor for token: $err', textColor: TextColor.red);
return handler.next(response);
}
}
@@ -115,8 +112,7 @@ class _ApiNotifier extends StateNotifier<Dio> {
}
}
Future<Map<String, dynamic>?> put(
{required String path, Object? data}) async {
Future<Map<String, dynamic>?> put({required String path, Object? data}) async {
try {
final res = await dio.put(path, data: data);
@@ -129,8 +125,7 @@ class _ApiNotifier extends StateNotifier<Dio> {
}
}
Future<Map<String, dynamic>?> post(
{required String path, Object? data}) async {
Future<Map<String, dynamic>?> post({required String path, Object? data}) async {
try {
final res = await dio.post(path, data: data);
@@ -143,8 +138,7 @@ class _ApiNotifier extends StateNotifier<Dio> {
}
}
Future<Map<String, dynamic>?> delete(
{required String path, Object? data}) async {
Future<Map<String, dynamic>?> delete({required String path, Object? data}) async {
try {
final res = await dio.delete(path, data: data);
@@ -162,8 +156,7 @@ class _LoggingInterceptor extends Interceptor {
_LoggingInterceptor();
@override
Future onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
Future onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
logPrint('///*** REQUEST ***\\\\\\');
printKV('URI', options.uri);
printKV('METHOD', options.method);
@@ -177,7 +170,7 @@ class _LoggingInterceptor extends Interceptor {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
printRed('///*** ERROR RESPONSE ***\\\\\\');
printColor('///*** ERROR RESPONSE ***\\\\\\', textColor: TextColor.red);
logPrint('URI: ${err.requestOptions.uri}');
if (err.response != null) {
logPrint('STATUS CODE: ${err.response?.statusCode?.toString()}');
@@ -192,8 +185,7 @@ class _LoggingInterceptor extends Interceptor {
}
@override
Future onResponse(
Response response, ResponseInterceptorHandler handler) async {
Future onResponse(Response response, ResponseInterceptorHandler handler) async {
logPrint('///*** RESPONSE ***\\\\\\');
printKV('URI', response.requestOptions.uri);
printKV('STATUS CODE', response.statusCode ?? '');
@@ -206,7 +198,7 @@ class _LoggingInterceptor extends Interceptor {
void printKV(String key, Object v) {
if (kDebugMode) {
printOrange('$key: $v');
printColor('$key: $v', textColor: TextColor.orange);
}
}
@@ -215,21 +207,21 @@ class _LoggingInterceptor extends Interceptor {
final data = (s as Map<String, dynamic>?);
if (kDebugMode) {
if (data == null) {
printAmber({});
printColor({}, textColor: TextColor.yellow);
return;
}
JsonEncoder encoder = const JsonEncoder.withIndent(' ');
String prettyprint = encoder.convert(s);
printAmber(prettyprint);
printColor(prettyprint, textColor: TextColor.yellow);
}
} catch (_) {
printAmber(s);
printColor(s, textColor: TextColor.yellow);
}
}
void logPrint(String s) {
if (kDebugMode) {
printOrange(s);
printColor(s, textColor: TextColor.yellow);
}
}
}
+14 -22
View File
@@ -1,8 +1,8 @@
import 'dart:convert';
import 'package:colorful_print/colorful_print.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:helpers/helpers/print.dart';
import 'package:rluv/global/api.dart';
import 'package:rluv/models/budget.dart';
import 'package:rluv/models/budget_category_model.dart';
@@ -13,8 +13,7 @@ import '../models/shared_note.dart';
import '../models/transaction_model.dart';
import '../models/user.dart';
StateProvider<SharedPreferences?> prefsProvider =
StateProvider<SharedPreferences?>((ref) => null);
StateProvider<SharedPreferences?> prefsProvider = StateProvider<SharedPreferences?>((ref) => null);
// final StateProvider<Token?> tokenProvider = StateProvider<Token?>((ref) {
// // final tokenStr = prefs.getString('jwt');
@@ -42,8 +41,7 @@ final Provider<FamilyModel?> familyProvider = Provider<FamilyModel?>(
},
);
final Provider<List<BudgetCategory>> budgetCategoriesProvider =
Provider<List<BudgetCategory>>((ref) {
final Provider<List<BudgetCategory>> budgetCategoriesProvider = Provider<List<BudgetCategory>>((ref) {
final dash = ref.watch(dashboardProvider);
if (dash == null) return [];
final categoriesData = dash['budget_categories'] as List<dynamic>;
@@ -55,7 +53,7 @@ final Provider<List<BudgetCategory>> budgetCategoriesProvider =
final prefs = ref.read(prefsProvider);
final budgetJson = jsonEncode({'budget_categories': categoriesData});
printBlue('updated prefs stored categories');
printColor('updated prefs stored categories', textColor: TextColor.blue);
prefs?.setString('budget_categories', budgetJson);
return categories;
@@ -71,8 +69,7 @@ final Provider<Budget?> budgetProvider = Provider<Budget?>(
},
);
final Provider<List<Transaction>> transactionsProvider =
Provider<List<Transaction>>((ref) {
final Provider<List<Transaction>> transactionsProvider = Provider<List<Transaction>>((ref) {
final dash = ref.watch(dashboardProvider);
if (dash == null) return [];
final transactions = dash['transactions'] as List<dynamic>;
@@ -83,8 +80,7 @@ final Provider<List<Transaction>> transactionsProvider =
.toList();
});
final Provider<List<SharedNote>> sharedNotesProvider =
Provider<List<SharedNote>>(
final Provider<List<SharedNote>> sharedNotesProvider = Provider<List<SharedNote>>(
(ref) {
final dash = ref.watch(dashboardProvider);
if (dash == null) return [];
@@ -97,8 +93,7 @@ final Provider<List<SharedNote>> sharedNotesProvider =
},
);
final dashboardProvider =
StateNotifierProvider<DashBoardStateNotifier, Map<String, dynamic>?>(
final dashboardProvider = StateNotifierProvider<DashBoardStateNotifier, Map<String, dynamic>?>(
(ref) {
return DashBoardStateNotifier(ref);
},
@@ -119,10 +114,10 @@ class DashBoardStateNotifier extends StateNotifier<Map<String, dynamic>?> {
);
final token = ref.read(tokenProvider);
if (token?.familyId == null) {
printPink('No token, cannot fetch dashboard');
printColor('No token, cannot fetch dashboard', textColor: TextColor.red);
return;
}
printAmber('Fetching dashboard');
printColor('Fetching dashboard', textColor: TextColor.yellow);
state = await ref.read(apiProvider.notifier).get("dashboard");
WidgetsBinding.instance.addPostFrameCallback(
(_) => ref.read(loadingStateProvider.notifier).state = false,
@@ -131,7 +126,7 @@ class DashBoardStateNotifier extends StateNotifier<Map<String, dynamic>?> {
void update(Map<String, dynamic> data) {
if (state == null) {
printPink('Cant update data, state is null');
printColor('Cant update data, state is null', textColor: TextColor.red);
return;
}
if (data.keys.length != 1 || data.values.length != 1) {
@@ -149,9 +144,7 @@ class DashBoardStateNotifier extends StateNotifier<Map<String, dynamic>?> {
)
.toList();
subStateListObj.removeWhere(
(element) =>
element['id'] ==
(data.values.first as Map<String, dynamic>)['id'],
(element) => element['id'] == (data.values.first as Map<String, dynamic>)['id'],
);
subStateListObj.add(data.values.first);
@@ -167,7 +160,7 @@ class DashBoardStateNotifier extends StateNotifier<Map<String, dynamic>?> {
void add(Map<String, dynamic> data) {
if (state == null) {
printPink('Cant add data, state is null');
printColor('Cant add data, state is null', textColor: TextColor.red);
return;
}
if (data.keys.length != 1 || data.values.length != 1) {
@@ -192,7 +185,7 @@ class DashBoardStateNotifier extends StateNotifier<Map<String, dynamic>?> {
void removeWithId(String stateKey, int id) {
if (state == null) {
printPink('Cant remove data, state is null');
printColor('Cant remove data, state is null', textColor: TextColor.red);
return;
}
switch (stateKey) {
@@ -201,8 +194,7 @@ class DashBoardStateNotifier extends StateNotifier<Map<String, dynamic>?> {
case 'shared_noted':
final subStateList = state![stateKey] as List<dynamic>;
final newState = state;
subStateList
.removeWhere((e) => (e as Map<String, dynamic>)['id'] == id);
subStateList.removeWhere((e) => (e as Map<String, dynamic>)['id'] == id);
newState![stateKey] = subStateList;
state = {...newState};
// printBlue(state);
+11 -19
View File
@@ -1,9 +1,9 @@
import 'dart:math';
import 'package:colorful_print/colorful_print.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:helpers/helpers/print.dart';
import 'package:intl/intl.dart';
import 'package:rluv/global/styles.dart';
import 'package:rluv/main.dart';
@@ -20,20 +20,16 @@ bool boolFromJson(int value) => value == 1;
int boolToJson(bool hide) => hide ? 1 : 0;
String colorToJson(Color color) =>
color.toString().split('(0x')[1].split(')')[0];
String colorToJson(Color color) => color.toString().split('(0x')[1].split(')')[0];
Color colorFromJson(String hex) => Color(int.parse(hex, radix: 16));
String? optionalColorToJson(Color? optionalColor) => optionalColor == null
? null
: optionalColor.toString().split('(0x')[1].split(')')[0];
String? optionalColorToJson(Color? optionalColor) =>
optionalColor == null ? null : optionalColor.toString().split('(0x')[1].split(')')[0];
Color? optionalColorFromJson(String? hex) =>
hex == null ? null : Color(int.parse(hex, radix: 16));
Color? optionalColorFromJson(String? hex) => hex == null ? null : Color(int.parse(hex, radix: 16));
Brightness getBrightness(Color color) =>
ThemeData.estimateBrightnessForColor(color);
Brightness getBrightness(Color color) => ThemeData.estimateBrightnessForColor(color);
List<Color> generateColorList() {
List<Color> colors = [];
@@ -60,7 +56,7 @@ extension MonetaryExtension on double {
return "\$${pieces.first}.00";
} else {
if (pieces.length > 2) {
printBlue(pieces);
printColor(pieces, textColor: TextColor.blue);
}
return '\$${pieces[0]}.${pieces[1].padRight(2, "0")}';
}
@@ -83,7 +79,7 @@ void showSnack(
Duration duration = const Duration(seconds: 2)}) {
final messengerKey = ref.read(scaffoldMessengerKeyProvider);
if (messengerKey.currentState == null) {
printPink('Cannot show snackbar, state == null');
printColor('Cannot show snackbar, state == null', textColor: TextColor.red);
return;
}
final color = type == SnackType.info
@@ -99,8 +95,7 @@ void showSnack(
elevation: 8,
backgroundColor: Styles.deepPurpleNurple,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0), topRight: Radius.circular(20.0)),
borderRadius: BorderRadius.only(topLeft: Radius.circular(20.0), topRight: Radius.circular(20.0)),
),
content: Padding(
padding: const EdgeInsets.all(8.0),
@@ -108,9 +103,7 @@ void showSnack(
children: [
Padding(
padding: const EdgeInsets.only(right: 14.0),
child: Icon(
type == SnackType.success ? Icons.check_circle : Icons.info,
color: color),
child: Icon(type == SnackType.success ? Icons.check_circle : Icons.info, color: color),
),
Text(text, style: textStyle),
],
@@ -120,8 +113,7 @@ void showSnack(
}
bool isEmailValid(String email) {
final RegExp regex =
RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$");
final RegExp regex = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$");
return regex.hasMatch(email);
}
+10 -20
View File
@@ -1,7 +1,6 @@
import 'package:animated_splash_screen/animated_splash_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:helpers/helpers/misc_build/build_media.dart';
import 'package:rluv/features/account/account_create_screen.dart';
import 'package:rluv/features/budget/screens/budget_overview.dart';
import 'package:rluv/features/notes/screens/notes_screen.dart';
@@ -74,8 +73,7 @@ class _HomeState extends ConsumerState<Home> {
}
WidgetsBinding.instance.addPostFrameCallback(
(_) {
ref.read(currentHomePageProvider.notifier).state =
const BudgetOverviewScreen(initialData: {});
ref.read(currentHomePageProvider.notifier).state = const BudgetOverviewScreen(initialData: {});
},
);
super.initState();
@@ -92,8 +90,7 @@ class _HomeState extends ConsumerState<Home> {
);
});
}
if (ref.watch(currentHomePageProvider).toString() ==
"BudgetOverviewScreen") {
if (ref.watch(currentHomePageProvider).toString() == "BudgetOverviewScreen") {
initData = {};
}
return Scaffold(
@@ -104,15 +101,13 @@ class _HomeState extends ConsumerState<Home> {
child: SafeArea(
child: Column(
children: [
SizedBox(height: BuildMedia(context).height * 0.15),
SizedBox(height: MediaQuery.of(context).size.height * 0.15),
UiButton(
text: 'Budget',
color: drawerColors[4],
onPressed: () {
if (ref.read(currentHomePageProvider).toString() !=
"BudgetOverviewScreen") {
ref.read(currentHomePageProvider.notifier).state =
BudgetOverviewScreen(initialData: initData);
if (ref.read(currentHomePageProvider).toString() != "BudgetOverviewScreen") {
ref.read(currentHomePageProvider.notifier).state = BudgetOverviewScreen(initialData: initData);
}
toggleDrawer();
},
@@ -121,10 +116,8 @@ class _HomeState extends ConsumerState<Home> {
text: 'Notes',
color: drawerColors[5],
onPressed: () {
if (ref.read(currentHomePageProvider).toString() !=
"SharedNotesScreen") {
ref.read(currentHomePageProvider.notifier).state =
SharedNotesScreen(initialData: initData);
if (ref.read(currentHomePageProvider).toString() != "SharedNotesScreen") {
ref.read(currentHomePageProvider.notifier).state = SharedNotesScreen(initialData: initData);
}
toggleDrawer();
},
@@ -134,10 +127,8 @@ class _HomeState extends ConsumerState<Home> {
text: 'Settings',
color: drawerColors[0],
onPressed: () {
if (ref.read(currentHomePageProvider).toString() !=
"SettingsScreen") {
ref.read(currentHomePageProvider.notifier).state =
const SettingsScreen();
if (ref.read(currentHomePageProvider).toString() != "SettingsScreen") {
ref.read(currentHomePageProvider.notifier).state = const SettingsScreen();
}
toggleDrawer();
},
@@ -190,5 +181,4 @@ final appBarTitleProvider = Provider<String>(
);
final scaffoldMessengerKeyProvider =
Provider<GlobalKey<ScaffoldMessengerState>>(
(ref) => GlobalKey<ScaffoldMessengerState>());
Provider<GlobalKey<ScaffoldMessengerState>>((ref) => GlobalKey<ScaffoldMessengerState>());
+27
View File
@@ -0,0 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'budget.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Budget _$BudgetFromJson(Map<String, dynamic> json) => Budget(
id: json['id'] as int?,
familyId: json['family_id'] as int,
name: json['name'] as String,
expectedIncome: (json['expected_income'] as num?)?.toDouble(),
createdAt: dateFromJson(json['created_at'] as int),
updatedAt: dateFromJson(json['updated_at'] as int),
hide: json['hide'] == null ? false : boolFromJson(json['hide'] as int),
);
Map<String, dynamic> _$BudgetToJson(Budget instance) => <String, dynamic>{
'id': instance.id,
'family_id': instance.familyId,
'expected_income': instance.expectedIncome,
'name': instance.name,
'hide': boolToJson(instance.hide),
'created_at': dateToJson(instance.createdAt),
'updated_at': dateToJson(instance.updatedAt),
};
+31
View File
@@ -0,0 +1,31 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'budget_category_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
BudgetCategory _$BudgetCategoryFromJson(Map<String, dynamic> json) =>
BudgetCategory(
id: json['id'] as int?,
budgetId: json['budget_id'] as int,
name: json['name'] as String,
color: colorFromJson(json['color'] as String),
createdAt: dateFromJson(json['created_at'] as int),
updatedAt: dateFromJson(json['updated_at'] as int),
amount: (json['amount'] as num).toDouble(),
hide: json['hide'] == null ? false : boolFromJson(json['hide'] as int),
);
Map<String, dynamic> _$BudgetCategoryToJson(BudgetCategory instance) =>
<String, dynamic>{
'budget_id': instance.budgetId,
'id': instance.id,
'name': instance.name,
'amount': instance.amount,
'hide': boolToJson(instance.hide),
'color': colorToJson(instance.color),
'created_at': dateToJson(instance.createdAt),
'updated_at': dateToJson(instance.updatedAt),
};
+24
View File
@@ -0,0 +1,24 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'family_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
FamilyModel _$FamilyModelFromJson(Map<String, dynamic> json) => FamilyModel(
id: json['id'] as int,
code: json['code'] as String?,
createdAt: dateFromJson(json['created_at'] as int),
updatedAt: dateFromJson(json['updated_at'] as int),
hide: json['hide'] == null ? false : boolFromJson(json['hide'] as int),
);
Map<String, dynamic> _$FamilyModelToJson(FamilyModel instance) =>
<String, dynamic>{
'id': instance.id,
'code': instance.code,
'hide': boolToJson(instance.hide),
'created_at': dateToJson(instance.createdAt),
'updated_at': dateToJson(instance.updatedAt),
};
+38
View File
@@ -0,0 +1,38 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'shared_note.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
SharedNote _$SharedNoteFromJson(Map<String, dynamic> json) => SharedNote(
id: json['id'] as int?,
familyId: json['family_id'] as int,
createdByUserId: json['created_by_user_id'] as int,
content: json['content'] as String,
title: json['title'] as String,
color: optionalColorFromJson(json['color'] as String?),
createdAt: dateFromJson(json['created_at'] as int),
updatedAt: dateFromJson(json['updated_at'] as int),
tagIds: _tagIdsFromJson(json['tag_ids'] as String),
isMarkdown: json['is_markdown'] == null
? false
: boolFromJson(json['is_markdown'] as int),
hide: json['hide'] == null ? false : boolFromJson(json['hide'] as int),
);
Map<String, dynamic> _$SharedNoteToJson(SharedNote instance) =>
<String, dynamic>{
'id': instance.id,
'family_id': instance.familyId,
'created_by_user_id': instance.createdByUserId,
'content': instance.content,
'title': instance.title,
'tag_ids': _tagIdsToJson(instance.tagIds),
'color': optionalColorToJson(instance.color),
'is_markdown': boolToJson(instance.isMarkdown),
'hide': boolToJson(instance.hide),
'created_at': dateToJson(instance.createdAt),
'updated_at': dateToJson(instance.updatedAt),
};
+33
View File
@@ -0,0 +1,33 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'tag.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Tag _$TagFromJson(Map<String, dynamic> json) => Tag(
id: json['id'] as int?,
familyId: json['family_id'] as int,
createdByUserId: json['created_by_user_id'] as int,
name: json['name'] as String,
type: $enumDecode(_$TagTypeEnumMap, json['type']),
createdAt: dateFromJson(json['created_at'] as int),
updatedAt: dateFromJson(json['updated_at'] as int),
hide: json['hide'] == null ? false : boolFromJson(json['hide'] as int),
);
Map<String, dynamic> _$TagToJson(Tag instance) => <String, dynamic>{
'id': instance.id,
'family_id': instance.familyId,
'created_by_user_id': instance.createdByUserId,
'name': instance.name,
'type': _$TagTypeEnumMap[instance.type]!,
'hide': boolToJson(instance.hide),
'created_at': dateToJson(instance.createdAt),
'updated_at': dateToJson(instance.updatedAt),
};
const _$TagTypeEnumMap = {
TagType.note: 'note',
};
+21
View File
@@ -0,0 +1,21 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'token.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Token _$TokenFromJson(Map<String, dynamic> json) => Token(
userId: json['user_id'] as int,
familyId: json['family_id'] as int,
generatedAt: dateFromJson(json['generated_at'] as int),
expiresAt: dateFromJson(json['expires_at'] as int),
);
Map<String, dynamic> _$TokenToJson(Token instance) => <String, dynamic>{
'family_id': instance.familyId,
'user_id': instance.userId,
'generated_at': dateToJson(instance.generatedAt),
'expires_at': dateToJson(instance.expiresAt),
};
+41
View File
@@ -0,0 +1,41 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'transaction_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Transaction _$TransactionFromJson(Map<String, dynamic> json) => Transaction(
id: json['id'] as int?,
amount: (json['amount'] as num).toDouble(),
type: $enumDecode(_$TransactionTypeEnumMap, json['type']),
budgetId: json['budget_id'] as int,
budgetCategoryId: json['budget_category_id'] as int?,
createdByUserId: json['created_by_user_id'] as int,
date: dateFromJson(json['date'] as int),
memo: json['memo'] as String?,
createdAt: dateFromJson(json['created_at'] as int),
updatedAt: dateFromJson(json['updated_at'] as int),
hide: json['hide'] == null ? false : boolFromJson(json['hide'] as int),
);
Map<String, dynamic> _$TransactionToJson(Transaction instance) =>
<String, dynamic>{
'id': instance.id,
'budget_category_id': instance.budgetCategoryId,
'budget_id': instance.budgetId,
'created_by_user_id': instance.createdByUserId,
'amount': instance.amount,
'memo': instance.memo,
'type': _$TransactionTypeEnumMap[instance.type]!,
'hide': boolToJson(instance.hide),
'date': dateToJson(instance.date),
'created_at': dateToJson(instance.createdAt),
'updated_at': dateToJson(instance.updatedAt),
};
const _$TransactionTypeEnumMap = {
TransactionType.income: 'income',
TransactionType.expense: 'expense',
};
+33
View File
@@ -0,0 +1,33 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'user.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
User _$UserFromJson(Map<String, dynamic> json) => User(
id: json['id'] as int?,
name: json['name'] as String,
familyId: json['family_id'] as int,
budgetId: json['budget_id'] as int,
email: json['email'] as String?,
username: json['username'] as String?,
createdAt: dateFromJson(json['created_at'] as int),
updatedAt: dateFromJson(json['updated_at'] as int),
lastActivityAt: dateFromJson(json['last_activity_at'] as int),
hide: json['hide'] == null ? false : boolFromJson(json['hide'] as int),
);
Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
'id': instance.id,
'family_id': instance.familyId,
'budget_id': instance.budgetId,
'name': instance.name,
'username': instance.username,
'email': instance.email,
'hide': boolToJson(instance.hide),
'created_at': dateToJson(instance.createdAt),
'updated_at': dateToJson(instance.updatedAt),
'last_activity_at': dateToJson(instance.lastActivityAt),
};
+1 -1
View File
@@ -7,7 +7,7 @@ project(runner LANGUAGES CXX)
set(BINARY_NAME "rluv")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.example.rluv")
set(APPLICATION_ID "com.fosscat.rluv")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
+20
View File
@@ -0,0 +1,20 @@
package_rename_config:
android:
app_name: rluv
package_name: com.fosscat.rluv
override_old_package: com.example.rluv
lang: kotlin
ios:
app_name: rluv
bundle_name: fosscatrluv
package_name: com.fosscat.rluv
web:
app_name: rluv
description: Package to change project configurations.
linux:
app_name: rluv
package_name: com.fosscat.rluv
exe_name: rluv
+12
View File
@@ -0,0 +1,12 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=rluv
Comment=Budget App
Categories=Utility;
Icon=com.fosscat.rluv
Exec=rluv
Terminal=false
StartupWMClass=rluv
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Generator for metainfo & .desktop files:
https://www.freedesktop.org/software/appstream/metainfocreator/#/
-->
<component type="desktop-application">
<id>com.fosscat.rluv</id>
<name>rluv</name>
<summary>A budget app made with love</summary>
<developer_name>Nate Anderson</developer_name>
<url type="homepage">https://git.fosscat.com/n8r/rluv_client</url>
<metadata_license></metadata_license>
<project_license></project_license>
<supports>
<control>pointing</control>
<control>keyboard</control>
<control>touch</control>
</supports>
<description>
<p>A budgeting app for my needs. Maybe your too?</p>
</description>
<launchable type="desktop-id">com.fosscat.rluv.desktop</launchable>
<screenshots>
<!-- <screenshot type="default"> -->
<!-- <image>https://raw.githubusercontent.com/Merrit/flutter_flatpak_example/main/screenshots/screenshot.png</image> -->
<!-- </screenshot> -->
</screenshots>
<content_rating type="oars-1.1" />
<releases>
<release version="0.0.1" date="2024-06-02" />
</releases>
</component>
+50 -27
View File
@@ -161,6 +161,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.18.0"
colorful_print:
dependency: "direct main"
description:
name: colorful_print
sha256: "2c9784a0d5e6dcd480a0d4ab67b7263e19d31644a30a8bc7f67ceb6db89549c7"
url: "https://pub.dev"
source: hosted
version: "0.1.2"
convert:
dependency: transitive
description:
@@ -177,14 +185,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.3"
cupertino_icons:
dependency: "direct main"
csslib:
dependency: transitive
description:
name: cupertino_icons
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
name: csslib
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
url: "https://pub.dev"
source: hosted
version: "1.0.5"
version: "1.0.0"
custom_refresh_indicator:
dependency: "direct main"
description:
@@ -304,15 +312,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.1"
helpers:
dependency: "direct main"
description:
path: "."
ref: main
resolved-ref: e9486562cb1ec5ac31fdd91ff573e99d51b2d57f
url: "https://github.com/mitch2na/helpers.git"
source: git
version: "1.2.0"
highlight:
dependency: transitive
description:
@@ -321,6 +320,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.0"
html:
dependency: transitive
description:
name: html
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
url: "https://pub.dev"
source: hosted
version: "0.15.4"
http_multi_server:
dependency: transitive
description:
@@ -405,26 +412,26 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
version: "10.0.4"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.3"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.1"
lints:
dependency: transitive
description:
@@ -433,6 +440,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
logger:
dependency: transitive
description:
name: logger
sha256: af05cc8714f356fd1f3888fb6741cbe9fbe25cdb6eedbab80e1a6db21047d4a4
url: "https://pub.dev"
source: hosted
version: "2.3.0"
logging:
dependency: transitive
description:
@@ -477,10 +492,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev"
source: hosted
version: "1.11.0"
version: "1.12.0"
mime:
dependency: transitive
description:
@@ -497,6 +512,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.0"
package_rename:
dependency: "direct dev"
description:
name: package_rename
sha256: "5066d021830f7a984f66a3d6912b0b95efb1d91f14322ba26ceacce2a62fd312"
url: "https://pub.dev"
source: hosted
version: "1.6.0"
page_transition:
dependency: transitive
description:
@@ -762,10 +785,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.dev"
source: hosted
version: "0.6.1"
version: "0.7.0"
timing:
dependency: transitive
description:
@@ -882,10 +905,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev"
source: hosted
version: "13.0.0"
version: "14.2.1"
watcher:
dependency: transitive
description:
@@ -936,4 +959,4 @@ packages:
version: "3.1.2"
sdks:
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.19.0"
flutter: ">=3.18.0-18.0.pre.54"
+4 -66
View File
@@ -1,47 +1,18 @@
name: rluv
description: A new Flutter project.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
description: A budget app for lovers
publish_to: 'none'
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
environment:
sdk: '>=2.19.6 <3.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
dio: ^5.2.1+1
flutter_riverpod: ^2.1.3
helpers:
git:
url: https://github.com/mitch2na/helpers.git
ref: main
uuid: ^3.0.7
colorful_print: ^0.1.2
json_annotation: ^4.8.0
shared_preferences: ^2.1.2
intl: ^0.18.1
@@ -53,52 +24,19 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.3.3
json_serializable: ^6.6.0
flutter_lints: ^2.0.0
icons_launcher: ^2.1.3
package_rename: ^1.6.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- assets/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
icons_launcher:
image_path: "assets/app_icon.png"
platforms:
+8 -8
View File
@@ -1,6 +1,4 @@
<!DOCTYPE html>
<html>
<head>
<!DOCTYPE html><html><head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
@@ -18,7 +16,7 @@
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<meta name="description" content="Package to change project configurations.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
@@ -27,7 +25,7 @@
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<link rel="icon" type="image/png" href="favicon.png">
<title>rluv</title>
<link rel="manifest" href="manifest.json">
@@ -37,7 +35,7 @@
var serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
<script src="flutter.js" defer=""></script>
</head>
<body>
<script>
@@ -55,5 +53,7 @@
});
});
</script>
</body>
</html>
</body></html>
+1 -1
View File
@@ -5,7 +5,7 @@
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"description": "Package to change project configurations.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [