From eba628bb4c6a281b388a83921576013725c9e790 Mon Sep 17 00:00:00 2001
From: Nathan Anderson <nathananderson98@gmail.com>
Date: Wed, 19 Jul 2023 02:16:13 -0600
Subject: [PATCH] Working api call to backend

---
 .ignore                                       |   8 +
 .metadata                                     |  45 ++++
 README.md                                     |  16 --
 android/build.gradle                          |   2 +-
 build.yaml                                    |  10 +
 .../budget/screens/budget_overview.dart       | 253 ++++++++++++++++++
 .../widgets/add_transaction_dialog.dart       | 162 +++++++++++
 .../budget/widgets/budget_category_bar.dart   |   0
 .../budget/widgets/budget_net_bar.dart        |  40 +++
 lib/global/api.dart                           |  96 +++++++
 lib/global/store.dart                         |  81 ++++++
 lib/global/styles.dart                        |  21 ++
 lib/global/utils.dart                         |  17 ++
 lib/main.dart                                 | 124 +++------
 lib/models/budget.dart                        |  27 ++
 lib/models/budget.g.dart                      |  21 ++
 lib/models/budget_category_model.dart         |  35 +++
 lib/models/budget_category_model.g.dart       |  27 ++
 lib/models/family_model.dart                  |  27 ++
 lib/models/family_model.g.dart                |  22 ++
 lib/models/transaction_model.dart             |  40 +++
 lib/models/transaction_model.g.dart           |  36 +++
 lib/models/user.dart                          |  33 +++
 lib/models/user.g.dart                        |  27 ++
 pubspec.lock                                  |  42 +--
 pubspec.yaml                                  |   1 +
 26 files changed, 1094 insertions(+), 119 deletions(-)
 create mode 100644 .ignore
 create mode 100644 .metadata
 delete mode 100644 README.md
 create mode 100644 build.yaml
 create mode 100644 lib/features/budget/widgets/add_transaction_dialog.dart
 create mode 100644 lib/features/budget/widgets/budget_category_bar.dart
 create mode 100644 lib/features/budget/widgets/budget_net_bar.dart
 create mode 100644 lib/global/styles.dart
 create mode 100644 lib/global/utils.dart
 create mode 100644 lib/models/budget.dart
 create mode 100644 lib/models/budget.g.dart
 create mode 100644 lib/models/budget_category_model.dart
 create mode 100644 lib/models/budget_category_model.g.dart
 create mode 100644 lib/models/family_model.dart
 create mode 100644 lib/models/family_model.g.dart
 create mode 100644 lib/models/transaction_model.dart
 create mode 100644 lib/models/transaction_model.g.dart
 create mode 100644 lib/models/user.dart
 create mode 100644 lib/models/user.g.dart

