diff --git a/android/app/build.gradle b/android/app/build.gradle index d8b02cd..724a882 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,5 +1,8 @@ plugins { id "com.android.application" + // START: FlutterFire Configuration + id 'com.google.gms.google-services' + // END: FlutterFire Configuration id "kotlin-android" // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id "dev.flutter.flutter-gradle-plugin" @@ -8,15 +11,19 @@ plugins { android { namespace = "com.example.fiscus" compileSdk = flutter.compileSdkVersion - ndkVersion = flutter.ndkVersion + //ndkVersion = flutter.ndkVersion + ndkVersion "25.1.8937393" compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + //sourceCompatibility = JavaVersion.VERSION_1_8 + //targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 + //jvmTarget = JavaVersion.VERSION_1_8 + jvmTarget = 17 } defaultConfig { @@ -24,7 +31,8 @@ android { applicationId = "com.example.fiscus" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. - minSdk = flutter.minSdkVersion + //minSdk = flutter.minSdkVersion + minSdkVersion 23 targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..68c93d5 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "1054264689837", + "project_id": "fiscus-fa2ab", + "storage_bucket": "fiscus-fa2ab.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:1054264689837:android:f6ac64bc93a8f266503224", + "android_client_info": { + "package_name": "com.example.fiscus" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyCPegnFSkjByLr3Dd7ww1XGssveLeV5wMs" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 7bb2df6..f5f1a53 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip +#distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index b9e43bd..8e8a18b 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -18,8 +18,13 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.1.0" apply false - id "org.jetbrains.kotlin.android" version "1.8.22" apply false + //id "com.android.application" version "8.1.0" apply false + // START: FlutterFire Configuration + id "com.google.gms.google-services" version "4.3.15" apply false + // END: FlutterFire Configuration + //id "org.jetbrains.kotlin.android" version "1.8.22" apply false + id "com.android.application" version "8.3.2" apply false + id "org.jetbrains.kotlin.android" version "2.0.20" apply false } include ":app" diff --git a/firebase.json b/firebase.json new file mode 100644 index 0000000..0fa10f5 --- /dev/null +++ b/firebase.json @@ -0,0 +1 @@ +{"flutter":{"platforms":{"android":{"default":{"projectId":"fiscus-fa2ab","appId":"1:1054264689837:android:f6ac64bc93a8f266503224","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"fiscus-fa2ab","configurations":{"android":"1:1054264689837:android:f6ac64bc93a8f266503224","ios":"1:1054264689837:ios:336b2dceab0181ae503224","macos":"1:1054264689837:ios:336b2dceab0181ae503224","web":"1:1054264689837:web:7dd99b49fab8870c503224","windows":"1:1054264689837:web:5571e664c9dc6e01503224"}}}}}} \ No newline at end of file diff --git a/lib/assets/Login-Setup.png b/lib/assets/Login-Setup.png new file mode 100644 index 0000000..ad89e46 Binary files /dev/null and b/lib/assets/Login-Setup.png differ diff --git a/lib/assets/background_for_Fictus.png b/lib/assets/background_for_Fictus.png new file mode 100644 index 0000000..146957e Binary files /dev/null and b/lib/assets/background_for_Fictus.png differ diff --git a/lib/componets/Account_Card.dart b/lib/componets/Account_Card.dart new file mode 100644 index 0000000..2eafb39 --- /dev/null +++ b/lib/componets/Account_Card.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; + +class AccountCard extends StatelessWidget { + final String title; + final double amount; + final VoidCallback? onAddMoney; + + AccountCard({ + required this.title, + required this.amount, + this.onAddMoney, + }); + + @override + Widget build(BuildContext context) { + return Card( + margin: EdgeInsets.symmetric(vertical: 8.0), + color: Colors.white.withOpacity(0.9), // Slightly transparent + child: Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle(fontSize: 18, color: Colors.grey.shade700), + ), + SizedBox(height: 8), + Text( + '\$${amount.toStringAsFixed(2)}', + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, // Prevent overflow issues + ), + ], + ), + ), + ), + if (onAddMoney != null) // Only show button if onAddMoney is provided + IconButton( + icon: Icon( + Icons.add, + color: Colors.black, // Color of the icon + ), + onPressed: onAddMoney, // Callback when pressed + ) + else + IconButton( + icon: Icon( + Icons.add, + color: Colors.grey, // Grey out the icon + ), + onPressed: null, // Disable the button + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/componets/BudgetItemCard.dart b/lib/componets/BudgetItemCard.dart new file mode 100644 index 0000000..8d53754 --- /dev/null +++ b/lib/componets/BudgetItemCard.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class BudgetItemCard extends StatelessWidget { + final String label; + final String percentage; + final Color color; + + BudgetItemCard( + {required this.label, required this.percentage, required this.color}); + + @override + Widget build(BuildContext context) { + return Card( + color: color.withOpacity(0.9), + margin: EdgeInsets.symmetric(vertical: 8.0), + child: ListTile( + title: Text( + percentage, + style: TextStyle(color: Colors.white, fontSize: 18), + ), + subtitle: Text( + label, + style: TextStyle(color: Colors.white, fontSize: 14), + ), + trailing: Icon(Icons.more_vert, color: Colors.white), + ), + ); + } +} diff --git a/lib/componets/Stock_Item.dart b/lib/componets/Stock_Item.dart new file mode 100644 index 0000000..6e7d0f2 --- /dev/null +++ b/lib/componets/Stock_Item.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; + +class StockItem extends StatelessWidget { + final String name; + final String symbol; + final String price; + final String change; + final bool isPositive; + + StockItem({ + required this.name, + required this.symbol, + required this.price, + required this.change, + required this.isPositive, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + name, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ), + Text( + symbol, + style: TextStyle(color: Colors.grey.shade500, fontSize: 12), + ), + ], + ), + // Small placeholder for the line chart + Container( + width: 50, + height: 20, + color: Colors.green.shade300, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + price, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16), + ), + Container( + decoration: BoxDecoration( + color: isPositive ? Colors.green : Colors.red, + borderRadius: BorderRadius.circular(4), + ), + padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2), + child: Text( + change, + style: TextStyle(color: Colors.white, fontSize: 12), + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/componets/setting_tiles.dart b/lib/componets/setting_tiles.dart new file mode 100644 index 0000000..d515c9c --- /dev/null +++ b/lib/componets/setting_tiles.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +class SettingsTile extends StatelessWidget { + final IconData icon; + final String title; + final String? subtitle; + final Widget? trailing; + final Color? titleColor; + final Function? onTap; + + SettingsTile({ + required this.icon, + required this.title, + this.subtitle, + this.trailing, + this.titleColor, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return ListTile( + leading: Icon(icon, color: Colors.blueGrey), + title: Text( + title, + style: TextStyle(color: titleColor ?? Colors.black), + ), + subtitle: subtitle != null ? Text(subtitle!) : null, + trailing: trailing, + onTap: onTap != null ? () => onTap!() : null, + ); + } +} diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart new file mode 100644 index 0000000..e636810 --- /dev/null +++ b/lib/firebase_options.dart @@ -0,0 +1,86 @@ +// File generated by FlutterFire CLI. +// ignore_for_file: type=lint +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + return web; + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.macOS: + return macos; + case TargetPlatform.windows: + return windows; + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions web = FirebaseOptions( + apiKey: 'AIzaSyDaO0-5DaCwK4U9pEIp2F7E9h6PLK1r_NU', + appId: '1:1054264689837:web:7dd99b49fab8870c503224', + messagingSenderId: '1054264689837', + projectId: 'fiscus-fa2ab', + authDomain: 'fiscus-fa2ab.firebaseapp.com', + storageBucket: 'fiscus-fa2ab.firebasestorage.app', + ); + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'AIzaSyCPegnFSkjByLr3Dd7ww1XGssveLeV5wMs', + appId: '1:1054264689837:android:f6ac64bc93a8f266503224', + messagingSenderId: '1054264689837', + projectId: 'fiscus-fa2ab', + storageBucket: 'fiscus-fa2ab.firebasestorage.app', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'AIzaSyBRHW3G_1atFY-toQA3tODyrOfzTZ4xjkM', + appId: '1:1054264689837:ios:336b2dceab0181ae503224', + messagingSenderId: '1054264689837', + projectId: 'fiscus-fa2ab', + storageBucket: 'fiscus-fa2ab.firebasestorage.app', + iosBundleId: 'com.example.fiscus', + ); + + static const FirebaseOptions macos = FirebaseOptions( + apiKey: 'AIzaSyBRHW3G_1atFY-toQA3tODyrOfzTZ4xjkM', + appId: '1:1054264689837:ios:336b2dceab0181ae503224', + messagingSenderId: '1054264689837', + projectId: 'fiscus-fa2ab', + storageBucket: 'fiscus-fa2ab.firebasestorage.app', + iosBundleId: 'com.example.fiscus', + ); + + static const FirebaseOptions windows = FirebaseOptions( + apiKey: 'AIzaSyDaO0-5DaCwK4U9pEIp2F7E9h6PLK1r_NU', + appId: '1:1054264689837:web:5571e664c9dc6e01503224', + messagingSenderId: '1054264689837', + projectId: 'fiscus-fa2ab', + authDomain: 'fiscus-fa2ab.firebaseapp.com', + storageBucket: 'fiscus-fa2ab.firebasestorage.app', + ); +} diff --git a/lib/main.dart b/lib/main.dart index fc25988..61b09e3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,12 +1,18 @@ +import 'package:fiscus/screens/home_page.dart'; +import 'package:fiscus/screens/home_screen.dart'; +import 'package:fiscus/screens/login_page.dart'; import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_auth/firebase_auth.dart'; -import 'screens/auth_screen.dart'; -import 'screens/home_screen.dart'; +import 'screens/signup_page.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); + + // FOR TESTING: Force sign out any currently logged-in user + await FirebaseAuth.instance.signOut(); + runApp(const MainApp()); } @@ -20,7 +26,48 @@ class MainApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, brightness: Brightness.light, + scaffoldBackgroundColor: Colors.white, + visualDensity: VisualDensity.adaptivePlatformDensity, ), + home: const AuthWrapper(), + routes: { + '/signup': (context) => SignUpPage(), + }, + ); + } +} + +class AuthWrapper extends StatelessWidget { + const AuthWrapper({super.key}); + + @override + Widget build(BuildContext context) { + return StreamBuilder( + stream: FirebaseAuth.instance.authStateChanges(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Scaffold( + body: Center( + child: CircularProgressIndicator( + color: Colors.blue, + ), + ), + ); + } + + // If the user is logged in, show HomePage + if (snapshot.hasData && snapshot.data != null) { + print('User is logged in: ${snapshot.data!.email}'); + return HomeScreen(); + } + // Otherwise, show LoginPage + else { + print('User is not logged in'); + return LoginPage(); + } + }, + +/* home: StreamBuilder( stream: FirebaseAuth.instance.authStateChanges(), builder: (context, snapshot) { @@ -33,6 +80,7 @@ class MainApp extends StatelessWidget { return const AuthScreen(); }, ), +*/ ); } } diff --git a/lib/screens/auth_screen.dart b/lib/screens/auth_screen.dart deleted file mode 100644 index 4cb957e..0000000 --- a/lib/screens/auth_screen.dart +++ /dev/null @@ -1,33 +0,0 @@ - -import 'package:flutter/material.dart'; - -class AuthScreen extends StatefulWidget { - const AuthScreen({super.key}); - - @override - State createState() => _AuthScreenState(); -} - -class _AuthScreenState extends State { - bool isLogin = true; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(isLogin ? 'Login' : 'Sign Up'), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () => setState(() => isLogin = !isLogin), - child: Text(isLogin ? 'Need an account? Sign up' : 'Have an account? Login'), - ), - ], - ), - ), - ); - } -} \ No newline at end of file diff --git a/lib/screens/budget_page.dart b/lib/screens/budget_page.dart new file mode 100644 index 0000000..a1427c4 --- /dev/null +++ b/lib/screens/budget_page.dart @@ -0,0 +1,562 @@ +import 'package:flutter/material.dart'; +import 'package:fl_chart/fl_chart.dart'; + +double balance = 14000; +double currAmount = 14000; +class BudgetPage extends StatefulWidget { + @override + _BudgetPageState createState() => _BudgetPageState(); +} + +class _BudgetPageState extends State { + // List to store budget items + List budgetItems = [ + BudgetItem(label: 'Taxes', percentage: 65, color: Colors.blue.shade800, amount: 9100), + BudgetItem(label: 'Food', percentage: 20, color: Colors.lightBlue.shade300, amount: 2800), + BudgetItem(label: 'Entertainment', percentage: 15, color: Colors.red, amount: 2100), + ]; + + Future setCurrentAmount(BuildContext context) async { + final TextEditingController budgetController = TextEditingController(); + + final result = await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text('Custom Budget'), + content: TextField( + controller: budgetController, + decoration: InputDecoration(labelText: 'Amount'), + keyboardType: TextInputType.number, + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(null); // Return null if canceled + }, + child: Text('Cancel'), + ), + ElevatedButton( + onPressed: () { + final budget = double.tryParse(budgetController.text) ?? 0; + Navigator.of(context).pop(budget); // Return the entered value + }, + child: Text('Confirm'), + ), + ], + ); + }, + ); + + // Ensure a non-null value is always returned + return result ?? 0.0; +} + + int selectedPeriod = 0; // 0: Year, 1: Week, 2: Month, 3: Day + +void update() { + setState(() { + currAmount = currAmount + 1; + currAmount = currAmount - 1; + }); +} + + // Function to update the amount for all budget items +Future updateAmountsForAll(int periodic) async { + if (periodic == 0) { + setState(() { + currAmount = balance; + for (var item in budgetItems) { + item.amount = currAmount * (item.percentage / 100); + } + }); + } else if (periodic == 1) { + setState(() { + currAmount = balance / 12; + for (var item in budgetItems) { + item.amount = currAmount * (item.percentage / 100); + } + }); + } else if (periodic == 2) { + setState(() { + currAmount = balance / 52; + for (var item in budgetItems) { + item.amount = currAmount * (item.percentage / 100); + } + }); + } else if (periodic == 3) { + setState(() { + currAmount = balance / 365; + for (var item in budgetItems) { + item.amount = currAmount * (item.percentage / 100); + } + }); + } else if (periodic == 4) { + // Custom budget logic + final budget = await setCurrentAmount(context); // Wait for dialog result + if (budget > 0) { + setState(() { + currAmount = budget; + for (var item in budgetItems) { + item.amount = currAmount * (item.percentage / 100); + } + }); + } + } +} + + + // Method edit + void _showEditBudgetItemDialog(BuildContext context, int index) { + final TextEditingController labelController = + TextEditingController(text: budgetItems[index].label); + final TextEditingController percentageController = + TextEditingController(text: budgetItems[index].percentage.toString()); + + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text('Edit Budget Item'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: labelController, + decoration: InputDecoration(labelText: 'Label'), + ), + TextField( + controller: percentageController, + decoration: InputDecoration(labelText: 'Percentage'), + keyboardType: TextInputType.number, + ), + ], + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text('Cancel'), + ), + ElevatedButton( + onPressed: () { + final label = labelController.text; + final percentage = double.tryParse(percentageController.text) ?? 0; + if (label.isNotEmpty && percentage > 0) { + setState(() { + budgetItems[index] = BudgetItem( + label: label, + percentage: percentage, + color: budgetItems[index].color, + amount: currAmount * (percentage/100), + ); + }); + //update(); + checkTotalPercentage(); + } + Navigator.of(context).pop(); + }, + child: Text('Save'), + ), + ], + ); + }, + ); +} + + // Method to calculate the total percentage + double getTotalPercentage() { + return budgetItems.fold(0, (sum, item) => sum + item.percentage); + } + + // Method to calculate the sections of the pie chart + List getPieChartSections() { + double totalPercentage = getTotalPercentage(); + + // If the total is 0, return a faint outline of the pie chart + if (totalPercentage == 0) { + return [ + PieChartSectionData( + color: Colors.grey.shade300, + value: 1, + radius: 70, + showTitle: false, + ), + ]; + } + + // Create sections for each budget item + List sections = budgetItems.map((item) { + double adjustedPercentage = (item.percentage / 100) * 100; + return PieChartSectionData( + color: item.color, + value: adjustedPercentage, + title: '${adjustedPercentage.toStringAsFixed(1)}%', + radius: 70, + titleStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ); + }).toList(); + + // Add a greyed-out section for the remaining percentage + double remainingPercentage = 100 - totalPercentage; + if (remainingPercentage > 0) { + sections.add( + PieChartSectionData( + color: Colors.grey.shade300, + value: remainingPercentage, + title: '${remainingPercentage.toStringAsFixed(1)}%', + radius: 60, + titleStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.grey.shade700, + ), + ), + ); + } + + return sections; + } + + // Method to add a new budget item + void addBudgetItem(String label, double percentage, Color color) { + setState(() { + budgetItems.add(BudgetItem(label: label, percentage: percentage, color: color, amount: currAmount*(percentage/100))); + }); + checkTotalPercentage(); + } + + // Method to remove a budget item + void removeBudgetItem(int index) { + setState(() { + budgetItems.removeAt(index); + }); + } + + // Warn if total percentage exceeds 100% + void checkTotalPercentage() { + double total = getTotalPercentage(); + if (total > 100) { + WidgetsBinding.instance.addPostFrameCallback((_) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Warning'), + content: Text('The total budget exceeds 100%. Adjust your values.'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text('OK'), + ), + ], + ), + ); + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + // Background Image + Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage("lib/assets/Login-Setup.png"), + fit: BoxFit.cover, + ), + ), + ), + // Overlay Content + Column( + children: [ + SizedBox(height: 50), // Adjust for spacing + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 5.0), + child: Row( + children: [ + CircleAvatar( + radius: 24, + backgroundColor: Colors.grey, + ), + SizedBox(width: 16), + Text( + 'Budget Report', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + Spacer(), + Icon(Icons.notifications, color: Colors.black), + SizedBox(width: 16), + Icon(Icons.more_vert, color: Colors.black), + ], + ), + ), + SizedBox(height: 30), + // Circular Progress (Budget Pie Chart) + SizedBox( + height: 210, + child: PieChart( + PieChartData( + sections: getPieChartSections(), + centerSpaceRadius: 40, + ), + ), + ), + SizedBox(height: 10), + // Dropdown menu for selecting time period (Day, Month, Week, Year) + // Row with Dropdown and Current Amount + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // Current Amount Display + Text( + 'Budget: \$${currAmount.toStringAsFixed(2)}', + style: TextStyle(color: const Color.fromARGB(255, 0, 0, 0), fontSize: 16), + ), + // Dropdown menu for selecting time period (Day, Month, Week, Year) + DropdownButton( + value: selectedPeriod, + + dropdownColor: Colors.white, + borderRadius: BorderRadius.circular(15), + items: [ + DropdownMenuItem( + value: 0, + child: Text('Year'), + ), + DropdownMenuItem( + value: 1, + child: Text('Month'), + ), + DropdownMenuItem( + value: 2, + child: Text('Week'), + ), + DropdownMenuItem( + value: 3, + child: Text('Day'), + ), + DropdownMenuItem( + value: 4, + child: Text('Custom Budget'), + ), + ], + onChanged: (value) { + setState(() { + selectedPeriod = value!; + updateAmountsForAll(selectedPeriod); + }); + }, + ), + ], + ), + ), + SizedBox(height: 10), + // Scrollable Budget Items + Expanded( + child: ShaderMask( + shaderCallback: (Rect bounds) { + return LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, // Top fade + Colors.black, // Fully visible content + Colors.black, // Fully visible content + Colors.transparent, // Bottom fade + ], + stops: [0.0, 0.02, 0.98, 1.0], // Control the fade range + ).createShader(bounds); + }, + blendMode: BlendMode.dstIn, // Blend mode for fading + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Column( + children: budgetItems + .asMap() + .entries + .map( + (entry) => Column( + children: [ + BudgetItemCard( + label: entry.value.label, + percentage: '${entry.value.percentage}%', + color: entry.value.color, + amount: entry.value.amount, + onDelete: () => removeBudgetItem(entry.key), + onEdit: () => _showEditBudgetItemDialog(context, entry.key), + ), + SizedBox(height: 5), + ], + ), + ) + .toList(), + ), + ), + ), + ), + ), + // Add Budget Item Button + Padding( + padding: const EdgeInsets.all(10.0), + child: ElevatedButton( + onPressed: () { + _showAddBudgetItemDialog(context); + }, + child: Text('Add Budget Item'), + ), + ), + ], + ), + ], + ), + ); + } + + // Dialog to add a new budget item + void _showAddBudgetItemDialog(BuildContext context) { + final TextEditingController labelController = TextEditingController(); + final TextEditingController percentageController = TextEditingController(); + + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text('Add Budget Item'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: labelController, + decoration: InputDecoration(labelText: 'Label'), + ), + TextField( + controller: percentageController, + decoration: InputDecoration(labelText: 'Percentage'), + keyboardType: TextInputType.number, + ), + ], + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text('Cancel'), + ), + ElevatedButton( + onPressed: () { + final label = labelController.text; + final percentage = double.tryParse(percentageController.text) ?? 0; + if (label.isNotEmpty && percentage > 0) { + addBudgetItem(label, percentage, Colors.primaries[budgetItems.length % Colors.primaries.length]); + } + Navigator.of(context).pop(); + }, + child: Text('Add'), + ), + ], + ); + }, + ); + } +} +// Helper to create a circle with shadow + Widget _buildCircleWithShadow(IconData icon, Color color) { + return Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + blurRadius: 1, + spreadRadius: 1, + offset: Offset(0, 3), + ), + ], + ), + child: Icon(icon, size: 12, color: color), + ); + } + +// Model for a budget item +class BudgetItem { + final String label; + final double percentage; + final Color color; + double amount; + + BudgetItem({required this.label, required this.percentage, required this.color, required this.amount}); +} + +// Widget for Budget Item Card +class BudgetItemCard extends StatelessWidget { + final String label; + final String percentage; + double amount; + final Color color; + final VoidCallback? onDelete; + final VoidCallback? onEdit; + + BudgetItemCard({ + required this.label, + required this.percentage, + required this.color, + this.onDelete, + this.onEdit, + required this.amount, + }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onEdit, // Trigger edit dialog when tapped + child: Card( + color: color.withOpacity(0.8), + child: ListTile( + title: Text( + label, + style: TextStyle(color: Colors.white), + ), + subtitle: Text( + percentage, + style: TextStyle(color: Colors.white70), + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + '\$${amount.toStringAsFixed(2)}', // Format the amount + style: TextStyle( + color: Colors.white, + fontSize: 18, // Larger font size + ), + ), + SizedBox(width: 8), // Spacing + IconButton( + icon: Icon(Icons.delete, color: Colors.white), + onPressed: onDelete, + ), + ], + ), + ), + ), + ); + } +} + + diff --git a/lib/screens/budget_screen.dart b/lib/screens/budget_screen.dart deleted file mode 100644 index 24ad0aa..0000000 --- a/lib/screens/budget_screen.dart +++ /dev/null @@ -1,11 +0,0 @@ - -import 'package:flutter/material.dart'; - -class BudgetScreen extends StatelessWidget { - const BudgetScreen({super.key}); - - @override - Widget build(BuildContext context) { - return const Center(child: Text('Budget')); - } -} \ No newline at end of file diff --git a/lib/screens/camera_page.dart b/lib/screens/camera_page.dart new file mode 100644 index 0000000..2d47335 --- /dev/null +++ b/lib/screens/camera_page.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:camera/camera.dart'; + +class CameraPage extends StatefulWidget { + @override + _CameraPageState createState() => _CameraPageState(); +} + +class _CameraPageState extends State { + late List cameras; + CameraController? controller; + bool isCameraInitialized = false; + + @override + void initState() { + super.initState(); + initializeCamera(); + } + + Future initializeCamera() async { + try { + // Retrieve available cameras + cameras = await availableCameras(); + // Initialize the first camera (back camera) + controller = CameraController( + cameras.first, + ResolutionPreset.high, + ); + await controller?.initialize(); + setState(() { + isCameraInitialized = true; + }); + } catch (e) { + print("Error initializing camera: $e"); + } + } + + @override + void dispose() { + controller?.dispose(); + super.dispose(); + } + + Future captureImage() async { + if (!controller!.value.isInitialized) { + return; + } + try { + final image = await controller?.takePicture(); + if (image != null) { + // Process the captured image (e.g., send it to an OCR library) + print('Image saved to: ${image.path}'); + // Add receipt-reading logic here + } + } catch (e) { + print("Error capturing image: $e"); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Camera Page'), + ), + body: isCameraInitialized + ? Stack( + children: [ + CameraPreview(controller!), + Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton( + onPressed: captureImage, + child: Text('Capture Receipt'), + ), + ), + ), + ], + ) + : Center(child: CircularProgressIndicator()), + ); + } +} diff --git a/lib/screens/charts_screen.dart b/lib/screens/charts_screen.dart deleted file mode 100644 index a4da46d..0000000 --- a/lib/screens/charts_screen.dart +++ /dev/null @@ -1,11 +0,0 @@ - -import 'package:flutter/material.dart'; - -class ChartsScreen extends StatelessWidget { - const ChartsScreen({super.key}); - - @override - Widget build(BuildContext context) { - return const Center(child: Text('Charts')); - } -} \ No newline at end of file diff --git a/lib/screens/dashboard_screen.dart b/lib/screens/dashboard_screen.dart deleted file mode 100644 index 7d02d38..0000000 --- a/lib/screens/dashboard_screen.dart +++ /dev/null @@ -1,11 +0,0 @@ - -import 'package:flutter/material.dart'; - -class DashboardScreen extends StatelessWidget { - const DashboardScreen({super.key}); - - @override - Widget build(BuildContext context) { - return const Center(child: Text('Dashboard')); - } -} \ No newline at end of file diff --git a/lib/screens/home_page.dart b/lib/screens/home_page.dart new file mode 100644 index 0000000..c1aefc7 --- /dev/null +++ b/lib/screens/home_page.dart @@ -0,0 +1,235 @@ +import 'package:flutter/material.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:fiscus/componets/Account_Card.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class HomePage extends StatefulWidget { + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State { + String _userName = 'Loading...'; + + @override + void initState() { + super.initState(); + _getUserName(); + } + + Future _getUserName() async { + try { + SharedPreferences prefs = await SharedPreferences.getInstance(); + setState(() { + _userName = prefs.getString('first_name') ?? 'User'; + }); + } catch (e) { + print('Error fetching user name: $e'); + } + } + + // Initialize account balances + double debitBalance = 14000.0; + double creditBalance = 0.0; + double savingsBalance = 0.0; + double walletBalance = 0.0; + double balance = 14000.0; + + void totalBalanceCal(BuildContext context){ + balance = debitBalance + walletBalance; + } + // Function to update account balances + void _showAddMoneyDialog(BuildContext context) { + final TextEditingController amountController = TextEditingController(); + + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text('Add Money to Wallet'), + content: TextField( + controller: amountController, + keyboardType: TextInputType.number, + decoration: InputDecoration(hintText: 'Enter amount'), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), // Close dialog + child: Text('Cancel'), + ), + TextButton( + onPressed: () { + final enteredAmount = double.tryParse(amountController.text); + if (enteredAmount != null) { + setState(() { + walletBalance += enteredAmount; // Update balance using setState + }); + totalBalanceCal(context); + } + Navigator.of(context).pop(); // Close dialog + }, + child: Text('Add'), + ), + ], + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage("lib/assets/Login-Setup.png"), + fit: BoxFit.cover, + ), + ), + ), + Column( + children: [ + SizedBox(height: 50), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + child: Row( + children: [ + CircleAvatar( + radius: 24, + backgroundColor: Colors.grey, + ), + SizedBox(width: 16), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Welcome,', + style: TextStyle(fontSize: 30, color: Colors.white), + ), + Text( + _userName, + style: TextStyle(fontSize: 30, color: Colors.white), + ), + ], + ), + ], + ), + ), + + Padding( + padding: const EdgeInsets.symmetric(horizontal: 30.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + // Shadow text + Text( + 'Total Balance: \$${balance.toStringAsFixed(2)}', + style: TextStyle( + fontSize: 23.2, + + color: Colors.black.withOpacity(0.5), // Shadow color + ), + ), + // Foreground text + Text( + 'Total Balance: \$${balance.toStringAsFixed(2)}', + style: TextStyle( + fontSize: 23, + + color: Colors.white, + ), + ), + ], + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 30.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Wells Fargo:', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.white), + ), + ], + ), + ), + // Scrollable Area with Edge Fading + Expanded( + child: ShaderMask( + shaderCallback: (Rect bounds) { + return LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, // Top fade + Colors.black, // Fully visible content + Colors.black, // Fully visible content + Colors.transparent, // Bottom fade + ], + stops: [0.0, 0.01, 0.99, 1.0], // Control the fade range + ).createShader(bounds); + }, + blendMode: BlendMode.dstIn, // Blend mode for fading + child: SingleChildScrollView( + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 30.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + AccountCard( + title: 'Debit', + amount: debitBalance, + ), + AccountCard( + title: 'Credit', + amount: creditBalance, + ), + AccountCard( + title: 'Savings', + amount: savingsBalance, + ), + AccountCard( + title: 'Wallet', + amount: walletBalance, + onAddMoney: () => _showAddMoneyDialog(context), + ), + ], + ), + ), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.circle, size: 20, color: Colors.grey), + SizedBox(width: 8), + Icon(Icons.circle, size: 16, color: Colors.grey.shade300), + SizedBox(width: 8), + Icon(Icons.circle, size: 16, color: Colors.grey.shade300), + ], + ), + ), + ], + ), + ], + ), + ); + } +} + diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 3021413..b488b43 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1,9 +1,12 @@ +import 'package:fiscus/screens/report_page.dart'; import 'package:flutter/material.dart'; -import 'dashboard_screen.dart'; -import 'budget_screen.dart'; -import 'charts_screen.dart'; -import 'profile_screen.dart'; +import 'home_page.dart'; +import 'camera_page.dart'; +import 'setting_page.dart'; +import 'stocks_page.dart'; +import 'budget_page.dart'; +import 'spending_page.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @@ -15,26 +18,56 @@ class HomeScreen extends StatefulWidget { class _HomeScreenState extends State { int _selectedIndex = 0; - final List _screens = [ - const DashboardScreen(), - const BudgetScreen(), - const ChartsScreen(), - const ProfileScreen(), + // List of pages to navigate to + final List _pages = [ + HomePage(), // Home page + ReportPage(), // Reports page + CameraPage(), // Camera page + StocksPage(), // Stocks page + SettingsPage(), // Settings page ]; + // Function to handle navigation item selection + void _onItemTapped(int index) { + setState(() { + _selectedIndex = index; + }); + } + @override Widget build(BuildContext context) { return Scaffold( - body: _screens[_selectedIndex], + body: IndexedStack( + index: _selectedIndex, + children: _pages, + ), bottomNavigationBar: BottomNavigationBar( currentIndex: _selectedIndex, - onTap: (index) => setState(() => _selectedIndex = index), - items: const [ - BottomNavigationBarItem(icon: Icon(Icons.dashboard), label: 'Dashboard'), - BottomNavigationBarItem(icon: Icon(Icons.account_balance_wallet), label: 'Budget'), - BottomNavigationBarItem(icon: Icon(Icons.pie_chart), label: 'Analytics'), - BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'), + onTap: _onItemTapped, + items: [ + BottomNavigationBarItem( + icon: Icon(Icons.home), + label: 'Accounts', + ), + BottomNavigationBarItem( + icon: Icon(Icons.bar_chart), + label: 'Reports', + ), + BottomNavigationBarItem( + icon: Icon(Icons.camera_alt), + label: 'Camera', + ), + BottomNavigationBarItem( + icon: Icon(Icons.attach_money), + label: 'Stocks', + ), + BottomNavigationBarItem( + icon: Icon(Icons.settings), + label: 'Settings', + ), ], + selectedItemColor: Colors.blue, // Selected icon color + unselectedItemColor: Colors.grey, // Unselected icon color ), ); } diff --git a/lib/screens/login_page.dart b/lib/screens/login_page.dart new file mode 100644 index 0000000..c331357 --- /dev/null +++ b/lib/screens/login_page.dart @@ -0,0 +1,243 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/material.dart'; + +class LoginPage extends StatefulWidget { + @override + _LoginPageState createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); + bool _isLoading = false; + + Future _loginUser() async { + if (_emailController.text.isEmpty || _passwordController.text.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Please fill in both email and password.')), + ); + return; + } + + setState(() { + _isLoading = true; + }); + + try { + UserCredential userCredential = + await FirebaseAuth.instance.signInWithEmailAndPassword( + email: _emailController.text.trim(), + password: _passwordController.text.trim(), + ); + if (mounted) { + Navigator.pushReplacementNamed(context, '/home'); + } + } on FirebaseAuthException catch (e) { + String errorMessage = 'An unknown error occurred'; + if (e.code == 'user-not-found') { + errorMessage = 'No user found with that email.'; + } else if (e.code == 'wrong-password') { + errorMessage = 'Incorrect password.'; + } else if (e.code == 'network-request-failed') { + errorMessage = 'No internet connection.'; + } else if (e.code == 'too-many-requests') { + errorMessage = 'Too many requests. Please try again later.'; + } else if (e.code == 'invalid-email') { + errorMessage = 'Invalid email address.'; + } + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(errorMessage)), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Error: ${e.toString()}')), + ); + } + } finally { + if (mounted) { + setState(() { + _isLoading = false; + }); + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + // Background Image + Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + 'lib/assets/Login-Setup.png'), // Background image path + fit: BoxFit.cover, + ), + ), + ), + Container( + decoration: BoxDecoration( + color: const Color.fromARGB(255, 0, 0, 0) + .withOpacity(0.1), // Darken the background slightly + ), + ), + Center( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 30.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Welcome Text + Text( + 'Welcome Back!', + style: TextStyle( + color: Colors.white, + fontSize: 32, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 10), + Text( + 'Please login to your account', + style: TextStyle( + color: Colors.white.withOpacity(0.9), + fontSize: 16, + ), + ), + SizedBox(height: 30), + + // Email Input + TextField( + controller: _emailController, + style: TextStyle(color: Colors.white), + decoration: InputDecoration( + filled: true, + fillColor: Colors.white.withOpacity(0.2), + hintText: 'Email', + hintStyle: TextStyle(color: Colors.white70), + contentPadding: + EdgeInsets.symmetric(vertical: 18, horizontal: 20), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(30.0), + borderSide: BorderSide.none, + ), + ), + ), + SizedBox(height: 20), + + // Password Input + TextField( + controller: _passwordController, + obscureText: true, + style: TextStyle(color: Colors.white), + decoration: InputDecoration( + filled: true, + fillColor: Colors.white.withOpacity(0.2), + hintText: 'Password', + hintStyle: TextStyle(color: Colors.white70), + contentPadding: + EdgeInsets.symmetric(vertical: 18, horizontal: 20), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(30.0), + borderSide: BorderSide.none, + ), + ), + ), + + SizedBox(height: 10), + + // Forgot Password Button + Align( + alignment: Alignment.centerRight, + child: TextButton( + onPressed: () { + // Handle forgot password logic + }, + child: Text( + 'Forgot Password?', + style: TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + ), + ), + + SizedBox(height: 30), + + // Login Button + _isLoading + ? CircularProgressIndicator(color: Colors.white) + : ElevatedButton( + onPressed: _loginUser, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blueAccent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30.0), + ), + padding: EdgeInsets.symmetric(vertical: 20), + textStyle: TextStyle(fontSize: 18), + ), + child: Text('Login', + style: TextStyle(color: Colors.white)), + ), + + SizedBox(height: 20), + + // OR Divider + Row( + children: [ + Expanded( + child: Divider( + color: Colors.white.withOpacity(0.5), + thickness: 1, + ), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 10), + child: Text( + 'OR', + style: TextStyle(color: Colors.white70), + ), + ), + Expanded( + child: Divider( + color: Colors.white.withOpacity(0.5), + thickness: 1, + ), + ), + ], + ), + + SizedBox(height: 20), + + // Sign Up Option + TextButton( + onPressed: () { + Navigator.pushNamed(context, '/signup'); + }, + child: Text( + "Don't have an account? Sign up now", + style: TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/profile_screen.dart b/lib/screens/profile_screen.dart deleted file mode 100644 index 3da8dcd..0000000 --- a/lib/screens/profile_screen.dart +++ /dev/null @@ -1,11 +0,0 @@ - -import 'package:flutter/material.dart'; - -class ProfileScreen extends StatelessWidget { - const ProfileScreen({super.key}); - - @override - Widget build(BuildContext context) { - return const Center(child: Text('Profile')); - } -} \ No newline at end of file diff --git a/lib/screens/report_page.dart b/lib/screens/report_page.dart new file mode 100644 index 0000000..30e85fd --- /dev/null +++ b/lib/screens/report_page.dart @@ -0,0 +1,20 @@ + +import 'package:fiscus/screens/budget_page.dart'; +import 'package:fiscus/screens/spending_page.dart'; +import 'package:flutter/material.dart'; + +class ReportPage extends StatelessWidget { + const ReportPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: PageView( + children: [ + BudgetPage(), + SpendingReportPage(), + ], + ) + ); + } +} \ No newline at end of file diff --git a/lib/screens/setting_page.dart b/lib/screens/setting_page.dart new file mode 100644 index 0000000..427a261 --- /dev/null +++ b/lib/screens/setting_page.dart @@ -0,0 +1,151 @@ +// ignore_for_file: prefer_const_constructors, prefer_const_literals_to_create_immutables + +import 'package:fiscus/componets/setting_tiles.dart'; +import 'package:flutter/material.dart'; + +class SettingsPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Settings"), + backgroundColor: Colors.blueGrey, + ), + body: ListView( + children: [ + // Account Settings Section + SettingsSection( + title: "Account", + tiles: [ + SettingsTile( + icon: Icons.person, + title: "Profile", + subtitle: "Edit your profile details", + onTap: () { + // Handle profile tap + }, + ), + SettingsTile( + icon: Icons.lock, + title: "Password", + subtitle: "Change your password", + onTap: () { + // Handle password tap + }, + ), + SettingsTile( + icon: Icons.security, + title: "Privacy", + subtitle: "Manage your privacy settings", + onTap: () { + // Handle privacy tap + }, + ), + ], + ), + // Notifications Section + SettingsSection( + title: "Notifications", + tiles: [ + SettingsTile( + icon: Icons.notifications, + title: "Push Notifications", + trailing: Switch(value: true, onChanged: (val) {}), + ), + SettingsTile( + icon: Icons.email, + title: "Email Notifications", + trailing: Switch(value: false, onChanged: (val) {}), + ), + SettingsTile( + icon: Icons.sms, + title: "SMS Notifications", + trailing: Switch(value: false, onChanged: (val) {}), + ), + ], + ), + // General Settings Section + SettingsSection( + title: "General", + tiles: [ + SettingsTile( + icon: Icons.language, + title: "Language", + subtitle: "English", + onTap: () { + // Handle language tap + }, + ), + SettingsTile( + icon: Icons.palette, + title: "Theme", + subtitle: "Light", + onTap: () { + // Handle theme tap + }, + ), + ], + ), + // About Section + SettingsSection( + title: "About", + tiles: [ + SettingsTile( + icon: Icons.info, + title: "About Us", + onTap: () { + // Handle About Us tap + }, + ), + SettingsTile( + icon: Icons.help, + title: "Help", + onTap: () { + // Handle Help tap + }, + ), + SettingsTile( + icon: Icons.logout, + title: "Logout", + titleColor: Colors.red, + onTap: () { + // Handle logout + }, + ), + ], + ), + ], + ), + ); + } +} + +class SettingsSection extends StatelessWidget { + final String title; + final List tiles; + + SettingsSection({required this.title, required this.tiles}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.grey.shade700, + ), + ), + SizedBox(height: 8), + Column(children: tiles), + Divider(thickness: 1), + ], + ), + ); + } +} diff --git a/lib/screens/signup_page.dart b/lib/screens/signup_page.dart new file mode 100644 index 0000000..df7ed2f --- /dev/null +++ b/lib/screens/signup_page.dart @@ -0,0 +1,127 @@ +import 'package:flutter/material.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class SignUpPage extends StatefulWidget { + @override + _SignUpPage createState() => _SignUpPage(); +} + +class _SignUpPage extends State { + final _firstNameController = TextEditingController(); + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); + final _confirmPasswordController = TextEditingController(); + final _formKey = GlobalKey(); + + bool _isLoading = false; + + Future _createAccount() async { + if (_formKey.currentState!.validate()) { + setState(() { + _isLoading = true; + }); + + try { + UserCredential userCredential = + await FirebaseAuth.instance.createUserWithEmailAndPassword( + email: _emailController.text.trim(), + password: _passwordController.text.trim(), + ); + + // Store user info in SharedPreferences + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString('first_name', _firstNameController.text.trim()); + await prefs.setString('email', _emailController.text.trim()); + + // Show success message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Account created successfully!')), + ); + + // Navigate back to login screen or home screen + Navigator.of(context).pop(); // Use pop() to return to login page + } catch (e) { + // Show error message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Failed to create account: $e')), + ); + } finally { + setState(() { + _isLoading = false; + }); + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Sign Up'), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Form( + key: _formKey, + child: Column( + children: [ + TextFormField( + controller: _firstNameController, + decoration: InputDecoration(labelText: 'First Name'), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your first name'; + } + return null; + }, + ), + TextFormField( + controller: _emailController, + decoration: InputDecoration(labelText: 'Email'), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your email'; + } + return null; + }, + ), + TextFormField( + controller: _passwordController, + decoration: InputDecoration(labelText: 'Password'), + obscureText: true, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter your password'; + } + return null; + }, + ), + TextFormField( + controller: _confirmPasswordController, + decoration: InputDecoration(labelText: 'Confirm Password'), + obscureText: true, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please confirm your password'; + } + if (value != _passwordController.text) { + return 'Passwords do not match'; + } + return null; + }, + ), + SizedBox(height: 20), + _isLoading + ? CircularProgressIndicator() + : ElevatedButton( + onPressed: _createAccount, + child: Text('Create Account'), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/screens/spending_page.dart b/lib/screens/spending_page.dart new file mode 100644 index 0000000..53e1145 --- /dev/null +++ b/lib/screens/spending_page.dart @@ -0,0 +1,214 @@ +// ignore_for_file: prefer_const_constructors, prefer_const_literals_to_create_immutables + +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; + +class SpendingReportPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + // Background Image + Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage("lib/assets/Login-Setup.png"), + fit: BoxFit.cover, + ), + ), + ), + // Overlay Content + Column( + children: [ + SizedBox(height: 50), // Adjust for spacing + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + child: Row( + children: [ + CircleAvatar( + radius: 24, + backgroundColor: Colors.grey, + ), + SizedBox(width: 16), + Text( + 'Spending Report', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + Spacer(), + Icon(Icons.notifications, color: Colors.white), + SizedBox(width: 16), + Icon(Icons.more_vert, color: Colors.white), + ], + ), + ), + SizedBox(height: 30), + // Bar Chart Container + Center( + child: Container( + height: 350, + width: 350, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.8), + borderRadius: BorderRadius.circular(16), + ), + padding: EdgeInsets.all(16), + child: BarChart( + BarChartData( + maxY: 400, + gridData: FlGridData( + show: true, + drawVerticalLine: false, + drawHorizontalLine: true, + ), + borderData: FlBorderData(show: false), + titlesData: FlTitlesData( + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + interval: 100, + reservedSize: 40, + getTitlesWidget: (value, meta) { + return Text( + '\$${value.toInt()}', + style: TextStyle(color: Colors.black), + ); + }, + ), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (value, meta) { + const days = [ + 'M', + 'T', + 'W', + 'Th', + 'F', + 'Sa', + 'Su' + ]; + return Text( + days[value.toInt() % days.length], + style: TextStyle(color: Colors.black), + ); + }, + ), + ), + ), + barGroups: [ + BarChartGroupData( + x: 0, + barRods: [ + BarChartRodData(toY: 250, color: Colors.lightBlue) + ], + ), + BarChartGroupData( + x: 1, + barRods: [ + BarChartRodData(toY: 200, color: Colors.lightBlue) + ], + ), + BarChartGroupData( + x: 2, + barRods: [ + BarChartRodData(toY: 280, color: Colors.lightBlue) + ], + ), + BarChartGroupData( + x: 3, + barRods: [ + BarChartRodData(toY: 220, color: Colors.lightBlue) + ], + ), + BarChartGroupData( + x: 4, + barRods: [ + BarChartRodData(toY: 300, color: Colors.lightBlue) + ], + ), + BarChartGroupData( + x: 5, + barRods: [ + BarChartRodData(toY: 180, color: Colors.lightBlue) + ], + ), + BarChartGroupData( + x: 6, + barRods: [ + BarChartRodData(toY: 350, color: Colors.lightBlue) + ], + ), + ], + ), + ), + ), + ), + SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildCircleWithShadow(Icons.circle, Colors.grey.shade300), + SizedBox(width: 8), + _buildCircleWithShadow(Icons.circle, Colors.grey), + SizedBox(width: 8), + _buildCircleWithShadow(Icons.circle, Colors.grey.shade300), + ], + ), + SizedBox(height: 20), + // Scrollable Content + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Container( + decoration: BoxDecoration( + color: Colors.grey.shade300.withOpacity(0.8), + borderRadius: BorderRadius.circular(16), + ), + child: Column( + children: [ + SizedBox(height: 20), + Text( + 'Additional content goes here', + style: TextStyle(fontSize: 16), + ), + SizedBox(height: 300), // Example content height + ], + ), + ), + ), + ), + ), + ], + ), + ], + ), + + ); + } + // Helper to create a circle with shadow + Widget _buildCircleWithShadow(IconData icon, Color color) { + return Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + blurRadius: 1, + spreadRadius: 1, + offset: Offset(0, 3), + ), + ], + ), + child: Icon(icon, size: 12, color: color), + ); + } +} + diff --git a/lib/screens/stocks_page.dart b/lib/screens/stocks_page.dart new file mode 100644 index 0000000..f98d487 --- /dev/null +++ b/lib/screens/stocks_page.dart @@ -0,0 +1,114 @@ +// ignore_for_file: prefer_const_constructors, prefer_const_literals_to_create_immutables + +import 'package:fiscus/componets/Stock_Item.dart'; +import 'package:flutter/material.dart'; + +class StocksPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + // Background Image + Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage("lib/assets/Login-Setup.png"), + fit: BoxFit.cover, + ), + ), + ), + // Overlay Content + Column( + children: [ + SizedBox(height: 50), // Adjust for spacing + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + child: Row( + children: [ + CircleAvatar( + radius: 24, + backgroundColor: Colors.grey, + ), + SizedBox(width: 16), + Text( + 'Stocks', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + Spacer(), + Icon(Icons.notifications, color: Colors.white), + SizedBox(width: 16), + Icon(Icons.more_vert, color: Colors.white), + ], + ), + ), + SizedBox(height: 30), + // Stocks List Container + Center( + child: Container( + width: 350, + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.8), + borderRadius: BorderRadius.circular(16), + ), + child: ListView( + shrinkWrap: true, + children: [ + StockItem( + name: "Dow Jones", + symbol: "Dow Jones Industrial Average", + price: "40,696.21", + change: "+1.91%", + isPositive: true, + ), + StockItem( + name: "S&P 500", + symbol: "Standard & Poor's 500", + price: "5,480.03", + change: "+1.50%", + isPositive: true, + ), + StockItem( + name: "^FTSE", + symbol: "FTSE 100", + price: "8,285.71", + change: "+1.21%", + isPositive: true, + ), + StockItem( + name: "GE", + symbol: "GE Aerospace", + price: "171.01", + change: "+3.85%", + isPositive: true, + ), + StockItem( + name: "AAPL", + symbol: "Apple Inc.", + price: "219.37", + change: "+0.86%", + isPositive: true, + ), + StockItem( + name: "NKE", + symbol: "NIKE, Inc.", + price: "72.44", + change: "+1.45%", + isPositive: true, + ), + ], + ), + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 7b9be20..3ba4b5e 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,10 +5,14 @@ import FlutterMacOS import Foundation +import cloud_firestore import firebase_auth import firebase_core +import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin")) FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 0b1330f..a376d62 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: "71c01c1998c40b3af1944ad0a5f374b4e6fef7f3d2df487f3970dbeadaeb25a1" + sha256: eae3133cbb06de9205899b822e3897fc6a8bc278ad4c944b4ce612689369694b url: "https://pub.dev" source: hosted - version: "1.3.46" + version: "1.3.47" async: dependency: transitive description: @@ -25,6 +25,46 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + camera: + dependency: "direct main" + description: + name: camera + sha256: "26ff41045772153f222ffffecba711a206f670f5834d40ebf5eed3811692f167" + url: "https://pub.dev" + source: hosted + version: "0.11.0+2" + camera_android_camerax: + dependency: transitive + description: + name: camera_android_camerax + sha256: e3627fdc2132d89212b8a8676679f5b07008c7e3d8ae00cea775c3397f9e742b + url: "https://pub.dev" + source: hosted + version: "0.6.10" + camera_avfoundation: + dependency: transitive + description: + name: camera_avfoundation + sha256: "2e4c568f70e406ccb87376bc06b53d2f5bebaab71e2fbcc1a950e31449381bcf" + url: "https://pub.dev" + source: hosted + version: "0.9.17+5" + camera_platform_interface: + dependency: transitive + description: + name: camera_platform_interface + sha256: b3ede1f171532e0d83111fe0980b46d17f1aa9788a07a2fbed07366bbdbb9061 + url: "https://pub.dev" + source: hosted + version: "2.8.0" + camera_web: + dependency: transitive + description: + name: camera_web + sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f" + url: "https://pub.dev" + source: hosted + version: "0.3.5" characters: dependency: transitive description: @@ -41,6 +81,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + cloud_firestore: + dependency: "direct main" + description: + name: cloud_firestore + sha256: "852c1a14399be76c71a881d2475392393f2ceef77c0859405a6bcac5ec8c0221" + url: "https://pub.dev" + source: hosted + version: "5.5.1" + cloud_firestore_platform_interface: + dependency: transitive + description: + name: cloud_firestore_platform_interface + sha256: "69f0baeb7ac0577946f82991075b18133fd81441aeda487e41fb5692958117cd" + url: "https://pub.dev" + source: hosted + version: "6.5.1" + cloud_firestore_web: + dependency: transitive + description: + name: cloud_firestore_web + sha256: ff65ee2f8caafb1bcba3133666d5ee00848f94c677900a368cad593982ef8ba8 + url: "https://pub.dev" + source: hosted + version: "4.3.5" collection: dependency: transitive description: @@ -49,6 +113,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + url: "https://pub.dev" + source: hosted + version: "0.3.4+2" + equatable: + dependency: transitive + description: + name: equatable + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + url: "https://pub.dev" + source: hosted + version: "2.0.7" fake_async: dependency: transitive description: @@ -57,6 +137,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" firebase_auth: dependency: "direct main" description: @@ -85,26 +181,34 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: "2438a75ad803e818ad3bd5df49137ee619c46b6fc7101f4dbc23da07305ce553" + sha256: fef81a53ba1ca618def1f8bef4361df07968434e62cb204c1fb90bb880a03da2 url: "https://pub.dev" source: hosted - version: "3.8.0" + version: "3.8.1" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: e30da58198a6d4b49d5bce4e852f985c32cb10db329ebef9473db2b9f09ce810 + sha256: b94b217e3ad745e784960603d33d99471621ecca151c99c670869b76e50ad2a6 url: "https://pub.dev" source: hosted - version: "5.3.0" + version: "5.3.1" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: f967a7138f5d2ffb1ce15950e2a382924239eaa521150a8f144af34e68b3b3e5 + sha256: "9e69806bb3d905aeec3c1242e0e1475de6ea6d48f456af29d598fb229a2b4e5e" url: "https://pub.dev" source: hosted - version: "2.18.1" + version: "2.18.2" + fl_chart: + dependency: "direct main" + description: + name: fl_chart + sha256: "74959b99b92b9eebeed1a4049426fd67c4abc3c5a0f4d12e2877097d6a11ae08" + url: "https://pub.dev" + source: hosted + version: "0.69.2" flutter: dependency: "direct main" description: flutter @@ -118,6 +222,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "9b78450b89f059e96c9ebb355fa6b3df1d6b330436e0b885fb49594c41721398" + url: "https://pub.dev" + source: hosted + version: "2.0.23" flutter_test: dependency: "direct dev" description: flutter @@ -200,6 +312,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" plugin_platform_interface: dependency: transitive description: @@ -208,6 +352,62 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "95f9997ca1fb9799d494d0cb2a780fd7be075818d59f00c43832ed112b158a82" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "7f172d1b06de5da47b6264c2692ee2ead20bbbc246690427cdb4fc301cd0c549" + url: "https://pub.dev" + source: hosted + version: "2.3.4" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e + url: "https://pub.dev" + source: hosted + version: "2.4.2" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" sky_engine: dependency: transitive description: flutter @@ -237,6 +437,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -293,6 +501,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" sdks: dart: ">=3.5.3 <4.0.0" - flutter: ">=3.22.0" + flutter: ">=3.24.0" diff --git a/pubspec.yaml b/pubspec.yaml index da685c8..bd9f4dc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,15 +7,21 @@ environment: sdk: ^3.5.3 dependencies: + camera: ^0.11.0+2 + cloud_firestore: ^5.5.1 + firebase_auth: ^5.3.3 + firebase_core: ^3.8.0 + fl_chart: ^0.69.2 flutter: sdk: flutter - firebase_core: ^3.8.0 - firebase_auth: ^5.3.3 + shared_preferences: ^2.3.3 dev_dependencies: + flutter_lints: ^4.0.0 flutter_test: sdk: flutter - flutter_lints: ^4.0.0 flutter: uses-material-design: true + assets: + - lib/assets/ diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index d141b74..bf6d21a 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) { + CloudFirestorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("CloudFirestorePluginCApi")); FirebaseAuthPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi")); FirebaseCorePluginCApiRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 29944d5..b83b40a 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + cloud_firestore firebase_auth firebase_core )