Compare commits
10 Commits
45b5e33491
...
96811f5bfd
Author | SHA1 | Date | |
---|---|---|---|
96811f5bfd | |||
c44d4553c3 | |||
d722172cb3 | |||
43dd151cb4 | |||
c2cc71eae0 | |||
0c9b365f7f | |||
f820265dba | |||
85b7a0a3e6 | |||
861e8bf904 | |||
aaa95bd3a6 |
@ -6,7 +6,7 @@ plugins {
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.example.flutter_empty"
|
||||
namespace = "com.hyperling.expense_tracker"
|
||||
compileSdk = flutter.compileSdkVersion
|
||||
ndkVersion = flutter.ndkVersion
|
||||
|
||||
@ -21,7 +21,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId = "com.example.flutter_empty"
|
||||
applicationId = "com.hyperling.expense_tracker"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||
minSdk = flutter.minSdkVersion
|
||||
|
@ -1,6 +1,6 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application
|
||||
android:label="flutter_empty"
|
||||
android:label="Recurring Expense Tracker"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
|
252
lib/main.dart
252
lib/main.dart
@ -7,26 +7,166 @@ void main() {
|
||||
runApp(const MainApp());
|
||||
}
|
||||
|
||||
class MainApp extends StatefulWidget {
|
||||
// https://www.tutorialspoint.com/dart_programming/dart_programming_enumeration.htm
|
||||
enum Recurrence { daily, weekly, biweekly, montly, yearly }
|
||||
|
||||
class Expense {
|
||||
String name;
|
||||
double cost;
|
||||
Recurrence recurrence;
|
||||
String description;
|
||||
|
||||
Expense(this.name, this.cost, this.recurrence, this.description);
|
||||
}
|
||||
|
||||
class MainApp extends StatelessWidget {
|
||||
const MainApp({super.key});
|
||||
|
||||
@override
|
||||
State<MainApp> createState() => _MainAppState();
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Recurring Expense Tracker',
|
||||
theme: ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
|
||||
),
|
||||
home: HomePage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MainAppState extends State<MainApp> {
|
||||
final nameFieldController = TextEditingController();
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Clean up the controller when the widget is disposed.
|
||||
nameFieldController.dispose();
|
||||
super.dispose();
|
||||
State<HomePage> createState() => _HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> {
|
||||
var pageSelected = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const inputWidth = 400.0;
|
||||
Widget page;
|
||||
Widget? dialog;
|
||||
switch (pageSelected) {
|
||||
case 0:
|
||||
page = ExpensePage();
|
||||
dialog = ExpenseInputForm();
|
||||
case 1:
|
||||
page = IncomePage();
|
||||
case 2:
|
||||
page = AssetPage();
|
||||
case 3:
|
||||
page = ProjectionPage();
|
||||
case 4:
|
||||
page = SettingsPage();
|
||||
default:
|
||||
throw UnimplementedError('No widget for page $pageSelected yet!');
|
||||
}
|
||||
|
||||
Widget newValue() {
|
||||
return AlertDialog(title: Text("Add New Value"), content: dialog);
|
||||
}
|
||||
|
||||
Widget? floatingButton;
|
||||
if (dialog != null) {
|
||||
floatingButton = IconButton(
|
||||
onPressed: newValue,
|
||||
icon: Icon(Icons.add),
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
);
|
||||
}
|
||||
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
return Scaffold(
|
||||
body: Row(
|
||||
children: [
|
||||
SafeArea(
|
||||
child: NavigationRail(
|
||||
extended: constraints.maxWidth >= 800,
|
||||
destinations: [
|
||||
NavigationRailDestination(
|
||||
icon: Icon(Icons.payment),
|
||||
label: Text('Expenses'),
|
||||
),
|
||||
NavigationRailDestination(
|
||||
icon: Icon(Icons.account_balance),
|
||||
label: Text('Income'),
|
||||
),
|
||||
NavigationRailDestination(
|
||||
icon: Icon(Icons.attach_money),
|
||||
label: Text('Liquid Assets'),
|
||||
),
|
||||
NavigationRailDestination(
|
||||
icon: Icon(Icons.bar_chart),
|
||||
label: Text('Projections'),
|
||||
),
|
||||
NavigationRailDestination(
|
||||
icon: Icon(Icons.settings),
|
||||
label: Text('Settings'),
|
||||
),
|
||||
],
|
||||
selectedIndex: pageSelected,
|
||||
onDestinationSelected: (value) {
|
||||
setState(() {
|
||||
pageSelected = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
child: page,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: floatingButton,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ExpensePage extends StatelessWidget {
|
||||
const ExpensePage({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView(
|
||||
children: [
|
||||
ExpenseInputForm(),
|
||||
ListTile(
|
||||
title: Text("Fake Item 1"),
|
||||
subtitle: Text("30.00 / month"),
|
||||
),
|
||||
ListTile(
|
||||
title: Text("Fake Item 2"),
|
||||
subtitle: Text("180.00 / year"),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ExpenseInputForm extends StatefulWidget {
|
||||
const ExpenseInputForm({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ExpenseInputForm> createState() => _ExpenseInputFormState();
|
||||
}
|
||||
|
||||
class _ExpenseInputFormState extends State<ExpenseInputForm> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const inputWidth = 300.0;
|
||||
const inputHeight = 50.0;
|
||||
|
||||
const recurrenceValues = <DropdownMenuEntry>[
|
||||
@ -37,11 +177,9 @@ class _MainAppState extends State<MainApp> {
|
||||
DropdownMenuEntry(value: Recurrence.yearly, label: "Yearly"),
|
||||
];
|
||||
|
||||
return const MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
return Center(
|
||||
child: Column(mainAxisSize: MainAxisSize.min, spacing: 10, children: [
|
||||
Text('Input an expense below!'),
|
||||
Text('New Expense'),
|
||||
SizedBox(
|
||||
width: inputWidth,
|
||||
height: inputHeight,
|
||||
@ -59,8 +197,8 @@ class _MainAppState extends State<MainApp> {
|
||||
height: inputHeight,
|
||||
child: TextField(
|
||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Cost", hintText: "Example: 10.00"),
|
||||
decoration:
|
||||
InputDecoration(labelText: "Cost", hintText: "Example: 10.00"),
|
||||
),
|
||||
),
|
||||
DropdownMenu(
|
||||
@ -76,25 +214,81 @@ class _MainAppState extends State<MainApp> {
|
||||
keyboardType: TextInputType.text,
|
||||
decoration: InputDecoration(
|
||||
labelText: "Description",
|
||||
hintText:
|
||||
"Example: 1GB data with unlimited talk & text."
|
||||
),
|
||||
hintText: "Example: 1GB data with unlimited talk & text."),
|
||||
)),
|
||||
SizedBox(
|
||||
width: inputWidth,
|
||||
height: inputHeight,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
print("TODO: Clear fields!");
|
||||
},
|
||||
icon: Icon(Icons.cancel),
|
||||
label: Text('Cancel'),
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
print("TODO: Save expense!");
|
||||
},
|
||||
icon: Icon(Icons.save),
|
||||
label: Text('Submit'),
|
||||
),
|
||||
],
|
||||
))
|
||||
]),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.tutorialspoint.com/dart_programming/dart_programming_enumeration.htm
|
||||
enum Recurrence { daily, weekly, biweekly, montly, yearly }
|
||||
class IncomePage extends StatelessWidget {
|
||||
const IncomePage({
|
||||
super.key,
|
||||
});
|
||||
|
||||
class Expense {
|
||||
String name;
|
||||
double cost;
|
||||
Recurrence recurrence;
|
||||
String description;
|
||||
|
||||
Expense(this.name, this.cost, this.recurrence, this.description);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Text("TBD"),
|
||||
Placeholder(),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
class AssetPage extends StatelessWidget {
|
||||
const AssetPage({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Placeholder();
|
||||
}
|
||||
}
|
||||
|
||||
class ProjectionPage extends StatelessWidget {
|
||||
const ProjectionPage({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Placeholder();
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsPage extends StatelessWidget {
|
||||
const SettingsPage({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Placeholder();
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,10 @@ project(runner LANGUAGES CXX)
|
||||
|
||||
# The name of the executable created for the application. Change this to change
|
||||
# the on-disk name of your application.
|
||||
set(BINARY_NAME "flutter_empty")
|
||||
set(BINARY_NAME "expense_tracker")
|
||||
# The unique GTK application identifier for this application. See:
|
||||
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
||||
set(APPLICATION_ID "com.example.flutter_empty")
|
||||
set(APPLICATION_ID "com.hyperling.expense_tracker")
|
||||
|
||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||
# versions of CMake.
|
||||
|
@ -40,11 +40,11 @@ static void my_application_activate(GApplication* application) {
|
||||
if (use_header_bar) {
|
||||
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
|
||||
gtk_widget_show(GTK_WIDGET(header_bar));
|
||||
gtk_header_bar_set_title(header_bar, "flutter_empty");
|
||||
gtk_header_bar_set_title(header_bar, "Recurring Expense Tracker");
|
||||
gtk_header_bar_set_show_close_button(header_bar, TRUE);
|
||||
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
|
||||
} else {
|
||||
gtk_window_set_title(window, "flutter_empty");
|
||||
gtk_window_set_title(window, "Recurring Expense Tracker");
|
||||
}
|
||||
|
||||
gtk_window_set_default_size(window, 1280, 720);
|
||||
|
@ -1,5 +1,5 @@
|
||||
name: flutter_empty
|
||||
description: "A new Flutter project."
|
||||
name: flutter_expense_tracker
|
||||
description: Track recurring expenses against income and liquid assets.
|
||||
publish_to: 'none'
|
||||
version: 0.1.0
|
||||
|
||||
|
3
run_offline.sh
Executable file
3
run_offline.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
flutter run --no-pub
|
Reference in New Issue
Block a user