diff --git a/.ignore b/.ignore
new file mode 100644
index 0000000..8957b74
--- /dev/null
+++ b/.ignore
@@ -0,0 +1,8 @@
+android
+ios
+web
+linux
+.metadata
+analysis_options.yaml
+pubspec.lock
+// lib/**/*.g.dart
\ No newline at end of file
diff --git a/.metadata b/.metadata
new file mode 100644
index 0000000..ce01264
--- /dev/null
+++ b/.metadata
@@ -0,0 +1,45 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled.
+
+version:
+  revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+  channel: unknown
+
+project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+  platforms:
+    - platform: root
+      create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+      base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+    - platform: android
+      create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+      base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+    - platform: ios
+      create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+      base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+    - platform: linux
+      create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+      base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+    - platform: macos
+      create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+      base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+    - platform: web
+      create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+      base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+    - platform: windows
+      create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+      base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
+
+  # User provided section
+
+  # List of Local paths (relative to this file) that should be
+  # ignored by the migrate tool.
+  #
+  # Files that are not part of the templates will be ignored by default.
+  unmanaged_files:
+    - 'lib/main.dart'
+    - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/README.md b/README.md
deleted file mode 100644
index e5970b1..0000000
--- a/README.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# rluv
-
-A new Flutter project.
-
-## Getting Started
-
-This project is a starting point for a Flutter application.
-
-A few resources to get you started if this is your first Flutter project:
-
-- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
-- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
-
-For help getting started with Flutter development, view the
-[online documentation](https://docs.flutter.dev/), which offers tutorials,
-samples, guidance on mobile development, and a full API reference.
diff --git a/android/build.gradle b/android/build.gradle
index 58a8c74..713d7f6 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -26,6 +26,6 @@ subprojects {
     project.evaluationDependsOn(':app')
 }
 
-task clean(type: Delete) {
+tasks.register("clean", Delete) {
     delete rootProject.buildDir
 }
diff --git a/build.yaml b/build.yaml
new file mode 100644
index 0000000..8705c4a
--- /dev/null
+++ b/build.yaml
@@ -0,0 +1,10 @@
+targets:
+  $default:
+    sources:
+      exclude:
+        - 'example/**'
+    builders:
+      json_serializable:
+        options:
+          explicit_to_json: true 
+          field_rename: snake
\ No newline at end of file
diff --git a/lib/features/budget/screens/budget_overview.dart b/lib/features/budget/screens/budget_overview.dart
index e69de29..7749807 100644
--- a/lib/features/budget/screens/budget_overview.dart
+++ b/lib/features/budget/screens/budget_overview.dart
@@ -0,0 +1,253 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:helpers/helpers.dart';
+import 'package:rluv/features/budget/widgets/add_transaction_dialog.dart';
+import 'package:rluv/features/budget/widgets/budget_net_bar.dart';
+import 'package:rluv/global/styles.dart';
+import 'package:rluv/global/utils.dart';
+
+import '../../../global/store.dart';
+
+class BudgetOverviewScreen extends ConsumerStatefulWidget {
+  const BudgetOverviewScreen({super.key});
+
+  @override
+  ConsumerState<BudgetOverviewScreen> createState() =>
+      _BudgetOverviewScreenState();
+}
+
+class _BudgetOverviewScreenState extends ConsumerState<BudgetOverviewScreen> {
+  final budgetListScrollController = ScrollController();
+  @override
+  Widget build(BuildContext context) {
+    final budgetCategoriesRef = ref.watch(Store().budgetCategoriesProvider);
+    final screen = BuildMedia(context).size;
+    return budgetCategoriesRef.when(
+      data: ((budgetCategories) => RefreshIndicator(
+            onRefresh: () async {
+              final _ = await ref.refresh(Store().dashboardProvider.future);
+            },
+            child: Column(
+              children: [
+                /// TOP HALF, TITLE & OVERVIEW
+                Container(
+                  height: screen.height * 0.33,
+                  width: screen.width,
+                  color: Styles.purpleNurple,
+                  child: Column(
+                    crossAxisAlignment: CrossAxisAlignment.center,
+                    children: [
+                      const Spacer(flex: 2),
+                      Text(
+                        formatDate(DateTime.now()),
+                        style: const TextStyle(
+                            fontSize: 16, color: Styles.electricBlue),
+                      ),
+                      const Spacer(),
+                      const Text('MONTHLY',
+                          style: TextStyle(
+                              fontSize: 42, color: Styles.electricBlue)),
+                      const Spacer(flex: 2),
+                      const BudgetNetBar(isPositive: true, net: 5024.64),
+                      const Spacer(),
+                      const BudgetNetBar(isPositive: false, net: 2004.37),
+                      const Spacer(),
+                    ],
+                  ),
+                ),
+
+                /// BOTTOM HALF, BUDGET BREAKDOWN
+                Expanded(
+                  child: SizedBox(
+                    width: screen.width,
+                    child: Column(
+                      mainAxisSize: MainAxisSize.max,
+                      children: [
+                        Padding(
+                          padding: const EdgeInsets.all(14.0),
+                          child: Container(
+                            decoration: BoxDecoration(
+                              color: Styles.blushingPink,
+                              borderRadius: BorderRadius.circular(16.0),
+                            ),
+                            child: Column(
+                              children: [
+                                const Padding(
+                                  padding: EdgeInsets.all(8.0),
+                                  child: Text(
+                                    'BUDGET',
+                                    style: TextStyle(
+                                        fontSize: 28,
+                                        color: Styles.electricBlue),
+                                  ),
+                                ),
+                                Scrollbar(
+                                  controller: budgetListScrollController,
+                                  thumbVisibility: true,
+                                  child: ListView(
+                                    controller: budgetListScrollController,
+                                    shrinkWrap: true,
+                                    children: [
+                                      for (final budget in budgetCategories)
+                                        Row(
+                                          children: [
+                                            Padding(
+                                              padding:
+                                                  const EdgeInsets.all(8.0),
+                                              child: SizedBox(
+                                                width: 85,
+                                                child: Text(
+                                                  budget.name,
+                                                  style: const TextStyle(
+                                                      fontSize: 16.0),
+                                                ),
+                                              ),
+                                            ),
+                                            AnimatedContainer(
+                                              duration: const Duration(
+                                                  milliseconds: 500),
+                                              child: Expanded(
+                                                  child: Padding(
+                                                padding: const EdgeInsets.only(
+                                                    right: 18.0),
+                                                child: Stack(
+                                                  children: [
+                                                    Container(
+                                                      height: 30,
+                                                      decoration: BoxDecoration(
+                                                        color: Colors.black,
+                                                        borderRadius:
+                                                            BorderRadius
+                                                                .circular(10.0),
+                                                      ),
+                                                    ),
+                                                    Padding(
+                                                      padding:
+                                                          const EdgeInsets.all(
+                                                              2.0),
+                                                      child: Container(
+                                                        height: 26,
+                                                        decoration:
+                                                            BoxDecoration(
+                                                          color: Styles
+                                                              .emptyBarGrey,
+                                                          borderRadius:
+                                                              BorderRadius
+                                                                  .circular(
+                                                                      10.0),
+                                                        ),
+                                                      ),
+                                                    ),
+                                                    Padding(
+                                                      padding:
+                                                          const EdgeInsets.all(
+                                                              2.0),
+                                                      child: SizedBox(
+                                                        height: 26,
+                                                        child:
+                                                            FractionallySizedBox(
+                                                          heightFactor: 1.0,
+                                                          widthFactor: 0.5,
+                                                          child: Container(
+                                                            decoration:
+                                                                BoxDecoration(
+                                                              color:
+                                                                  budget.color,
+                                                              borderRadius:
+                                                                  BorderRadius
+                                                                      .circular(
+                                                                          10.0),
+                                                            ),
+                                                          ),
+                                                        ),
+                                                      ),
+                                                    ),
+                                                  ],
+                                                ),
+                                              )),
+                                            )
+                                          ],
+                                        ),
+                                      const SizedBox(height: 20),
+                                    ],
+                                  ),
+                                ),
+                              ],
+                            ),
+                          ),
+                        ),
+                        const Spacer(),
+                        Padding(
+                          padding: const EdgeInsets.only(bottom: 24.0),
+                          child: Row(
+                            children: [
+                              Expanded(
+                                child: Padding(
+                                  padding: const EdgeInsets.only(
+                                      left: 20.0, right: 15.0),
+                                  child: Container(
+                                    height: 70,
+                                    decoration: BoxDecoration(
+                                      color: Styles.seaweedGreen,
+                                      borderRadius: BorderRadius.circular(15.0),
+                                    ),
+                                    child: InkWell(
+                                      child: const Center(
+                                        child: Padding(
+                                          padding: EdgeInsets.symmetric(
+                                              horizontal: 20.0, vertical: 14.0),
+                                          child: Text(
+                                            'Transaction History',
+                                            textAlign: TextAlign.center,
+                                            style: TextStyle(fontSize: 25),
+                                          ),
+                                        ),
+                                      ),
+                                      onTap: () {
+                                        printRed('FIXME');
+                                      },
+                                    ),
+                                  ),
+                                ),
+                              ),
+                              Padding(
+                                padding: const EdgeInsets.only(right: 20.0),
+                                child: Container(
+                                  decoration: BoxDecoration(
+                                    color: Styles.purpleNurple,
+                                    borderRadius: BorderRadius.circular(40.0),
+                                  ),
+                                  height: 80,
+                                  width: 80,
+                                  child: IconButton(
+                                    icon: const Icon(
+                                      Icons.add,
+                                      size: 48,
+                                      color: Styles.lavender,
+                                    ),
+                                    onPressed: () {
+                                      showDialog(
+                                        context: context,
+                                        builder: (BuildContext context) {
+                                          return const AddTransactionDialog();
+                                        },
+                                      );
+                                    },
+                                  ),
+                                ),
+                              ),
+                            ],
+                          ),
+                        ),
+                      ],
+                    ),
+                  ),
+                )
+              ],
+            ),
+          )),
+      loading: () => const CircularProgressIndicator(),
+      error: (error, stackTrace) => Text('Error: $error, \n\n$stackTrace'),
+    );
+  }
+}
diff --git a/lib/features/budget/widgets/add_transaction_dialog.dart b/lib/features/budget/widgets/add_transaction_dialog.dart
new file mode 100644
index 0000000..47d70f6
--- /dev/null
+++ b/lib/features/budget/widgets/add_transaction_dialog.dart
@@ -0,0 +1,162 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+import '../../../global/api.dart';
+import '../../../global/store.dart';
+import '../../../global/styles.dart';
+import '../../../models/budget_category_model.dart';
+import '../../../models/transaction_model.dart';
+
+class AddTransactionDialog extends ConsumerStatefulWidget {
+  const AddTransactionDialog({super.key});
+
+  @override
+  ConsumerState<AddTransactionDialog> createState() =>
+      _AddTransactionDialogState();
+}
+
+class _AddTransactionDialogState extends ConsumerState<AddTransactionDialog> {
+  final amountController = TextEditingController();
+
+  @override
+  Widget build(BuildContext context) {
+    final List<BudgetCategory> budgetCategories =
+        ref.read(Store().budgetCategoriesProvider).requireValue;
+    return Dialog(
+      shape: RoundedRectangleBorder(
+        borderRadius: BorderRadius.circular(10.0),
+        side: const BorderSide(color: Styles.purpleNurple, width: 5.0),
+      ),
+      child: StatefulBuilder(builder: (context, setState) {
+        TransactionType transactionType = TransactionType.expense;
+        BudgetCategory selectedBudgetCategory = budgetCategories.first;
+        return Container(
+          decoration: BoxDecoration(
+            borderRadius: BorderRadius.circular(10.0),
+            color: Styles.purpleNurple,
+          ),
+          child: Padding(
+            padding: const EdgeInsets.all(12.0),
+            child: Column(
+              mainAxisSize: MainAxisSize.min,
+              children: [
+                Row(children: [
+                  InkWell(
+                    onTap: transactionType == TransactionType.expense
+                        ? null
+                        : () {
+                            setState(() =>
+                                transactionType = TransactionType.expense);
+                          },
+                    child: AnimatedContainer(
+                      height: 30,
+                      width: 70,
+                      decoration: BoxDecoration(
+                          borderRadius: const BorderRadius.only(
+                              topLeft: Radius.circular(8.0),
+                              topRight: Radius.circular(8.0)),
+                          color: transactionType == TransactionType.expense
+                              ? Styles.lavender
+                              : Styles.sand),
+                      duration: const Duration(milliseconds: 300),
+                      child: const Padding(
+                        padding: EdgeInsets.all(8.0),
+                        child: Text('Expense'),
+                      ),
+                    ),
+                  ),
+                  InkWell(
+                    onTap: transactionType == TransactionType.income
+                        ? null
+                        : () {
+                            setState(
+                                () => transactionType = TransactionType.income);
+                          },
+                    child: AnimatedContainer(
+                      height: 30,
+                      width: 70,
+                      decoration: BoxDecoration(
+                          borderRadius: const BorderRadius.only(
+                              topLeft: Radius.circular(8.0),
+                              topRight: Radius.circular(8.0)),
+                          color: transactionType == TransactionType.income
+                              ? Styles.lavender
+                              : Styles.sand),
+                      duration: const Duration(milliseconds: 300),
+                      child: const Padding(
+                        padding: EdgeInsets.all(8.0),
+                        child: Text('Income'),
+                      ),
+                    ),
+                  ),
+                ]),
+                Row(
+                  children: [
+                    const Text('Amount:'),
+                    SizedBox(
+                      width: 80,
+                      child: TextField(
+                        controller: amountController,
+                        decoration: InputDecoration(
+                          fillColor: Styles.lavender,
+                          border: OutlineInputBorder(
+                            borderRadius: BorderRadius.circular(10.0),
+                            borderSide:
+                                const BorderSide(color: Colors.transparent),
+                          ),
+                        ),
+                      ),
+                    ),
+                  ],
+                ),
+                Row(
+                  children: [
+                    const Padding(
+                      padding: EdgeInsets.only(right: 28.0),
+                      child: Text('Category:'),
+                    ),
+                    DropdownButton<BudgetCategory>(
+                      value: selectedBudgetCategory,
+                      items: budgetCategories
+                          .map(
+                            (e) => DropdownMenuItem(
+                              value: e,
+                              child: Text(e.name),
+                            ),
+                          )
+                          .toList(),
+                      onChanged: (BudgetCategory? value) {
+                        if (value != null) {
+                          if (kDebugMode) {
+                            print('${value.name} selected');
+                          }
+                          setState(() => selectedBudgetCategory = value);
+                        }
+                      },
+                    ),
+                  ],
+                ),
+                ElevatedButton(
+                  child: const Text('Add'),
+                  onPressed: () {
+                    Api().put(
+                        path: 'transactions',
+                        data: Transaction(
+                            amount: double.parse(amountController.text),
+                            addedByUserId: 1,
+                            budgetCategoryId: 1,
+                            budgetId: 1,
+                            date: DateTime.now(),
+                            transactionType: transactionType));
+                    Navigator.pop(context);
+                  },
+                ),
+              ],
+            ),
+          ),
+        );
+      }),
+    );
+  }
+}
diff --git a/lib/features/budget/widgets/budget_category_bar.dart b/lib/features/budget/widgets/budget_category_bar.dart
new file mode 100644
index 0000000..e69de29
diff --git a/lib/features/budget/widgets/budget_net_bar.dart b/lib/features/budget/widgets/budget_net_bar.dart
new file mode 100644
index 0000000..13d64f9
--- /dev/null
+++ b/lib/features/budget/widgets/budget_net_bar.dart
@@ -0,0 +1,40 @@
+import 'package:flutter/material.dart';
+import 'package:helpers/helpers.dart';
+import 'package:rluv/global/styles.dart';
+
+class BudgetNetBar extends StatelessWidget {
+  const BudgetNetBar({super.key, required this.isPositive, required this.net});
+
+  final bool isPositive;
+  final double net;
+
+  @override
+  Widget build(BuildContext context) {
+    final screenWidth = BuildMedia(context).width;
+    return Container(
+      width: screenWidth * 0.85,
+      decoration: BoxDecoration(
+        borderRadius: BorderRadius.circular(10.0),
+        color: isPositive ? Styles.incomeBlue : Styles.expensesOrange,
+      ),
+      child: Padding(
+        padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 12.0),
+        child:
+            Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
+          Text(
+            isPositive ? 'Income' : 'Expenses',
+            style: const TextStyle(
+              fontSize: 20,
+            ),
+          ),
+          Text(
+            '\$$net',
+            style: TextStyle(
+                fontSize: 20,
+                color: isPositive ? Styles.incomeGreen : Styles.expensesRed),
+          ),
+        ]),
+      ),
+    );
+  }
+}
diff --git a/lib/global/api.dart b/lib/global/api.dart
index e69de29..04795e8 100644
--- a/lib/global/api.dart
+++ b/lib/global/api.dart
@@ -0,0 +1,96 @@
+import 'package:dio/dio.dart';
+import 'package:helpers/helpers/print.dart';
+
+class Api {
+  static final Api _instance = Api._internal();
+
+  factory Api() {
+    if (_instance.initDone) return _instance;
+
+    _instance.initDone = true;
+    _instance.dio = Dio();
+    _instance.dio.options.baseUrl = "http://localhost:8081/";
+    _instance.dio.interceptors.add(
+      InterceptorsWrapper(
+        onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
+          // Do something before request is sent.
+          // If you want to resolve the request with custom data,
+          // you can resolve a `Response` using `handler.resolve(response)`.
+          // If you want to reject the request with a error message,
+          // you can reject with a `DioException` using `handler.reject(dioError)`.
+          return handler.next(options);
+        },
+        onResponse: (Response response, ResponseInterceptorHandler handler) {
+          if (response.statusCode != null &&
+              response.statusCode! < 500 &&
+              response.statusCode! >= 400) {
+            return handler.reject(DioException.badResponse(
+                requestOptions: RequestOptions(),
+                response: response,
+                statusCode: response.statusCode!));
+          }
+          // Do something with response data.
+          // If you want to reject the request with a error message,
+          // you can reject a `DioException` object using `handler.reject(dioError)`.
+          return handler.next(response);
+        },
+        onError: (DioException e, ErrorInterceptorHandler handler) {
+          printPink(e);
+          // Do something with response error.
+          // If you want to resolve the request with some custom data,
+          // you can resolve a `Response` object using `handler.resolve(response)`.
+          return handler.next(e);
+        },
+      ),
+    );
+    return _instance;
+  }
+  Api._internal();
+
+  bool initDone = false;
+  late final Dio dio;
+
+  Future<Map<String, dynamic>?> get(String path) async {
+    try {
+      final res = await dio.get(path);
+
+      if (res.data != null) {
+        return res.data as Map<String, dynamic>;
+      }
+      return null;
+    } catch (err) {
+      printRed('Error in get: $err');
+      return null;
+    }
+  }
+
+  Future<Map<String, dynamic>?> put(
+      {required String path, Object? data}) async {
+    try {
+      final res = await dio.put(path, data: data);
+
+      if (res.data != null) {
+        return res.data as Map<String, dynamic>;
+      }
+      return null;
+    } catch (err) {
+      printRed('Error in put: $err');
+      return null;
+    }
+  }
+
+  Future<Map<String, dynamic>?> delete(
+      {required String path, Object? data}) async {
+    try {
+      final res = await dio.delete(path, data: data);
+
+      if (res.data != null) {
+        return res.data as Map<String, dynamic>;
+      }
+      return null;
+    } catch (err) {
+      printRed('Error in delete: $err');
+      return null;
+    }
+  }
+}
diff --git a/lib/global/store.dart b/lib/global/store.dart
index e69de29..9a8fbc3 100644
--- a/lib/global/store.dart
+++ b/lib/global/store.dart
@@ -0,0 +1,81 @@
+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';
+
+import '../models/family_model.dart';
+import '../models/transaction_model.dart';
+import '../models/user.dart';
+
+class Store {
+  static final Store _instance = Store._internal();
+  bool _initDone = false;
+
+  factory Store() {
+    if (_instance._initDone) {
+      return _instance;
+    }
+    _instance._initDone = true;
+    _instance.dashboardProvider = FutureProvider<Map<String, dynamic>?>(
+      (ref) async {
+        final family = await ref.watch(_instance.familyProvider.future);
+        return Api().get("dashboard/${family.id}");
+      },
+    );
+    _instance.budgetCategoriesProvider =
+        FutureProvider<List<BudgetCategory>>((ref) async {
+      final dash = await ref.watch(_instance.dashboardProvider.future);
+      printAmber(dash);
+      if (dash == null) return [];
+      final categories = dash['budget_categories'] as List<dynamic>;
+      return categories
+          .map(
+            (e) => BudgetCategory.fromJson(e as Map<String, dynamic>),
+          )
+          .toList();
+    });
+    _instance.transactionsProvider =
+        FutureProvider<List<Transaction>>((ref) async {
+      final dash = await ref.watch(_instance.dashboardProvider.future);
+      if (dash == null) return [];
+      final transactions = dash['transactions'] as List<dynamic>;
+      return transactions
+          .map(
+            (e) => Transaction.fromJson(e as Map<String, dynamic>),
+          )
+          .toList();
+    });
+    return _instance;
+  }
+
+  Store._internal();
+
+  final FutureProvider<User> userProvider = FutureProvider<User>(
+    (ref) {
+      return User(
+        id: 0,
+        budgetId: 1,
+        createdAt: DateTime.now(),
+        familyId: 1,
+        lastActivityAt: DateTime.now(),
+        name: 'TEMP',
+        updatedAt: DateTime.now(),
+      );
+    },
+  );
+
+  final FutureProvider<FamilyModel> familyProvider =
+      FutureProvider<FamilyModel>((ref) => FamilyModel(
+          id: 1,
+          budgetId: 1,
+          createdAt: DateTime.now(),
+          updatedAt: DateTime.now()));
+
+  late final FutureProvider<List<BudgetCategory>> budgetCategoriesProvider;
+  late final FutureProvider<Budget> budgetProvider;
+  late final FutureProvider<List<Transaction>> transactionsProvider;
+  late final FutureProvider<Map<String, dynamic>?> dashboardProvider;
+
+  void fetchDashboard() {}
+}
diff --git a/lib/global/styles.dart b/lib/global/styles.dart
new file mode 100644
index 0000000..4b71883
--- /dev/null
+++ b/lib/global/styles.dart
@@ -0,0 +1,21 @@
+import 'dart:ui';
+
+class Styles {
+  // Theme Colors
+  static const Color purpleNurple = Color(0xffA188A6);
+  static const Color sunflower = Color(0xffFFEE88);
+  static const Color electricBlue = Color(0xFF19647E);
+  static const Color blushingPink = Color(0xFFE78F8E);
+  static const Color seaweedGreen = Color(0xFF86BA90);
+  static const Color emptyBarGrey = Color(0xFFC8C8C8);
+  static const Color lavender = Color(0xFFB8B8FF);
+  static const Color sand = Color(0xFFD9D9D9);
+
+  // Income Colors
+  static const Color incomeBlue = Color(0xFFB8B8FF);
+  static const Color incomeGreen = Color(0xFF0FA102);
+
+  // Expenses Colors
+  static const Color expensesOrange = Color(0xFFFA824C);
+  static const Color expensesRed = Color(0xFF9E0000);
+}
diff --git a/lib/global/utils.dart b/lib/global/utils.dart
new file mode 100644
index 0000000..1a30b0c
--- /dev/null
+++ b/lib/global/utils.dart
@@ -0,0 +1,17 @@
+import 'dart:ui';
+
+import 'package:intl/intl.dart';
+
+String formatDate(DateTime time) {
+  return DateFormat('EEEE, dd MMMM yyyy').format(time);
+}
+
+DateTime dateFromJson(int value) => DateTime.fromMillisecondsSinceEpoch(value);
+
+int dateToJson(DateTime? value) =>
+    value == null ? 0 : value.millisecondsSinceEpoch;
+
+String colorToJson(Color color) =>
+    color.toString().split('(0x')[1].split(')')[0];
+
+Color colorFromJson(String hex) => Color(int.parse(hex, radix: 16));
diff --git a/lib/main.dart b/lib/main.dart
index 008fa38..f7a4a4b 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,4 +1,7 @@
 import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:rluv/features/budget/screens/budget_overview.dart';
