13 Commits

Author SHA1 Message Date
fa852faadc Use the dispose() method to reset the global values. Works excellently!! :D 2025-03-26 13:17:08 -07:00
6f9d0d8afb The initial page load now delays properly and shows the correct value rather than 0. Updating an item and going back to the page still shows old data though. 2025-03-26 12:39:09 -07:00
75cc72678b Add TBD of where the Income duplicate name validator is going wrong. 2025-03-25 08:45:44 -07:00
2970431b91 Increment the version. 2025-03-21 15:03:16 -07:00
4d0dc03c69 Create script which builds the delverables and places them in a convenient place with convenient names. 2025-03-21 14:58:56 -07:00
bdb3fc5c7a Ensure the README is up to date. Reword slightly. 2025-03-21 14:22:14 -07:00
da2ae6206a Add additional Help content and modify the Code Repo button to more specifically be Source Code for the project. 2025-03-21 14:21:43 -07:00
f5b22c6c1c Create and use a better app icon. 2025-03-21 14:08:26 -07:00
9f7773f724 Add functionality to Help buttons for opening URLs. 2025-03-21 13:40:11 -07:00
dec343af09 Add plugin url_launcher for Help page. 2025-03-21 12:17:10 -07:00
0890e15bfb Change the double SafeArea for Linux. 2025-03-21 12:05:59 -07:00
55fd4092ac Prevent the Help buttons from being underneath the Nav Bar on Android. 2025-03-21 12:05:03 -07:00
247b88daa2 Replace placeholder with a basic message for now. 2025-03-21 12:04:48 -07:00
26 changed files with 294 additions and 69 deletions

3
.gitignore vendored
View File

@ -44,3 +44,6 @@ app.*.map.json
/android/app/release
/android/app/.*
# Ignore the releases folder.
releases/*

View File

@ -1,6 +1,6 @@
# Recurring Expense Tracker
Recurring expense tracker app for Android.
Recurring expense tracker app for Linux and Android.
Add a debit as daily, weekly, monthly, etc, then see how it affects your liquid
assets based on your reported income over different time projections.
Add an expense as daily, weekly, monthly, etc, then see how it affects your
liquid assets based on your reported income over different time projections.

View File

@ -2,8 +2,8 @@
<application
android:label="Expenses"
android:name="${applicationName}"
android:icon="@mipmap/icon_v001"
android:roundIcon="@mipmap/icon_v001_round">
android:icon="@mipmap/icon_v003"
android:roundIcon="@mipmap/icon_v003_round">
<activity
android:name=".MainActivity"
android:exported="true"
@ -42,5 +42,10 @@
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
<!-- Allow opening of web URLs -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries>
</manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

46
create_release_files.sh Executable file
View File

@ -0,0 +1,46 @@
#!/usr/bin/env bash
DIR="$(dirname -- "${BASH_SOURCE[0]}")"
RELEASE_DIR=$DIR/releases
echo "*** Prepare for Builds ***"
cd $DIR
pwd
mkdir -pv $RELEASE_DIR
echo "*** Get Version ***"
grep 'version:' pubspec.yaml | while read v_text v_number; do
# Skip any newlines found while grepping.
if [[ -z $v_number ]]; then
continue
fi
echo "Creating assets for version '$v_number'."
# Set Up Variables
ANDROID_APK="build/app/outputs/flutter-apk/app-release.apk"
APK_RENAME="$RELEASE_DIR/ExpenseTracker_$v_number.apk"
LINUX_BUNDLE="build/linux/x64/release/bundle"
BUNDLE_RENAME="LinuxBundle_$v_number"
# Build Android App
echo -e "\n*** Android APK ***"
rm -v "$APK_RENAME"
flutter build apk
mv -v $ANDROID_APK "$APK_RENAME"
ls -sh "$APK_RENAME"
# Build Linux Project
echo -e "\n*** Linux Bundle ***"
rm -rv "$RELEASE_DIR/$BUNDLE_RENAME"*
flutter build linux
mv -v $LINUX_BUNDLE "$RELEASE_DIR/$BUNDLE_RENAME"
cd $RELEASE_DIR
zip -r $BUNDLE_RENAME.zip $BUNDLE_RENAME
rm -rv $BUNDLE_RENAME
ls -sh $BUNDLE_RENAME.zip
# Only one version should be found, but just in case, only use the top one!
break
done

View File

@ -1,4 +1,21 @@
// Flutter
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher_string.dart';
_launchSite(String url) async {
try {
if (await canLaunchUrlString(url)) {
await launchUrlString(
url,
mode: LaunchMode.externalApplication,
);
} else {
throw "System does not think it can launch '$url'.";
}
} on Exception catch (e) {
throw e.toString();
}
}
class HelpPage extends StatelessWidget {
const HelpPage({
@ -21,9 +38,24 @@ class HelpPage extends StatelessWidget {
),
child: Column(
children: [
Text("This app is meant to be a simple budgeting tool,"
" allowing you to view your income and expenses at a high level"
" without micro managing specific budget items or adding receipts."),
Text(
"\t\t This app is meant to be a simple budgeting tool,"
" allowing you to view your income and expenses at a high"
" level without micro managing specific budget items or"
" adding receipts.",
),
Text(
"\n\t\t Tracked items can be swiped left to right for ,"
" Deletion or right to left for Editing. Items are sorted"
" from highest to lowest so that the biggest impacts are"
" always in view.",
),
Text(
"\n\t\t To subscribe to app updates, install the Obtanium"
" app, then use the URL from the Source Code button below."
" Otherwise the app needs installed manually by downloading"
" APKs from the Source Code /releases/ page.",
),
//Text("Another paragraph.")
],
),
@ -41,9 +73,13 @@ class HelpPage extends StatelessWidget {
color: theme.colorScheme.onPrimary,
),
child: TextButton.icon(
onPressed: () {},
onPressed: () {
_launchSite(
"https://git.hyperling.com/me/flutter-expense-tracker",
);
},
icon: Icon(Icons.code),
label: Text("Code Repository"),
label: Text("Source Code"),
),
),
),
@ -57,7 +93,11 @@ class HelpPage extends StatelessWidget {
color: theme.colorScheme.onPrimary,
),
child: TextButton.icon(
onPressed: () {},
onPressed: () {
_launchSite(
"https://hyperling.com",
);
},
icon: Icon(Icons.web_asset),
label: Text("Personal Website"),
),

View File

@ -136,10 +136,11 @@ class _HomePageState extends State<HomePage> {
child: Center(child: page),
);
Widget? drawer, body;
Widget? drawer;
Widget body;
if (Platform.isAndroid || Platform.isIOS) {
drawer = navigation;
body = main;
body = SafeArea(child: main);
} else {
drawer = null;
body = Row(
@ -158,6 +159,9 @@ class _HomePageState extends State<HomePage> {
drawer: drawer,
body: body,
floatingActionButton: floatingButton,
extendBody: false,
extendBodyBehindAppBar: false,
resizeToAvoidBottomInset: true,
);
});
}

View File

@ -1,4 +1,6 @@
// Flutter
import 'dart:async';
import 'package:flutter/material.dart';
import '/models/item_type.dart';
@ -13,11 +15,11 @@ import '/models/tracked_item.dart';
/// - Fix bug where editing an item does not reflect immediately when returning to Reports page.
/// - Currently reflects after going back to Reports the 2nd time.
double _assetTotal = 0,
_expenseMonthly = 0,
_expenseYearly = 0,
_incomeMonthly = 0,
_incomeYearly = 0;
double _assetTotal = -1,
_expenseMonthly = -1,
_expenseYearly = -1,
_incomeMonthly = -1,
_incomeYearly = -1;
class ProjectionPage extends StatefulWidget {
const ProjectionPage({
@ -29,6 +31,18 @@ class ProjectionPage extends StatefulWidget {
}
class _ProjectionPageState extends State<ProjectionPage> {
bool _showProjections = true;
@override
void dispose() {
_assetTotal = -2;
_expenseMonthly = -2;
_expenseYearly = -2;
_incomeMonthly = -2;
_incomeYearly = -2;
super.dispose();
}
@override
Widget build(BuildContext context) {
// Summaries for display as well as calculation of totals for projections.
@ -46,6 +60,26 @@ class _ProjectionPageState extends State<ProjectionPage> {
);
// Calculations for the projections.
Widget projections;
if (_assetTotal < 0 ||
_incomeMonthly < 0 ||
_incomeYearly < 0 ||
_expenseMonthly < 0 ||
_expenseYearly < 0) {
_showProjections = false;
projections = Center(
child: SizedBox(
child: CircularProgressIndicator(),
),
);
Future.delayed(Duration(seconds: 1), () {
setState(() {
_showProjections = true;
});
});
} else {
double oneMonth = _assetTotal + _incomeMonthly - _expenseMonthly,
threeMonths = _assetTotal + (3 * (_incomeMonthly - _expenseMonthly)),
sixMonths = _assetTotal + (6 * (_incomeMonthly - _expenseMonthly)),
@ -91,6 +125,18 @@ class _ProjectionPageState extends State<ProjectionPage> {
rightText: "",
);
projections = Column(
children: [
proj1,
proj2,
proj3,
proj4,
proj5,
proj6,
],
);
}
// Return all of the UI elements.
return ListView(
children: [
@ -99,12 +145,7 @@ class _ProjectionPageState extends State<ProjectionPage> {
incomeSummary,
assetSummary,
TitleCard(title: "Projections"),
proj1,
proj2,
proj3,
proj4,
proj5,
proj6,
projections,
],
);
}

View File

@ -13,6 +13,10 @@ class SettingsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Placeholder();
return Center(
child: Text(
"No settings yet. :)",
),
);
}
}

View File

@ -337,6 +337,7 @@ class _TrackedItemInputDialogState extends State<TrackedItemInputDialog> {
: Text("Edit ${_type!.title}"),
),
content: FutureBuilder<List<Expense>>(
// TODO / TBD -- This should no longer only be Expenses.
future: DatabaseHelper.instance.getExpenses(),
builder: (BuildContext context,
AsyncSnapshot<List<Expense>> snapshot) {

View File

@ -6,6 +6,10 @@
#include "generated_plugin_registrant.h"
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}

View File

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -7,8 +7,10 @@ import Foundation
import path_provider_foundation
import sqflite_darwin
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}

BIN
media/icon_v002.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
media/icon_v002.xcf Normal file

Binary file not shown.

BIN
media/icon_v002_round.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
media/icon_v003.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
media/icon_v003.xcf Normal file

Binary file not shown.

BIN
media/icon_v003_round.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -75,6 +75,11 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
leak_tracker:
dependency: transitive
description:
@ -328,6 +333,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603"
url: "https://pub.dev"
source: hosted
version: "6.3.1"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: "1d0eae19bd7606ef60fe69ef3b312a437a16549476c42321d5dc1506c9ca3bf4"
url: "https://pub.dev"
source: hosted
version: "6.3.15"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626"
url: "https://pub.dev"
source: hosted
version: "6.3.2"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2"
url: "https://pub.dev"
source: hosted
version: "3.2.2"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: "3ba963161bd0fe395917ba881d320b9c4f6dd3c4a233da62ab18a5025c85f1e9"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77"
url: "https://pub.dev"
source: hosted
version: "3.1.4"
vector_math:
dependency: transitive
description:

View File

@ -1,7 +1,7 @@
name: expense_tracker
description: Track recurring expenses against income and liquid assets.
publish_to: 'none'
version: 0.1.1
version: 0.1.2
environment:
sdk: ^3.6.1
@ -13,6 +13,7 @@ dependencies:
path_provider: ^2.1.5
sqflite: ^2.4.1
sqflite_common_ffi: ^2.3.4+4
url_launcher: ^6.3.1
dev_dependencies:
flutter_test:

View File

@ -6,6 +6,9 @@
#include "generated_plugin_registrant.h"
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}

View File

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
url_launcher_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST