From 7dc76e7dd6a1ea5835f1f25c6e2d4cab6dfc6e07 Mon Sep 17 00:00:00 2001 From: fres-sudo Date: Fri, 29 Mar 2024 19:51:24 +0100 Subject: [PATCH 1/3] onboarding and minor layout fixes --- assets/openVault.png | Bin 0 -> 32086 bytes ios/Flutter/AppFrameworkInfo.plist | 2 +- ios/Podfile | 2 +- ios/Podfile.lock | 31 +- ios/Runner.xcodeproj/project.pbxproj | 26 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- lib/main.dart | 12 +- lib/pages/categories/add_category.dart | 28 +- .../onboarding_page/onboarding_page.dart | 91 +++++ .../widgets/account_setup.dart | 342 ++++++++++++++++++ .../onboarding_page/widgets/add_budget.dart | 89 +++++ .../widgets/add_category_button.dart | 37 ++ .../onboarding_page/widgets/budget_setup.dart | 220 +++++++++++ .../widgets/category_button.dart | 99 +++++ .../widgets/month_selector.dart | 10 +- lib/providers/budgets_provider.dart | 2 +- lib/routes.dart | 3 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 144 +++++++- .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 21 files changed, 1104 insertions(+), 42 deletions(-) create mode 100644 assets/openVault.png create mode 100644 lib/pages/onboarding_page/onboarding_page.dart create mode 100644 lib/pages/onboarding_page/widgets/account_setup.dart create mode 100644 lib/pages/onboarding_page/widgets/add_budget.dart create mode 100644 lib/pages/onboarding_page/widgets/add_category_button.dart create mode 100644 lib/pages/onboarding_page/widgets/budget_setup.dart create mode 100644 lib/pages/onboarding_page/widgets/category_button.dart diff --git a/assets/openVault.png b/assets/openVault.png new file mode 100644 index 0000000000000000000000000000000000000000..bc3c39a0b54b48bfd4744f6b6e7c55fcb8d515b1 GIT binary patch literal 32086 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�!VvX-o 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 z%0D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 Mlvb9ikuVAQKLU>$I{*Lx literal 0 HcmV?d00001 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 9625e105..7c569640 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/ios/Podfile b/ios/Podfile index fdcc671e..d97f17e2 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '11.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f6aa5be5..7a7e5f5b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,10 +1,17 @@ PODS: - Flutter (1.0.0) + - flutter_local_notifications (0.0.1): + - Flutter - flutter_native_splash (0.0.1): - Flutter - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) + - permission_handler_apple (9.3.0): + - Flutter + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS - sqflite (0.0.3): - Flutter - FMDB (>= 2.7.5) @@ -25,13 +32,19 @@ PODS: - sqlite3/rtree - url_launcher_ios (0.0.1): - Flutter + - workmanager (0.0.1): + - Flutter DEPENDENCIES: - Flutter (from `Flutter`) + - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) + - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + - workmanager (from `.symlinks/plugins/workmanager/ios`) SPEC REPOS: trunk: @@ -41,24 +54,36 @@ SPEC REPOS: EXTERNAL SOURCES: Flutter: :path: Flutter + flutter_local_notifications: + :path: ".symlinks/plugins/flutter_local_notifications/ios" flutter_native_splash: :path: ".symlinks/plugins/flutter_native_splash/ios" + permission_handler_apple: + :path: ".symlinks/plugins/permission_handler_apple/ios" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" sqflite: :path: ".symlinks/plugins/sqflite/ios" sqlite3_flutter_libs: :path: ".symlinks/plugins/sqlite3_flutter_libs/ios" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" + workmanager: + :path: ".symlinks/plugins/workmanager/ios" SPEC CHECKSUMS: - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086 flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 + shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a sqlite3: 6e2d4a4879854d0ec86b476bf3c3e30870bac273 sqlite3_flutter_libs: eb769059df0356dc52ddda040f09cacc9391a7cf url_launcher_ios: 68d46cc9766d0c41dbdc884310529557e3cd7a86 + workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6 -PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189 +PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 0a53136a..80823f5c 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -199,6 +199,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 211392328957DBA96447070D /* [CP] Embed Pods Frameworks */, + 6090A52EFEF53161A56AE5DE /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -216,7 +217,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C8080294A63A400263BE5 = { @@ -303,6 +304,23 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 6090A52EFEF53161A56AE5DE /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -453,7 +471,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -580,7 +598,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -629,7 +647,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 87131a09..8e3ca5df 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ runApp(const ProviderScope(child: Launcher()))); } @@ -35,7 +44,8 @@ class Launcher extends ConsumerWidget { themeMode: appThemeState.isDarkModeEnabled ? ThemeMode.dark : ThemeMode.light, onGenerateRoute: makeRoute, - initialRoute: '/', + //initialRoute: _isFirstLogin == null || _isFirstLogin! ? '/onboarding' : '/', + initialRoute: '/onboarding', //TODO: comment this line and uncomment the other ); } } diff --git a/lib/pages/categories/add_category.dart b/lib/pages/categories/add_category.dart index c74bcb97..ac8d4bdb 100644 --- a/lib/pages/categories/add_category.dart +++ b/lib/pages/categories/add_category.dart @@ -275,10 +275,10 @@ class _AddCategoryState extends ConsumerState with Functions { ], ), ), - Container( + Align( alignment: Alignment.bottomCenter, child: Container( - width: double.infinity, + width: MediaQuery.sizeOf(context).width, decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, boxShadow: [ @@ -289,7 +289,7 @@ class _AddCategoryState extends ConsumerState with Functions { ) ], ), - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), + padding: const EdgeInsets.only(left:24 ,right: 24, top: 16, bottom: 30), child: Container( decoration: BoxDecoration( boxShadow: [defaultShadow], @@ -297,16 +297,18 @@ class _AddCategoryState extends ConsumerState with Functions { ), child: TextButton( onPressed: () async { - if (selectedCategory != null) { - ref - .read(categoriesProvider.notifier) - .updateCategory(nameController.text) - .whenComplete(() => Navigator.of(context).pop()); - } else { - ref - .read(categoriesProvider.notifier) - .addCategory(nameController.text) - .whenComplete(() => Navigator.of(context).pop()); + if(nameController.text.isNotEmpty) { + if (selectedCategory != null) { + ref + .read(categoriesProvider.notifier) + .updateCategory(nameController.text) + .whenComplete(() => Navigator.of(context).pop()); + } else { + ref + .read(categoriesProvider.notifier) + .addCategory(nameController.text) + .whenComplete(() => Navigator.of(context).pop()); + } } }, style: TextButton.styleFrom( diff --git a/lib/pages/onboarding_page/onboarding_page.dart b/lib/pages/onboarding_page/onboarding_page.dart new file mode 100644 index 00000000..9e9be360 --- /dev/null +++ b/lib/pages/onboarding_page/onboarding_page.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; +import '/pages/onboarding_page/widgets/budget_setup.dart'; +import '/constants/style.dart'; + +class Onboarding extends StatefulWidget { + const Onboarding({Key? key}) : super(key: key); + + @override + State createState() => _OnboardingState(); +} + +class _OnboardingState extends State { + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: blue7, + body: SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: MediaQuery.sizeOf(context).height/9, + ), + Text( + 'Set up the app', + style: Theme.of(context) + .textTheme + .headlineLarge + ?.copyWith(color: blue1), + ), + const SizedBox( + height: 80, + ), + Image.asset( + 'assets/openVault.png', + height: MediaQuery.sizeOf(context).height/3.7, + ), + const SizedBox( + height: 74, + ), + Text( + 'In a few steps you\'ll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip', + textAlign: TextAlign.center, + style: + Theme.of(context).textTheme.bodySmall?.copyWith(color: blue1), + ), + + ], + + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10), + child: SizedBox( + width: MediaQuery.sizeOf(context).width, + height: 48, + child: ElevatedButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const BudgetSetup(), + ), + ); + }, + style: ElevatedButton.styleFrom( + backgroundColor: blue5, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: Text( + 'START THE SET UP', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: white), + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/onboarding_page/widgets/account_setup.dart b/lib/pages/onboarding_page/widgets/account_setup.dart new file mode 100644 index 00000000..b82c962d --- /dev/null +++ b/lib/pages/onboarding_page/widgets/account_setup.dart @@ -0,0 +1,342 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter/material.dart'; +import '../../../constants/constants.dart'; +import '../../../providers/accounts_provider.dart'; +import '/constants/style.dart'; + +final showAccountIconsProvider = StateProvider.autoDispose((ref) => false); + +class AccountSetup extends ConsumerStatefulWidget { + const AccountSetup({Key? key}) : super(key: key); + + @override + ConsumerState createState() => _AccountSetupState(); +} + +class _AccountSetupState extends ConsumerState { + TextEditingController accountNameController = TextEditingController(); + TextEditingController amountController = TextEditingController(); + + + bool _validName = false; + bool _validAmount = false; + + // Function to validate amount + void validateAmount(String value) { + setState(() { + // Check if value contains only digits and has at least 2 digits + _validAmount = RegExp(r'^\d{1,}$').hasMatch(value); + }); + } + + // Function to validate account name + void validateName(String value) { + setState(() { + // Check if value is not empty and has more than 3 characters + _validName = value.isNotEmpty && value.length > 3; + }); + } + + + @override + void dispose() { + accountNameController.dispose(); + amountController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final accountIcon = ref.watch(accountIconProvider); + final accountColor = ref.watch(accountColorProvider); + + return Scaffold( + backgroundColor: blue7, + resizeToAvoidBottomInset: false, + body: SafeArea( + child: Center( + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + Text("STEP 2 OF 2", style: Theme.of(context).textTheme.labelSmall), + const SizedBox(height: 20), + Text( + "Set the liquidity in your main\naccount", + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .headlineLarge + ?.copyWith(color: blue1), + ), + const SizedBox(height: 20), + Text( + "It will be used as a baseline to which you can add\nincome, expenses and calculate your wealth.\n\nYou’ll be able to add more accounts within the app.", + textAlign: TextAlign.center, + style: + Theme.of(context).textTheme.bodySmall?.copyWith(color: blue1), + ), + const SizedBox(height: 10), + Container( + margin: const EdgeInsets.only(left: 25.0, right: 25.0), + padding: const EdgeInsets.only(left: 20.0, right: 20.0,top: 15, bottom: 15), + decoration: BoxDecoration( + color: white, + shape: BoxShape.rectangle, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 5, + blurRadius: 20, + offset: const Offset(2, 2), + ), + ], + borderRadius: const BorderRadius.all(Radius.circular(20))), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("ACCOUNT NAME ", + style: Theme.of(context).textTheme.labelSmall?.copyWith(color: grey1)), + const Icon(Icons.edit, size: 10) + ], + ), + TextField( + textAlign: TextAlign.center, + controller: accountNameController, + onChanged: validateName, + autofocus: true, + inputFormatters: [ + FilteringTextInputFormatter.deny(RegExp('[ ]')), + ], + decoration: InputDecoration( + hintText: "Main Account", + errorStyle: Theme.of(context).textTheme.bodyLarge?.copyWith(fontSize: 10, color: red), + hintStyle: Theme.of(context).textTheme.bodyLarge, + border: const UnderlineInputBorder( + borderSide: BorderSide(color: grey2, width: 0.2), + ), + ), + ), + const SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("SET AMOUNT ", + style: Theme.of(context) + .textTheme + .labelSmall + ?.copyWith(color: grey1)), + const Icon(Icons.edit, size: 10) + ], + ), + TextField( + textAlign: TextAlign.center, + controller: amountController, + onChanged: validateAmount, + inputFormatters: [ + FilteringTextInputFormatter.deny(RegExp('[ ]')), + ], + decoration: InputDecoration( + hintText: "e.g 1300 €", + suffixText: "€", + errorStyle: Theme.of(context).textTheme.bodyLarge?.copyWith(fontSize: 10, color: red), + hintStyle: Theme.of(context).textTheme.bodySmall, + border: const UnderlineInputBorder( + borderSide:BorderSide(color: grey2, width: 0.2), + ), + ), + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("EDIT ICON AND COLOR ", + style: Theme.of(context) + .textTheme + .labelSmall + ?.copyWith(color: grey1)), + const Icon(Icons.edit, size: 10) + ], + ), + const SizedBox(height: 6), + Material( + color: Colors.transparent, + child: InkWell( + borderRadius: const BorderRadius.all(Radius.circular(90)), + onTap: () => ref.read(showAccountIconsProvider.notifier).state = true, + child: Ink( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: accountColorList[accountColor], + ), + padding: const EdgeInsets.all(16), + child: Icon( + accountIconList[accountIcon], + size: 36, + color: Theme.of(context).colorScheme.background, + ), + ), + ), + ), + const SizedBox(height: 10), + SizedBox( + height: 38, + child: ListView.separated( + scrollDirection: Axis.horizontal, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric(horizontal: 16), + separatorBuilder: (context, index) => const SizedBox(width: 16), + itemBuilder: (context, index) { + Color color = accountColorList[index]; + return GestureDetector( + onTap: () => ref.read(accountColorProvider.notifier).state = index, + child: Container( + height: accountColorList[accountColor] == color ? 38 : 32, + width: accountColorList[accountColor] == color ? 38 : 32, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: color, + border: accountColorList[accountColor] == color + ? Border.all( + color: Theme.of(context).colorScheme.primary, + width: 3, + ) + : null, + ), + ), + ); + }, + itemCount: accountColorList.length, + ), + ), + const Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Divider(height: 1, color: grey2), + ), + SizedBox( + height: 38, + child: ListView.separated( + scrollDirection: Axis.horizontal, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric(horizontal: 16), + separatorBuilder: (context, index) => const SizedBox(width: 16), + itemBuilder: (context, index) { + IconData accountIconData = + accountIconList.values.elementAt(index); + String accountIconName = accountIconList.keys.elementAt(index); + return GestureDetector( + onTap: () => ref.read(accountIconProvider.notifier).state = + accountIconName, + child: Container( + width: 38, + height: 38, + margin: const EdgeInsets.all(2), + decoration: BoxDecoration( + color: accountIconList[accountIcon] == accountIconData + ? Theme.of(context).colorScheme.secondary + : Theme.of(context).colorScheme.surface, + shape: BoxShape.circle,), + child: Icon( + accountIconData, + color: accountIconList[accountIcon] == accountIconData + ? Colors.white + : Theme.of(context).colorScheme.primary, + size: 24, + ), + ), + ); + }, + itemCount: accountIconList.length, + ), + ), + ], + ), + ), + ], + ), + const SizedBox( + height: 20, + ), + Column( + children: [ + Text('Or you can skip this step and start from 0', + style: Theme.of(context) + .textTheme + .bodySmall + ?.copyWith(color: blue1)), + const SizedBox(height: 10), + ElevatedButton( + onPressed: () { + Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false); + }, + style: ElevatedButton.styleFrom( + elevation: 0.0, + shadowColor: Colors.transparent, + backgroundColor: Colors.transparent, + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('START FROM 0 ', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: blue1)), + const Icon(Icons.arrow_forward, + size: 15, color: blue1), + ], + ), + SizedBox( + width: MediaQuery.sizeOf(context).width/3, + child: const Divider( + color: blue1, + thickness: 1, + ), + ) + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0,vertical: 15), + child: SizedBox( + width: MediaQuery.sizeOf(context).width, + height: 48, + child: ElevatedButton( + onPressed: () { + if(_validName && _validAmount){ + ref.watch(accountsProvider.notifier).addAccount(accountNameController.text, num.tryParse(amountController.text)); + Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false); + } + }, + style: ElevatedButton.styleFrom( + backgroundColor: _validName && _validAmount ? blue5 : grey2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: Text('START TRACKING YOUR EXPENSES', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: Colors.white)), + ), + ), + ), + ], + ) + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/onboarding_page/widgets/add_budget.dart b/lib/pages/onboarding_page/widgets/add_budget.dart new file mode 100644 index 00000000..e1ef5691 --- /dev/null +++ b/lib/pages/onboarding_page/widgets/add_budget.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:sossoldi/constants/functions.dart'; +import 'package:sossoldi/constants/style.dart'; +import 'package:sossoldi/pages/onboarding_page/widgets/budget_setup.dart'; +import 'package:sossoldi/providers/budgets_provider.dart'; +import '../../../model/budget.dart'; +import '../../../model/category_transaction.dart'; + +class AddBudget extends ConsumerStatefulWidget { + final CategoryTransaction category; + + const AddBudget(this.category, {Key? key}) : super(key: key); + + @override + ConsumerState createState() => _AddBudgetState(); +} + +class _AddBudgetState extends ConsumerState with Functions { + final TextEditingController amountController = TextEditingController(); + + List? budgetsList = []; + @override + void didChangeDependencies() { + super.didChangeDependencies(); + // Initialize the text controller with the current budget amount + budgetsList = ref.watch(budgetsProvider).value; + const Budget defaultBudget = Budget(idCategory: 99999, name: '', amountLimit: 9999, active: false); + + final Budget? budget = budgetsList?.firstWhere((element) => element.idCategory == widget.category.id, orElse: () => defaultBudget); + + if (budget != null) { + amountController.text = budget.amountLimit.toString(); + } + if(budget == defaultBudget){ + amountController.text = ""; + } + + } + + @override + void dispose() { + amountController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text( + 'Add budget for ${widget.category.name}', + style: Theme.of(context).textTheme.bodyMedium, + textAlign: TextAlign.center, + ), + content: TextField( + controller: amountController, + keyboardType: TextInputType.number, + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text('CANCEL', style: Theme.of(context).textTheme.bodyMedium), + ), + ElevatedButton( + onPressed: () async { + await ref.watch(budgetsProvider.notifier).addBudget( + Budget( + name: widget.category.name, + createdAt: DateTime.now(), + idCategory: widget.category.id!, + amountLimit: num.tryParse(amountController.text) ?? 0, + active: true, + )).whenComplete(() => Navigator.pop(context)); + }, + style: ElevatedButton.styleFrom( + backgroundColor: blue5, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: Text( + 'CONFIRM', + style: Theme.of(context).textTheme.bodyMedium?.apply(color: white), + ), + ), + ], + ); + } +} diff --git a/lib/pages/onboarding_page/widgets/add_category_button.dart b/lib/pages/onboarding_page/widgets/add_category_button.dart new file mode 100644 index 00000000..b3562543 --- /dev/null +++ b/lib/pages/onboarding_page/widgets/add_category_button.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +import '../../../constants/style.dart'; + +class AddCategoryButton extends StatelessWidget { + const AddCategoryButton({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + border: Border.all(color: grey2, width: 1.5), + color: grey3, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + const Padding( + padding: EdgeInsets.symmetric(horizontal: 10), + child: Icon(Icons.add_circle_outline_outlined, size: 30, color: grey1) + ), + Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + "Add category", + style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: grey1), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/pages/onboarding_page/widgets/budget_setup.dart b/lib/pages/onboarding_page/widgets/budget_setup.dart new file mode 100644 index 00000000..40d67e2d --- /dev/null +++ b/lib/pages/onboarding_page/widgets/budget_setup.dart @@ -0,0 +1,220 @@ +import 'dart:ui'; + +import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:sossoldi/pages/onboarding_page/widgets/add_category_button.dart'; +import 'package:sossoldi/pages/onboarding_page/widgets/category_button.dart'; + +import '/constants/constants.dart'; +import '/constants/style.dart'; +import '/model/budget.dart'; +import '/pages/onboarding_page/widgets/account_setup.dart'; +import '/pages/onboarding_page/widgets/add_budget.dart'; +import '/providers/budgets_provider.dart'; +import '/providers/categories_provider.dart'; + +class BudgetSetup extends ConsumerStatefulWidget { + const BudgetSetup({Key? key}) : super(key: key); + + @override + ConsumerState createState() => _BudgetSetupState(); +} + +class _BudgetSetupState extends ConsumerState { + // sum of the budget of the selected cards + + List? budgetsList = []; + num totalBudget = 0; //ref.read(budgetAmountLimitProvider.notifier).state; + + @override + Widget build(BuildContext context) { + budgetsList = ref.watch(budgetsProvider).value; + totalBudget = budgetsList?.fold( + 0, (total, budget) => total + budget.amountLimit) ?? 0; + final categoriesGrid = ref.watch(categoriesProvider); + return Scaffold( + backgroundColor: blue7, + body: SafeArea( + child: Padding( + padding: const EdgeInsets.only(left: 16.0, right: 16), + child: Column( + children: [ + Text("STEP 1 OF 2", + style: Theme.of(context).textTheme.labelSmall), + const SizedBox(height: 20), + Text( + "Set up your monthly\nbudgets", + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .headlineLarge + ?.copyWith(color: blue1), + ), + const SizedBox(height: 30), + Text( + "Choose which categories you want to set a budget for", + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .bodySmall + ?.copyWith(color: blue1), + ), + const SizedBox(height: 16), + Expanded( + child: NotificationListener( + onNotification: (OverscrollIndicatorNotification overscroll) { + overscroll.disallowIndicator(); + return true; + }, + child: categoriesGrid.when( + data: (categories) => GridView.builder( + itemCount: categories.length + 1, + scrollDirection: Axis.vertical, + gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 300, + childAspectRatio: 3, + crossAxisSpacing: 18, + mainAxisSpacing: 12, + ), + itemBuilder: (context, i) { + if (i < categories.length) { + return GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (context) => + AddBudget(categories.elementAt(i)), + ); + }, + child: CategoryButton( + categoryColor: categoryColorList[categories.elementAt(i).color], + categoryName: categories.elementAt(i).name, + budget: budgetsList?.firstWhereOrNull((budget) => budget.idCategory == categories.elementAt(i).id), + ) + ); + } else { + return GestureDetector( + onTap: () => Navigator.of(context) + .pushNamed('/add-category'), + child: const AddCategoryButton(), + ); + } + }, + ), + error: (err, stack) => Text('Error: $err'), + loading: () => const Center( + child: CircularProgressIndicator(), + ), + ), + ), + ), + + // if the total budget (sum of the budget of the selected cards) is > 0, set the other layout. otherwise set the "continue without budget" button + totalBudget > 0 + ? Center( + child: Column( + children: [ + const SizedBox(height: 10), + Text("Monthly budget total:", + style: Theme.of(context).textTheme.bodySmall), + const SizedBox(height: 10), + RichText( + text: TextSpan( + children: [ + TextSpan( + text: totalBudget.toString(), + style: + Theme.of(context).textTheme.displayMedium, + ), + TextSpan( + text: "€", + style: Theme.of(context) + .textTheme + .bodyMedium + ?.apply( + fontFeatures: [ + const FontFeature.subscripts() + ], + ), + ), + ], + ), + ), + const SizedBox(height: 20), + SizedBox( + width: MediaQuery.sizeOf(context).width, + height: 48, + child: ElevatedButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + const AccountSetup()), + ); + }, + style: ElevatedButton.styleFrom( + backgroundColor: blue5, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: Text('NEXT STEP', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: Colors.white)), + ), + ), + ], + ), + ) + : ElevatedButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const AccountSetup()), + ); + }, + style: ElevatedButton.styleFrom( + elevation: 0.0, + shadowColor: Colors.transparent, + backgroundColor: Colors.transparent, + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('CONTINUE WITHOUT BUDGET ', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: blue1)), + const Icon(Icons.arrow_forward, + size: 15, color: blue1), + ], + ), + SizedBox( + width: MediaQuery.sizeOf(context).width / 1.6, + child: const Divider( + color: blue1, + thickness: 1, + ), + ) + ], + ), + ), + const SizedBox(height: 20), + ], + ), + ), + ), + ); + } +} + + diff --git a/lib/pages/onboarding_page/widgets/category_button.dart b/lib/pages/onboarding_page/widgets/category_button.dart new file mode 100644 index 00000000..e1e38b63 --- /dev/null +++ b/lib/pages/onboarding_page/widgets/category_button.dart @@ -0,0 +1,99 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +import '../../../constants/style.dart'; +import '../../../model/budget.dart'; + +class CategoryButton extends StatelessWidget { + const CategoryButton({super.key, required this.categoryColor, required this.categoryName, this.budget}); + + final Color categoryColor; + final String categoryName; + final Budget? budget; + + @override + Widget build(BuildContext context) { + if(budget != null && budget!.active && budget!.amountLimit > 0) { + return Container( + decoration: BoxDecoration( + border: Border.all(color: categoryColor, width: 2.5), + color: categoryColor, + borderRadius: BorderRadius.circular(8), + ), + padding: const EdgeInsets.only(left: 12), + child: Row( + children: [ + Container( + decoration: const BoxDecoration( + color: white, + shape: BoxShape.circle, + ), + padding: const EdgeInsets.all(2), + child: + Icon(Icons.check_rounded, color: categoryColor, size: 22), + ), + const SizedBox(width: 10), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + categoryName, + textAlign: TextAlign.left, + style: Theme.of(context) + .textTheme + .bodyLarge + ?.copyWith(color: white), + ), + Text("BUDGET: ${budget?.amountLimit}€", + style: Theme.of(context) + .textTheme + .bodyLarge + ?.copyWith(fontSize: 10, color: white)) + ], + ), + ), + ], + ), + ); + } else { + return Container( + decoration: BoxDecoration( + border: Border.all(color: categoryColor, width: 2.5), + color: HSLColor.fromColor(categoryColor) + .withLightness(clampDouble(0.99, 0.0, 0.9)) + .toColor(), + borderRadius: BorderRadius.circular(8), + ), + alignment: Alignment.center, + child: Row( + children: [ + const Padding( + padding: EdgeInsets.symmetric(horizontal: 10), + child: Icon(Icons.add_circle_outline_outlined, + size: 30, color: blue1)), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + categoryName, + textAlign: TextAlign.left, + style: Theme.of(context).textTheme.bodyMedium, + ), + Text("ADD BUDGET", + style: Theme.of(context).textTheme.labelMedium), + ], + ), + ), + ], + ), + ); + } + } +} diff --git a/lib/pages/transactions_page/widgets/month_selector.dart b/lib/pages/transactions_page/widgets/month_selector.dart index 0894b6dc..57f8647c 100644 --- a/lib/pages/transactions_page/widgets/month_selector.dart +++ b/lib/pages/transactions_page/widgets/month_selector.dart @@ -67,9 +67,9 @@ class MonthSelector extends ConsumerWidget with Functions { height: height, width: height, color: Theme.of(context).colorScheme.primary, - child: const Icon( + child: Icon( Icons.chevron_left, - color: Colors.white, + color: Theme.of(context).colorScheme.primaryContainer, ), ), ), @@ -78,7 +78,7 @@ class MonthSelector extends ConsumerWidget with Functions { children: [ Text( getFormattedDateRange(startDate, endDate), - style: Theme.of(context).textTheme.titleLarge, + style: Theme.of(context).textTheme.titleLarge?.copyWith(color: darkWhite), ), RichText( text: TextSpan( @@ -115,9 +115,9 @@ class MonthSelector extends ConsumerWidget with Functions { height: height, width: height, color: Theme.of(context).colorScheme.primary, - child: const Icon( + child: Icon( Icons.chevron_right, - color: Colors.white, + color: Theme.of(context).colorScheme.primaryContainer, ), ), ), diff --git a/lib/providers/budgets_provider.dart b/lib/providers/budgets_provider.dart index b28686fb..c63ae437 100644 --- a/lib/providers/budgets_provider.dart +++ b/lib/providers/budgets_provider.dart @@ -20,7 +20,7 @@ class AsyncBudgetsNotifier extends AsyncNotifier> { Future addBudget(Budget budget) async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { - await BudgetMethods().insert(budget); + await BudgetMethods().insertOrUpdate(budget); return _getBudgets(); }); } diff --git a/lib/routes.dart b/lib/routes.dart index eb802944..1823f3d7 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -1,5 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:sossoldi/pages/onboarding_page/onboarding_page.dart'; import 'pages/account_page/account_page.dart'; import 'pages/accounts/account_list.dart'; @@ -24,6 +25,8 @@ Route makeRoute(RouteSettings settings) { switch (settings.name) { case '/': return _materialPageRoute(settings.name, const Structure()); + case '/onboarding': + return _materialPageRoute(settings.name, const Onboarding()); case '/dashboard': return _materialPageRoute(settings.name, const HomePage()); case '/add-page': diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 6bbda399..036b9901 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,12 +5,14 @@ import FlutterMacOS import Foundation +import flutter_local_notifications import shared_preferences_foundation import sqflite import sqlite3_flutter_libs import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 6307e251..e2c052e0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -109,10 +109,10 @@ packages: dependency: transitive description: name: coverage - sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" + sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" url: "https://pub.dev" source: hosted - version: "1.6.4" + version: "1.7.2" crypto: dependency: transitive description: @@ -137,6 +137,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + dbus: + dependency: transitive + description: + name: dbus + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" + url: "https://pub.dev" + source: hosted + version: "0.7.10" equatable: dependency: transitive description: @@ -198,6 +206,30 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: c18f1de98fe0bb9dd5ba91e1330d4febc8b6a7de6aae3ffe475ef423723e72f3 + url: "https://pub.dev" + source: hosted + version: "16.3.2" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03" + url: "https://pub.dev" + source: hosted + version: "4.0.0+1" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + sha256: "7cf643d6d5022f3baed0be777b0662cce5919c0a7b86e700299f22dc4ae660ef" + url: "https://pub.dev" + source: hosted + version: "7.0.0+1" flutter_native_splash: dependency: "direct main" description: @@ -312,6 +344,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" lints: dependency: transitive description: @@ -332,26 +388,26 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" mime: dependency: transitive description: @@ -380,10 +436,10 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" path_provider_linux: dependency: transitive description: @@ -416,6 +472,54 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.3" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: "45ff3fbcb99040fde55c528d5e3e6ca29171298a85436274d49c6201002087d6" + url: "https://pub.dev" + source: hosted + version: "11.2.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474" + url: "https://pub.dev" + source: hosted + version: "12.0.5" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662 + url: "https://pub.dev" + source: hosted + version: "9.4.4" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d" + url: "https://pub.dev" + source: hosted + version: "0.1.1" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20" + url: "https://pub.dev" + source: hosted + version: "4.2.1" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" petitparser: dependency: transitive description: @@ -701,6 +805,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.9" + timezone: + dependency: transitive + description: + name: timezone + sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0" + url: "https://pub.dev" + source: hosted + version: "0.9.2" typed_data: dependency: transitive description: @@ -793,10 +905,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "11.10.0" + version: "13.0.0" watcher: dependency: transitive description: @@ -837,6 +949,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.1.1" + workmanager: + dependency: "direct main" + description: + name: workmanager + sha256: ed13530cccd28c5c9959ad42d657cd0666274ca74c56dea0ca183ddd527d3a00 + url: "https://pub.dev" + source: hosted + version: "0.5.2" xdg_directories: dependency: transitive description: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 76d5285b..36f7ed9e 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,10 +6,13 @@ #include "generated_plugin_registrant.h" +#include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); Sqlite3FlutterLibsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 22aeaaea..41a82805 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + permission_handler_windows sqlite3_flutter_libs url_launcher_windows ) From 40d61dd0c38517886435464bb4ea75eb4ec356b2 Mon Sep 17 00:00:00 2001 From: fres-sudo Date: Sun, 31 Mar 2024 11:22:55 +0200 Subject: [PATCH 2/3] added add account button even if there's no account, edit keybord number input, add method to set currency at the start of the application --- lib/main.dart | 3 +- lib/model/bank_account.dart | 2 +- lib/model/currency.dart | 7 +++++ .../add_page/widgets/amount_section.dart | 14 +++++++-- .../add_page/widgets/category_selector.dart | 2 +- lib/pages/home_page.dart | 5 +-- .../widgets/account_setup.dart | 31 ++++++++++++++----- .../onboarding_page/widgets/add_budget.dart | 1 - lib/providers/currency_provider.dart | 11 +++++++ 9 files changed, 59 insertions(+), 17 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 74798484..6a05b6db 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -44,8 +44,7 @@ class Launcher extends ConsumerWidget { themeMode: appThemeState.isDarkModeEnabled ? ThemeMode.dark : ThemeMode.light, onGenerateRoute: makeRoute, - //initialRoute: _isFirstLogin == null || _isFirstLogin! ? '/onboarding' : '/', - initialRoute: '/onboarding', //TODO: comment this line and uncomment the other + initialRoute: _isFirstLogin == null || _isFirstLogin! ? '/onboarding' : '/', ); } } diff --git a/lib/model/bank_account.dart b/lib/model/bank_account.dart index 89458faa..fd6f7e4c 100644 --- a/lib/model/bank_account.dart +++ b/lib/model/bank_account.dart @@ -141,7 +141,7 @@ class BankAccountMethods extends SossoldiDatabase { if (maps.isNotEmpty) { return BankAccount.fromJson(maps.first); } else { - throw Exception('Main Account not found'); + return null; } } diff --git a/lib/model/currency.dart b/lib/model/currency.dart index 6a49af93..8f3a7a86 100644 --- a/lib/model/currency.dart +++ b/lib/model/currency.dart @@ -93,6 +93,13 @@ class CurrencyMethods extends SossoldiDatabase { return item.copy(id: id); } + Future insertAll(List list) async { + final db = await database; + for(Currency currency in list){ + await db.insert(currencyTable, currency.toJson()); + } + } + Future selectById(int id) async { final db = await database; diff --git a/lib/pages/add_page/widgets/amount_section.dart b/lib/pages/add_page/widgets/amount_section.dart index 2c743fef..273d4b33 100644 --- a/lib/pages/add_page/widgets/amount_section.dart +++ b/lib/pages/add_page/widgets/amount_section.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import "package:flutter_riverpod/flutter_riverpod.dart"; import '../../../constants/functions.dart'; @@ -162,7 +163,7 @@ class _AmountSectionState extends ConsumerState with Functions { ), const Spacer(), Text( - ref.watch(bankAccountProvider)!.name, + ref.watch(bankAccountProvider)?.name ?? "", style: Theme.of(context).textTheme.bodySmall!.copyWith( color: grey1, ), @@ -282,7 +283,10 @@ class _AmountSectionState extends ConsumerState with Functions { .headlineMedium! .copyWith(color: typeToColor(selectedType)), ), - keyboardType: const TextInputType.numberWithOptions(decimal: true), + keyboardType: const TextInputType.numberWithOptions(decimal: true, signed: true), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,2}')), + ], // inputFormatters: [DecimalTextInputFormatter(decimalDigits: 2)], autofocus: false, textAlign: TextAlign.center, @@ -292,6 +296,12 @@ class _AmountSectionState extends ConsumerState with Functions { fontSize: 58, fontWeight: FontWeight.bold, ), + onTapOutside: (_){ + FocusScopeNode currentFocus = FocusScope.of(context); + if (!currentFocus.hasPrimaryFocus) { + currentFocus.unfocus(); + } + }, ), ), ], diff --git a/lib/pages/add_page/widgets/category_selector.dart b/lib/pages/add_page/widgets/category_selector.dart index 45b0527b..65e16b52 100644 --- a/lib/pages/add_page/widgets/category_selector.dart +++ b/lib/pages/add_page/widgets/category_selector.dart @@ -60,7 +60,7 @@ class _CategorySelectorState extends ConsumerState with Functi width: double.infinity, child: categoriesList.when( data: (categories) => ListView.builder( - itemCount: 4, + itemCount: categories.length, // to prevent range error scrollDirection: Axis.horizontal, shrinkWrap: true, itemBuilder: (context, i) { diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 5a0418cb..095a8bfc 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -217,7 +217,7 @@ class _HomePageState extends ConsumerState with Functions { physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, itemBuilder: (context, i) { - if (i == accounts.length) { + if (i == accounts.length || accounts.isEmpty) { return Padding( padding: const EdgeInsets.fromLTRB(0, 4, 0, 16), child: Container( @@ -264,10 +264,11 @@ class _HomePageState extends ConsumerState with Functions { ), ), ); - } else { + } else if(accounts.isNotEmpty) { BankAccount account = accounts[i]; return AccountsSum(account: account); } + }, ), loading: () => const SizedBox(), diff --git a/lib/pages/onboarding_page/widgets/account_setup.dart b/lib/pages/onboarding_page/widgets/account_setup.dart index b82c962d..59b3820e 100644 --- a/lib/pages/onboarding_page/widgets/account_setup.dart +++ b/lib/pages/onboarding_page/widgets/account_setup.dart @@ -1,6 +1,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter/material.dart'; +import 'package:sossoldi/model/currency.dart'; +import 'package:sossoldi/providers/currency_provider.dart'; import '../../../constants/constants.dart'; import '../../../providers/accounts_provider.dart'; import '/constants/style.dart'; @@ -25,16 +27,14 @@ class _AccountSetupState extends ConsumerState { // Function to validate amount void validateAmount(String value) { setState(() { - // Check if value contains only digits and has at least 2 digits - _validAmount = RegExp(r'^\d{1,}$').hasMatch(value); + _validAmount = RegExp(r'^\d*\.?\d{0,2}$').hasMatch(value); }); } - // Function to validate account name +// Function to validate name void validateName(String value) { setState(() { - // Check if value is not empty and has more than 3 characters - _validName = value.isNotEmpty && value.length > 3; + _validName = RegExp(r'^[a-zA-Z\s]{3,}$').hasMatch(value); }); } @@ -112,16 +112,22 @@ class _AccountSetupState extends ConsumerState { onChanged: validateName, autofocus: true, inputFormatters: [ - FilteringTextInputFormatter.deny(RegExp('[ ]')), + FilteringTextInputFormatter.deny(RegExp(r'^[a-zA-Z]{10,}$')), ], decoration: InputDecoration( hintText: "Main Account", errorStyle: Theme.of(context).textTheme.bodyLarge?.copyWith(fontSize: 10, color: red), - hintStyle: Theme.of(context).textTheme.bodyLarge, + hintStyle: Theme.of(context).textTheme.bodySmall, border: const UnderlineInputBorder( borderSide: BorderSide(color: grey2, width: 0.2), ), ), + onTapOutside: (_){ + FocusScopeNode currentFocus = FocusScope.of(context); + if (!currentFocus.hasPrimaryFocus) { + currentFocus.unfocus(); + } + }, ), const SizedBox(height: 15), Row( @@ -138,9 +144,10 @@ class _AccountSetupState extends ConsumerState { TextField( textAlign: TextAlign.center, controller: amountController, + keyboardType: const TextInputType.numberWithOptions(decimal: true, signed: true), onChanged: validateAmount, inputFormatters: [ - FilteringTextInputFormatter.deny(RegExp('[ ]')), + FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,2}')), ], decoration: InputDecoration( hintText: "e.g 1300 €", @@ -151,6 +158,12 @@ class _AccountSetupState extends ConsumerState { borderSide:BorderSide(color: grey2, width: 0.2), ), ), + onTapOutside: (_){ + FocusScopeNode currentFocus = FocusScope.of(context); + if (!currentFocus.hasPrimaryFocus) { + currentFocus.unfocus(); + } + }, ), const SizedBox(height: 8), Row( @@ -273,6 +286,7 @@ class _AccountSetupState extends ConsumerState { const SizedBox(height: 10), ElevatedButton( onPressed: () { + ref.watch(currencyStateNotifier.notifier).insertAll(); Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false); }, style: ElevatedButton.styleFrom( @@ -313,6 +327,7 @@ class _AccountSetupState extends ConsumerState { onPressed: () { if(_validName && _validAmount){ ref.watch(accountsProvider.notifier).addAccount(accountNameController.text, num.tryParse(amountController.text)); + ref.watch(currencyStateNotifier.notifier).insertAll(); Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false); } }, diff --git a/lib/pages/onboarding_page/widgets/add_budget.dart b/lib/pages/onboarding_page/widgets/add_budget.dart index e1ef5691..0a924f78 100644 --- a/lib/pages/onboarding_page/widgets/add_budget.dart +++ b/lib/pages/onboarding_page/widgets/add_budget.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:sossoldi/constants/functions.dart'; import 'package:sossoldi/constants/style.dart'; -import 'package:sossoldi/pages/onboarding_page/widgets/budget_setup.dart'; import 'package:sossoldi/providers/budgets_provider.dart'; import '../../../model/budget.dart'; import '../../../model/category_transaction.dart'; diff --git a/lib/providers/currency_provider.dart b/lib/providers/currency_provider.dart index 6601e454..2774b518 100644 --- a/lib/providers/currency_provider.dart +++ b/lib/providers/currency_provider.dart @@ -26,6 +26,17 @@ class CurrencyState extends ChangeNotifier { notifyListeners(); } + void insertAll() { + Currency euro = const Currency(symbol: "€", code: "EUR", name: "Euro", mainCurrency: true); + Currency dollar = const Currency(symbol: "\$", code: "USD", name: "United States Dollar", mainCurrency: false); + Currency franc = const Currency(symbol: "CHF", code: "CHF", name: "Switzerland Franc", mainCurrency: false); + Currency pound = const Currency(symbol: "£", code: "GBP", name: "United Kingdom Pound", mainCurrency: false); + + List list = [euro, dollar, franc, pound]; + CurrencyMethods().insertAll(list); + notifyListeners(); + } + void setSelectedCurrency(Currency currency) { selectedCurrency = currency; CurrencyMethods().changeMainCurrency(currency.id!); From 5a123c2a11018b7bc9f2c6e8006b216d3b85d549 Mon Sep 17 00:00:00 2001 From: fres-sudo Date: Sun, 21 Apr 2024 17:58:21 +0200 Subject: [PATCH 3/3] import fix and other request --- lib/pages/home_page.dart | 2 +- lib/pages/onboarding_page/widgets/account_setup.dart | 3 +-- lib/pages/onboarding_page/widgets/add_budget.dart | 6 +++--- lib/pages/onboarding_page/widgets/budget_setup.dart | 5 ++--- lib/routes.dart | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 095a8bfc..3dda2ece 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -217,7 +217,7 @@ class _HomePageState extends ConsumerState with Functions { physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, itemBuilder: (context, i) { - if (i == accounts.length || accounts.isEmpty) { + if (i == accounts.length) { return Padding( padding: const EdgeInsets.fromLTRB(0, 4, 0, 16), child: Container( diff --git a/lib/pages/onboarding_page/widgets/account_setup.dart b/lib/pages/onboarding_page/widgets/account_setup.dart index 59b3820e..4c56b8fc 100644 --- a/lib/pages/onboarding_page/widgets/account_setup.dart +++ b/lib/pages/onboarding_page/widgets/account_setup.dart @@ -1,10 +1,9 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter/material.dart'; -import 'package:sossoldi/model/currency.dart'; -import 'package:sossoldi/providers/currency_provider.dart'; import '../../../constants/constants.dart'; import '../../../providers/accounts_provider.dart'; +import '../../../providers/currency_provider.dart'; import '/constants/style.dart'; final showAccountIconsProvider = StateProvider.autoDispose((ref) => false); diff --git a/lib/pages/onboarding_page/widgets/add_budget.dart b/lib/pages/onboarding_page/widgets/add_budget.dart index 0a924f78..baa05760 100644 --- a/lib/pages/onboarding_page/widgets/add_budget.dart +++ b/lib/pages/onboarding_page/widgets/add_budget.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:sossoldi/constants/functions.dart'; -import 'package:sossoldi/constants/style.dart'; -import 'package:sossoldi/providers/budgets_provider.dart'; +import '../../../constants/functions.dart'; +import '../../../constants/style.dart'; import '../../../model/budget.dart'; import '../../../model/category_transaction.dart'; +import '../../../providers/budgets_provider.dart'; class AddBudget extends ConsumerStatefulWidget { final CategoryTransaction category; diff --git a/lib/pages/onboarding_page/widgets/budget_setup.dart b/lib/pages/onboarding_page/widgets/budget_setup.dart index 40d67e2d..e6aa0ded 100644 --- a/lib/pages/onboarding_page/widgets/budget_setup.dart +++ b/lib/pages/onboarding_page/widgets/budget_setup.dart @@ -1,11 +1,8 @@ import 'dart:ui'; import 'package:collection/collection.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:sossoldi/pages/onboarding_page/widgets/add_category_button.dart'; -import 'package:sossoldi/pages/onboarding_page/widgets/category_button.dart'; import '/constants/constants.dart'; import '/constants/style.dart'; @@ -14,6 +11,8 @@ import '/pages/onboarding_page/widgets/account_setup.dart'; import '/pages/onboarding_page/widgets/add_budget.dart'; import '/providers/budgets_provider.dart'; import '/providers/categories_provider.dart'; +import 'add_category_button.dart'; +import 'category_button.dart'; class BudgetSetup extends ConsumerStatefulWidget { const BudgetSetup({Key? key}) : super(key: key); diff --git a/lib/routes.dart b/lib/routes.dart index 1823f3d7..4be7da9e 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -1,6 +1,5 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:sossoldi/pages/onboarding_page/onboarding_page.dart'; import 'pages/account_page/account_page.dart'; import 'pages/accounts/account_list.dart'; @@ -14,6 +13,7 @@ import 'pages/more_info_page/collaborators_page.dart'; import 'pages/more_info_page/more_info.dart'; import 'pages/more_info_page/privacy_policy.dart'; import 'pages/notifications/notifications_settings.dart'; +import 'pages/onboarding_page/onboarding_page.dart'; import 'pages/planning_page/planning_page.dart'; import 'pages/search_page/search_page.dart'; import 'pages/settings_page.dart';