+import 'package:rluv/global/styles.dart';
 
 void main() {
   runApp(const MyApp());
@@ -10,106 +13,57 @@ class MyApp extends StatelessWidget {
   // This widget is the root of your application.
   @override
   Widget build(BuildContext context) {
-    return MaterialApp(
-      title: 'Flutter Demo',
-      theme: ThemeData(
-        // This is the theme of your application.
-        //
-        // Try running your application with "flutter run". You'll see the
-        // application has a blue toolbar. Then, without quitting the app, try
-        // changing the primarySwatch below to Colors.green and then invoke
-        // "hot reload" (press "r" in the console where you ran "flutter run",
-        // or simply save your changes to "hot reload" in a Flutter IDE).
-        // Notice that the counter didn't reset back to zero; the application
-        // is not restarted.
-        primarySwatch: Colors.blue,
+    return ProviderScope(
+      child: MaterialApp(
+        debugShowCheckedModeBanner: false,
+        title: 'Flutter Demo',
+        theme: ThemeData(
+          primarySwatch: Colors.blue,
+        ),
+        home: const Home(),
       ),
-      home: const MyHomePage(title: 'Flutter Demo Home Page'),
     );
   }
 }
 
-class MyHomePage extends StatefulWidget {
-  const MyHomePage({super.key, required this.title});
-
-  // This widget is the home page of your application. It is stateful, meaning
-  // that it has a State object (defined below) that contains fields that affect
-  // how it looks.
-
-  // This class is the configuration for the state. It holds the values (in this
-  // case the title) provided by the parent (in this case the App widget) and
-  // used by the build method of the State. Fields in a Widget subclass are
-  // always marked "final".
-
-  final String title;
+class Home extends ConsumerStatefulWidget {
+  const Home({super.key});
 
   @override
-  State<MyHomePage> createState() => _MyHomePageState();
+  ConsumerState<Home> createState() => _HomeState();
 }
 
-class _MyHomePageState extends State<MyHomePage> {
-  int _counter = 0;
-
-  void _incrementCounter() {
-    setState(() {
-      // This call to setState tells the Flutter framework that something has
-      // changed in this State, which causes it to rerun the build method below
-      // so that the display can reflect the updated values. If we changed
-      // _counter without calling setState(), then the build method would not be
-      // called again, and so nothing would appear to happen.
-      _counter++;
-    });
-  }
-
+class _HomeState extends ConsumerState<Home> {
   @override
   Widget build(BuildContext context) {
-    // This method is rerun every time setState is called, for instance as done
-    // by the _incrementCounter method above.
-    //
-    // The Flutter framework has been optimized to make rerunning build methods
-    // fast, so that you can just rebuild anything that needs updating rather
-    // than having to individually change instances of widgets.
     return Scaffold(
-      appBar: AppBar(
-        // Here we take the value from the MyHomePage object that was created by
-        // the App.build method, and use it to set our appbar title.
-        title: Text(widget.title),
+      resizeToAvoidBottomInset: false,
+      drawer: const Drawer(
+        backgroundColor: Styles.purpleNurple,
       ),
-      body: Center(
-        // Center is a layout widget. It takes a single child and positions it
-        // in the middle of the parent.
-        child: Column(
-          // Column is also a layout widget. It takes a list of children and
-          // arranges them vertically. By default, it sizes itself to fit its
-          // children horizontally, and tries to be as tall as its parent.
-          //
-          // Invoke "debug painting" (press "p" in the console, choose the
-          // "Toggle Debug Paint" action from the Flutter Inspector in Android
-          // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
-          // to see the wireframe for each widget.
-          //
-          // Column has various properties to control how it sizes itself and
-          // how it positions its children. Here we use mainAxisAlignment to
-          // center the children vertically; the main axis here is the vertical
-          // axis because Columns are vertical (the cross axis would be
-          // horizontal).
-          mainAxisAlignment: MainAxisAlignment.center,
-          children: <Widget>[
-            const Text(
-              'You have pushed the button this many times:',
-            ),
-            Text(
-              '$_counter',
-              style: Theme.of(context).textTheme.headlineMedium,
-            ),
-          ],
+      appBar: AppBar(
+        backgroundColor: Styles.purpleNurple,
+        title: Text(
+          ref.watch(appBarTitleProvider),
         ),
       ),
-      floatingActionButton: FloatingActionButton(
-        onPressed: _incrementCounter,
-        tooltip: 'Increment',
-        child: const Icon(Icons.add),
-      ), // This trailing comma makes auto-formatting nicer for build methods.
+      body: ref.watch(currentHomePageProvider),
     );
   }
 }
+
+final currentHomePageProvider = StateProvider<Widget>(
+  (ref) => const BudgetOverviewScreen(),
+);
+
+final appBarTitleProvider = Provider<String>(
+  (ref) {
+    final currentPageName = ref.watch(currentHomePageProvider).toString();
+    switch (currentPageName) {
+      case 'BudgetOverviewScreen':
+        return 'Budget';
+      default:
+        return '';
+    }
+  },
+);
diff --git a/lib/models/budget.dart b/lib/models/budget.dart
new file mode 100644
index 0000000..b28db81
--- /dev/null
+++ b/lib/models/budget.dart
@@ -0,0 +1,27 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../global/utils.dart';
+
+part 'budget.g.dart';
+
+@JsonSerializable()
+class Budget {
+  const Budget({
+    this.id,
+    required this.name,
+    required this.createdAt,
+    required this.updatedAt,
+  });
+
+  final int? id;
+  final String name;
+
+  @JsonKey(fromJson: dateFromJson, toJson: dateToJson)
+  final DateTime createdAt;
+  @JsonKey(fromJson: dateFromJson, toJson: dateToJson)
+  final DateTime updatedAt;
+
+  factory Budget.fromJson(Map<String, dynamic> json) => _$BudgetFromJson(json);
+
+  Map<String, dynamic> toJson() => _$BudgetToJson(this);
+}
diff --git a/lib/models/budget.g.dart b/lib/models/budget.g.dart
new file mode 100644
index 0000000..1fba5c4
--- /dev/null
+++ b/lib/models/budget.g.dart
@@ -0,0 +1,21 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'budget.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+Budget _$BudgetFromJson(Map<String, dynamic> json) => Budget(
+      id: json['id'] as int?,
+      name: json['name'] as String,
+      createdAt: dateFromJson(json['created_at'] as int),
+      updatedAt: dateFromJson(json['updated_at'] as int),
+    );
+
+Map<String, dynamic> _$BudgetToJson(Budget instance) => <String, dynamic>{
+      'id': instance.id,
+      'name': instance.name,
+      'created_at': dateToJson(instance.createdAt),
+      'updated_at': dateToJson(instance.updatedAt),
+    };
diff --git a/lib/models/budget_category_model.dart b/lib/models/budget_category_model.dart
new file mode 100644
index 0000000..90a6e34
--- /dev/null
+++ b/lib/models/budget_category_model.dart
@@ -0,0 +1,35 @@
+import 'dart:ui';
+
+import 'package:json_annotation/json_annotation.dart';
+
+import '../global/utils.dart';
+
+part 'budget_category_model.g.dart';
+
+@JsonSerializable()
+class BudgetCategory {
+  const BudgetCategory({
+    this.id,
+    required this.budgetId,
+    required this.name,
+    required this.color,
+    required this.createdAt,
+    required this.amount,
+  });
+
+  final int budgetId;
+  final int? id;
+  final String name;
+  final double amount;
+
+  @JsonKey(fromJson: colorFromJson, toJson: colorToJson)
+  final Color color;
+
+  @JsonKey(fromJson: dateFromJson, toJson: dateToJson)
+  final DateTime createdAt;
+
+  factory BudgetCategory.fromJson(Map<String, dynamic> json) =>
+      _$BudgetCategoryFromJson(json);
+
+  Map<String, dynamic> toJson() => _$BudgetCategoryToJson(this);
+}
diff --git a/lib/models/budget_category_model.g.dart b/lib/models/budget_category_model.g.dart
new file mode 100644
index 0000000..a47efe8
--- /dev/null
+++ b/lib/models/budget_category_model.g.dart
@@ -0,0 +1,27 @@
+// 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),
+      amount: (json['amount'] as num).toDouble(),
+    );
+
+Map<String, dynamic> _$BudgetCategoryToJson(BudgetCategory instance) =>
+    <String, dynamic>{
+      'budget_id': instance.budgetId,
+      'id': instance.id,
+      'name': instance.name,
+      'amount': instance.amount,
+      'color': colorToJson(instance.color),
+      'created_at': dateToJson(instance.createdAt),
+    };
diff --git a/lib/models/family_model.dart b/lib/models/family_model.dart
new file mode 100644
index 0000000..8c9be80
--- /dev/null
+++ b/lib/models/family_model.dart
@@ -0,0 +1,27 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../global/utils.dart';
+
+part 'family_model.g.dart';
+
+@JsonSerializable()
+class FamilyModel {
+  const FamilyModel({
+    required this.id,
+    required this.budgetId,
+    required this.createdAt,
+    required this.updatedAt,
+  });
+
+  final int id, budgetId;
+
+  @JsonKey(fromJson: dateFromJson, toJson: dateToJson)
+  final DateTime createdAt;
+  @JsonKey(fromJson: dateFromJson, toJson: dateToJson)
+  final DateTime updatedAt;
+
+  factory FamilyModel.fromJson(Map<String, dynamic> json) =>
+      _$FamilyModelFromJson(json);
+
+  Map<String, dynamic> toJson() => _$FamilyModelToJson(this);
+}
diff --git a/lib/models/family_model.g.dart b/lib/models/family_model.g.dart
new file mode 100644
index 0000000..e411dbf
--- /dev/null
+++ b/lib/models/family_model.g.dart
@@ -0,0 +1,22 @@
+// 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,
+      budgetId: json['budget_id'] as int,
+      createdAt: dateFromJson(json['created_at'] as int),
+      updatedAt: dateFromJson(json['updated_at'] as int),
+    );
+
+Map<String, dynamic> _$FamilyModelToJson(FamilyModel instance) =>
+    <String, dynamic>{
+      'id': instance.id,
+      'budget_id': instance.budgetId,
+      'created_at': dateToJson(instance.createdAt),
+      'updated_at': dateToJson(instance.updatedAt),
+    };
diff --git a/lib/models/transaction_model.dart b/lib/models/transaction_model.dart
new file mode 100644
index 0000000..b2fc6a9
--- /dev/null
+++ b/lib/models/transaction_model.dart
@@ -0,0 +1,40 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../global/utils.dart';
+
+part 'transaction_model.g.dart';
+
+enum TransactionType {
+  income,
+  expense,
+}
+
+@JsonSerializable()
+class Transaction {
+  const Transaction({
+    this.id,
+    required this.amount,
+    required this.transactionType,
+    required this.budgetId,
+    required this.budgetCategoryId,
+    required this.addedByUserId,
+    required this.date,
+    this.createdAt,
+  });
+
+  final int? id;
+  final int budgetId, budgetCategoryId, addedByUserId;
+  final double amount;
+  final TransactionType transactionType;
+
+  @JsonKey(fromJson: dateFromJson, toJson: dateToJson)
+  final DateTime date;
+
+  @JsonKey(fromJson: dateFromJson, toJson: dateToJson)
+  final DateTime? createdAt;
+
+  factory Transaction.fromJson(Map<String, dynamic> json) =>
+      _$TransactionFromJson(json);
+
+  Map<String, dynamic> toJson() => _$TransactionToJson(this);
+}
diff --git a/lib/models/transaction_model.g.dart b/lib/models/transaction_model.g.dart
new file mode 100644
index 0000000..573be33
--- /dev/null
+++ b/lib/models/transaction_model.g.dart
@@ -0,0 +1,36 @@
+// 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(),
+      transactionType:
+          $enumDecode(_$TransactionTypeEnumMap, json['transaction_type']),
+      budgetId: json['budget_id'] as int,
+      budgetCategoryId: json['budget_category_id'] as int,
+      addedByUserId: json['added_by_user_id'] as int,
+      date: dateFromJson(json['date'] as int),
+      createdAt: dateFromJson(json['created_at'] as int),
+    );
+
+Map<String, dynamic> _$TransactionToJson(Transaction instance) =>
+    <String, dynamic>{
+      'id': instance.id,
+      'budget_id': instance.budgetId,
+      'budget_category_id': instance.budgetCategoryId,
+      'added_by_user_id': instance.addedByUserId,
+      'amount': instance.amount,
+      'transaction_type': _$TransactionTypeEnumMap[instance.transactionType]!,
+      'date': dateToJson(instance.date),
+      'created_at': dateToJson(instance.createdAt),
+    };
+
+const _$TransactionTypeEnumMap = {
+  TransactionType.income: 'income',
+  TransactionType.expense: 'expense',
+};
diff --git a/lib/models/user.dart b/lib/models/user.dart
new file mode 100644
index 0000000..dd2ca16
--- /dev/null
+++ b/lib/models/user.dart
@@ -0,0 +1,33 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../global/utils.dart';
+
+part 'user.g.dart';
+
+@JsonSerializable()
+class User {
+  const User({
+    this.id,
+    required this.name,
+    required this.familyId,
+    required this.budgetId,
+    this.createdAt,
+    this.updatedAt,
+    this.lastActivityAt,
+  });
+
+  final int? id;
+  final int familyId, budgetId;
+  final String name;
+
+  @JsonKey(fromJson: dateFromJson, toJson: dateToJson)
+  final DateTime? createdAt;
+  @JsonKey(fromJson: dateFromJson, toJson: dateToJson)
+  final DateTime? updatedAt;
+  @JsonKey(fromJson: dateFromJson, toJson: dateToJson)
+  final DateTime? lastActivityAt;
+
+  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
+
+  Map<String, dynamic> toJson() => _$UserToJson(this);
+}
diff --git a/lib/models/user.g.dart b/lib/models/user.g.dart
new file mode 100644
index 0000000..acd57a0
--- /dev/null
+++ b/lib/models/user.g.dart
@@ -0,0 +1,27 @@
+// 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,
+      createdAt: dateFromJson(json['created_at'] as int),
+      updatedAt: dateFromJson(json['updated_at'] as int),
+      lastActivityAt: dateFromJson(json['last_activity_at'] as int),
+    );
+
+Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
+      'id': instance.id,
+      'family_id': instance.familyId,
+      'budget_id': instance.budgetId,
+      'name': instance.name,
+      'created_at': dateToJson(instance.createdAt),
+      'updated_at': dateToJson(instance.updatedAt),
+      'last_activity_at': dateToJson(instance.lastActivityAt),
+    };
diff --git a/pubspec.lock b/pubspec.lock
index 677f451..cb577c8 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -29,10 +29,10 @@ packages:
     dependency: transitive
     description:
       name: async
-      sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
+      sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
       url: "https://pub.dev"
     source: hosted
-    version: "2.10.0"
+    version: "2.11.0"
   boolean_selector:
     dependency: transitive
     description:
@@ -109,10 +109,10 @@ packages:
     dependency: transitive
     description:
       name: characters
-      sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
+      sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
       url: "https://pub.dev"
     source: hosted
-    version: "1.2.1"
+    version: "1.3.0"
   checked_yaml:
     dependency: transitive
     description:
@@ -141,10 +141,10 @@ packages:
     dependency: transitive
     description:
       name: collection
-      sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
+      sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
       url: "https://pub.dev"
     source: hosted
-    version: "1.17.0"
+    version: "1.17.1"
   convert:
     dependency: transitive
     description:
@@ -296,6 +296,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "4.0.2"
+  intl:
+    dependency: "direct main"
+    description:
+      name: intl
+      sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.18.1"
   io:
     dependency: transitive
     description:
@@ -308,10 +316,10 @@ packages:
     dependency: transitive
     description:
       name: js
-      sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
+      sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
       url: "https://pub.dev"
     source: hosted
-    version: "0.6.5"
+    version: "0.6.7"
   json_annotation:
     dependency: "direct main"
     description:
@@ -348,10 +356,10 @@ packages:
     dependency: transitive
     description:
       name: matcher
-      sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72"
+      sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
       url: "https://pub.dev"
     source: hosted
-    version: "0.12.13"
+    version: "0.12.15"
   material_color_utilities:
     dependency: transitive
     description:
@@ -364,10 +372,10 @@ packages:
     dependency: transitive
     description:
       name: meta
-      sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
+      sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
       url: "https://pub.dev"
     source: hosted
-    version: "1.8.0"
+    version: "1.9.1"
   mime:
     dependency: transitive
     description:
@@ -388,10 +396,10 @@ packages:
     dependency: transitive
     description:
       name: path
-      sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
+      sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
       url: "https://pub.dev"
     source: hosted
-    version: "1.8.2"
+    version: "1.8.3"
   path_provider_linux:
     dependency: transitive
     description:
@@ -617,10 +625,10 @@ packages:
     dependency: transitive
     description:
       name: test_api
-      sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206
+      sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
       url: "https://pub.dev"
     source: hosted
-    version: "0.4.16"
+    version: "0.5.1"
   timing:
     dependency: transitive
     description:
@@ -694,5 +702,5 @@ packages:
     source: hosted
     version: "3.1.2"
 sdks:
-  dart: ">=2.19.6 <3.0.0"
+  dart: ">=3.0.0-0 <4.0.0"
   flutter: ">=3.3.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 8f11290..d65378c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -41,6 +41,7 @@ dependencies:
   uuid: ^3.0.7
   json_annotation: ^4.8.0
   shared_preferences: ^2.1.2
+  intl: ^0.18.1
 
 dev_dependencies:
   flutter_test: