init: ynab_lite Flutter project
This commit is contained in:
commit
9df372ebb7
45
.gitignore
vendored
Normal file
45
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.build/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
.swiftpm/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
/coverage/
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Android Studio will place build artifacts here
|
||||||
|
/android/app/debug
|
||||||
|
/android/app/profile
|
||||||
|
/android/app/release
|
||||||
30
.metadata
Normal file
30
.metadata
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: "ee80f08bbf97172ec030b8751ceab557177a34a6"
|
||||||
|
channel: "stable"
|
||||||
|
|
||||||
|
project_type: app
|
||||||
|
|
||||||
|
# Tracks metadata for the flutter migrate command
|
||||||
|
migration:
|
||||||
|
platforms:
|
||||||
|
- platform: root
|
||||||
|
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
- platform: android
|
||||||
|
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
|
||||||
|
|
||||||
|
# User provided section
|
||||||
|
|
||||||
|
# List of Local paths (relative to this file) that should be
|
||||||
|
# ignored by the migrate tool.
|
||||||
|
#
|
||||||
|
# Files that are not part of the templates will be ignored by default.
|
||||||
|
unmanaged_files:
|
||||||
|
- 'lib/main.dart'
|
||||||
|
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||||
17
README.md
Normal file
17
README.md
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# ynab_lite
|
||||||
|
|
||||||
|
A new Flutter project.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This project is a starting point for a Flutter application.
|
||||||
|
|
||||||
|
A few resources to get you started if this is your first Flutter project:
|
||||||
|
|
||||||
|
- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter)
|
||||||
|
- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||||
|
- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources)
|
||||||
|
|
||||||
|
For help getting started with Flutter development, view the
|
||||||
|
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||||
|
samples, guidance on mobile development, and a full API reference.
|
||||||
28
analysis_options.yaml
Normal file
28
analysis_options.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# check for errors, warnings, and lints.
|
||||||
|
#
|
||||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||||
|
# invoked from the command line by running `flutter analyze`.
|
||||||
|
|
||||||
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
# The lint rules applied to this project can be customized in the
|
||||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
# included above or to enable additional rules. A list of all available lints
|
||||||
|
# and their documentation is published at https://dart.dev/lints.
|
||||||
|
#
|
||||||
|
# Instead of disabling a lint rule for the entire project in the
|
||||||
|
# section below, it can also be suppressed for a single line of code
|
||||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
|
# producing the lint.
|
||||||
|
rules:
|
||||||
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
||||||
14
android/.gitignore
vendored
Normal file
14
android/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
gradle-wrapper.jar
|
||||||
|
/.gradle
|
||||||
|
/captures/
|
||||||
|
/gradlew
|
||||||
|
/gradlew.bat
|
||||||
|
/local.properties
|
||||||
|
GeneratedPluginRegistrant.java
|
||||||
|
.cxx/
|
||||||
|
|
||||||
|
# Remember to never publicly share your keystore.
|
||||||
|
# See https://flutter.dev/to/reference-keystore
|
||||||
|
key.properties
|
||||||
|
**/*.keystore
|
||||||
|
**/*.jks
|
||||||
45
android/app/build.gradle.kts
Normal file
45
android/app/build.gradle.kts
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
plugins {
|
||||||
|
id("com.android.application")
|
||||||
|
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||||
|
id("dev.flutter.flutter-gradle-plugin")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.ynab.ynab_lite"
|
||||||
|
compileSdk = flutter.compileSdkVersion
|
||||||
|
ndkVersion = flutter.ndkVersion
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
|
applicationId = "com.ynab.ynab_lite"
|
||||||
|
// 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
|
||||||
|
targetSdk = flutter.targetSdkVersion
|
||||||
|
versionCode = flutter.versionCode
|
||||||
|
versionName = flutter.versionName
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
// TODO: Add your own signing config for the release build.
|
||||||
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
compilerOptions {
|
||||||
|
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter {
|
||||||
|
source = "../.."
|
||||||
|
}
|
||||||
7
android/app/src/debug/AndroidManifest.xml
Normal file
7
android/app/src/debug/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
||||||
45
android/app/src/main/AndroidManifest.xml
Normal file
45
android/app/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<application
|
||||||
|
android:label="ynab_lite"
|
||||||
|
android:name="${applicationName}"
|
||||||
|
android:icon="@mipmap/ic_launcher">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:taskAffinity=""
|
||||||
|
android:theme="@style/LaunchTheme"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
|
the Android process has started. This theme is visible to the user
|
||||||
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
|
to determine the Window background behind the Flutter UI. -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
|
android:resource="@style/NormalTheme"
|
||||||
|
/>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<!-- Don't delete the meta-data below.
|
||||||
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2" />
|
||||||
|
</application>
|
||||||
|
<!-- Required to query activities that can process text, see:
|
||||||
|
https://developer.android.com/training/package-visibility and
|
||||||
|
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||||
|
|
||||||
|
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||||
|
<data android:mimeType="text/plain"/>
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
|
</manifest>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.ynab.ynab_lite
|
||||||
|
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
|
class MainActivity : FlutterActivity()
|
||||||
12
android/app/src/main/res/drawable-v21/launch_background.xml
Normal file
12
android/app/src/main/res/drawable-v21/launch_background.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="?android:colorBackground" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
||||||
12
android/app/src/main/res/drawable/launch_background.xml
Normal file
12
android/app/src/main/res/drawable/launch_background.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@android:color/white" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
||||||
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 544 B |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 442 B |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 721 B |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
18
android/app/src/main/res/values-night/styles.xml
Normal file
18
android/app/src/main/res/values-night/styles.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
18
android/app/src/main/res/values/styles.xml
Normal file
18
android/app/src/main/res/values/styles.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
7
android/app/src/profile/AndroidManifest.xml
Normal file
7
android/app/src/profile/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
||||||
24
android/build.gradle.kts
Normal file
24
android/build.gradle.kts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val newBuildDir: Directory =
|
||||||
|
rootProject.layout.buildDirectory
|
||||||
|
.dir("../../build")
|
||||||
|
.get()
|
||||||
|
rootProject.layout.buildDirectory.value(newBuildDir)
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
|
||||||
|
project.layout.buildDirectory.value(newSubprojectBuildDir)
|
||||||
|
}
|
||||||
|
subprojects {
|
||||||
|
project.evaluationDependsOn(":app")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<Delete>("clean") {
|
||||||
|
delete(rootProject.layout.buildDirectory)
|
||||||
|
}
|
||||||
6
android/gradle.properties
Normal file
6
android/gradle.properties
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||||
|
android.useAndroidX=true
|
||||||
|
# This newDsl flag was added by the Flutter template
|
||||||
|
android.newDsl=false
|
||||||
|
# This builtInKotlin flag was added by the Flutter template
|
||||||
|
android.builtInKotlin=false
|
||||||
5
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip
|
||||||
26
android/settings.gradle.kts
Normal file
26
android/settings.gradle.kts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
pluginManagement {
|
||||||
|
val flutterSdkPath =
|
||||||
|
run {
|
||||||
|
val properties = java.util.Properties()
|
||||||
|
file("local.properties").inputStream().use { properties.load(it) }
|
||||||
|
val flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||||
|
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
|
||||||
|
flutterSdkPath
|
||||||
|
}
|
||||||
|
|
||||||
|
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||||
|
id("com.android.application") version "9.0.1" apply false
|
||||||
|
id("org.jetbrains.kotlin.android") version "2.3.20" apply false
|
||||||
|
}
|
||||||
|
|
||||||
|
include(":app")
|
||||||
218
lib/app.dart
Normal file
218
lib/app.dart
Normal file
|
|
@ -0,0 +1,218 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'theme/app_theme.dart';
|
||||||
|
import 'screens/dashboard_screen.dart';
|
||||||
|
import 'screens/budget_screen.dart';
|
||||||
|
import 'screens/accounts_screen.dart';
|
||||||
|
import 'screens/transactions_screen.dart';
|
||||||
|
|
||||||
|
final _selectedIndexProvider = StateProvider<int>((ref) => 0);
|
||||||
|
|
||||||
|
class YnabApp extends ConsumerStatefulWidget {
|
||||||
|
const YnabApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<YnabApp> createState() => _YnabAppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _YnabAppState extends ConsumerState<YnabApp> {
|
||||||
|
late final List<Widget> _screens;
|
||||||
|
late final List<_NavItem> _navItems;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_screens = const [
|
||||||
|
DashboardScreen(),
|
||||||
|
BudgetScreen(),
|
||||||
|
AccountsScreen(),
|
||||||
|
TransactionsScreen(),
|
||||||
|
];
|
||||||
|
_navItems = const [
|
||||||
|
_NavItem(icon: Icons.dashboard_outlined, selectedIcon: Icons.dashboard, label: 'Tableau de bord'),
|
||||||
|
_NavItem(icon: Icons.pie_chart_outline, selectedIcon: Icons.pie_chart, label: 'Budget'),
|
||||||
|
_NavItem(icon: Icons.account_balance_outlined, selectedIcon: Icons.account_balance, label: 'Comptes'),
|
||||||
|
_NavItem(icon: Icons.receipt_long_outlined, selectedIcon: Icons.receipt_long, label: 'Transactions'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final selectedIndex = ref.watch(_selectedIndexProvider);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: kSurfaceBg,
|
||||||
|
body: SafeArea(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
// ── Side Navigation ──
|
||||||
|
Container(
|
||||||
|
width: 72,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: kSurfaceCard,
|
||||||
|
border: Border(
|
||||||
|
right: BorderSide(color: kDivider, width: 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
// App logo / icon
|
||||||
|
Container(
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: kGreenAccent.withAlpha(30),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: const Center(
|
||||||
|
child: Icon(Icons.currency_bitcoin, color: kGreenAccent, size: 22),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'YNAB\nLite',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 9,
|
||||||
|
color: kGreenAccent,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
height: 1.2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 28),
|
||||||
|
// Navigation items
|
||||||
|
...List.generate(_navItems.length, (i) {
|
||||||
|
final item = _navItems[i];
|
||||||
|
final active = i == selectedIndex;
|
||||||
|
return _NavRailItem(
|
||||||
|
icon: active ? item.selectedIcon : item.icon,
|
||||||
|
label: item.label,
|
||||||
|
active: active,
|
||||||
|
onTap: () => ref.read(_selectedIndexProvider.notifier).state = i,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
const Spacer(),
|
||||||
|
// Settings at bottom
|
||||||
|
_NavRailItem(
|
||||||
|
icon: Icons.settings_outlined,
|
||||||
|
selectedIcon: Icons.settings,
|
||||||
|
label: 'Paramètres',
|
||||||
|
active: false,
|
||||||
|
onTap: () {},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// ── Main Content ──
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
// Header
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: kSurfaceBg,
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(color: kDivider, width: 0.5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
_navItems[selectedIndex].label,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: kTextPrimary,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.add, color: kGreenAccent),
|
||||||
|
onPressed: () {},
|
||||||
|
tooltip: 'Ajouter une transaction',
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.search, color: kTextSecondary),
|
||||||
|
onPressed: () {},
|
||||||
|
tooltip: 'Rechercher',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Content
|
||||||
|
Expanded(
|
||||||
|
child: AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: _screens[selectedIndex],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NavItem {
|
||||||
|
final IconData icon;
|
||||||
|
final IconData selectedIcon;
|
||||||
|
final String label;
|
||||||
|
const _NavItem({required this.icon, required this.selectedIcon, required this.label});
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NavRailItem extends StatelessWidget {
|
||||||
|
final IconData icon;
|
||||||
|
final IconData? selectedIcon;
|
||||||
|
final String label;
|
||||||
|
final bool active;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
const _NavRailItem({
|
||||||
|
required this.icon,
|
||||||
|
this.selectedIcon,
|
||||||
|
required this.label,
|
||||||
|
required this.active,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||||
|
child: Tooltip(
|
||||||
|
message: label,
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: Container(
|
||||||
|
width: 56,
|
||||||
|
height: 48,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
color: active ? kGreenAccent.withAlpha(20) : null,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
icon,
|
||||||
|
size: 22,
|
||||||
|
color: active ? kGreenAccent : kTextSecondary,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
87
lib/data/db.dart
Normal file
87
lib/data/db.dart
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:drift_flutter/drift_flutter.dart';
|
||||||
|
import 'tables.dart';
|
||||||
|
|
||||||
|
part 'db.g.dart';
|
||||||
|
|
||||||
|
@DriftDatabase(
|
||||||
|
tables: [
|
||||||
|
Accounts,
|
||||||
|
CategoryGroups,
|
||||||
|
Categories,
|
||||||
|
Transactions,
|
||||||
|
Budgets,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
class AppDatabase extends _$AppDatabase {
|
||||||
|
AppDatabase() : super(_openConnection());
|
||||||
|
|
||||||
|
static QueryExecutor _openConnection() {
|
||||||
|
return driftDatabase(name: 'ynab_lite');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get schemaVersion => 1;
|
||||||
|
|
||||||
|
// ── Seed ──────────────────────────────────────────────────────────────────
|
||||||
|
Future<void> seedIfEmpty() async {
|
||||||
|
final count = await select(accounts).get().then((r) => r.length);
|
||||||
|
if (count > 0) return;
|
||||||
|
|
||||||
|
// default category groups
|
||||||
|
final groups = [
|
||||||
|
CategoryGroupsCompanion.insert(name: 'Logement & Charges Fixes', sortOrder: const Value(1)),
|
||||||
|
CategoryGroupsCompanion.insert(name: 'Alimentation', sortOrder: const Value(2)),
|
||||||
|
CategoryGroupsCompanion.insert(name: 'Transport', sortOrder: const Value(3)),
|
||||||
|
CategoryGroupsCompanion.insert(name: 'Loisirs & Divertissements', sortOrder: const Value(4)),
|
||||||
|
CategoryGroupsCompanion.insert(name: 'Épargne & Objectifs', sortOrder: const Value(5)),
|
||||||
|
];
|
||||||
|
for (final g in groups) {
|
||||||
|
await into(categoryGroups).insert(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
// categories
|
||||||
|
final cats = [
|
||||||
|
(group: 1, name: 'Loyer', icon: 'home'),
|
||||||
|
(group: 1, name: 'Électricité', icon: 'bolt'),
|
||||||
|
(group: 1, name: 'Eau', icon: 'water_drop'),
|
||||||
|
(group: 1, name: 'Assurance habitation', icon: 'shield'),
|
||||||
|
(group: 2, name: 'Courses', icon: 'shopping_cart'),
|
||||||
|
(group: 2, name: 'Restaurants', icon: 'restaurant'),
|
||||||
|
(group: 3, name: 'Essence', icon: 'local_gas_station'),
|
||||||
|
(group: 3, name: 'Transports en commun', icon: 'directions_bus'),
|
||||||
|
(group: 4, name: 'Streaming', icon: 'tv'),
|
||||||
|
(group: 4, name: 'Sorties', icon: 'celebration'),
|
||||||
|
(group: 5, name: 'Épargne d\'urgence', icon: 'savings'),
|
||||||
|
(group: 5, name: 'Vacances', icon: 'flight'),
|
||||||
|
];
|
||||||
|
for (int i = 0; i < cats.length; i++) {
|
||||||
|
await into(categories).insert(CategoriesCompanion.insert(
|
||||||
|
name: cats[i].name,
|
||||||
|
groupId: cats[i].group,
|
||||||
|
icon: Value(cats[i].icon),
|
||||||
|
sortOrder: Value(i + 1),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// default account
|
||||||
|
await into(accounts).insert(AccountsCompanion.insert(
|
||||||
|
name: 'Compte courant',
|
||||||
|
type: 'checking',
|
||||||
|
balance: const Value(2500.0),
|
||||||
|
sortOrder: const Value(1),
|
||||||
|
));
|
||||||
|
await into(accounts).insert(AccountsCompanion.insert(
|
||||||
|
name: 'Livret A',
|
||||||
|
type: 'savings',
|
||||||
|
balance: const Value(5000.0),
|
||||||
|
sortOrder: const Value(2),
|
||||||
|
));
|
||||||
|
await into(accounts).insert(AccountsCompanion.insert(
|
||||||
|
name: 'Espèces',
|
||||||
|
type: 'cash',
|
||||||
|
balance: const Value(120.0),
|
||||||
|
sortOrder: const Value(3),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
3874
lib/data/db.g.dart
Normal file
3874
lib/data/db.g.dart
Normal file
File diff suppressed because it is too large
Load diff
47
lib/data/tables.dart
Normal file
47
lib/data/tables.dart
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
// ── Tables ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
class Accounts extends Table {
|
||||||
|
IntColumn get id => integer().autoIncrement()();
|
||||||
|
TextColumn get name => text()();
|
||||||
|
TextColumn get type => text()(); // checking, savings, credit, cash
|
||||||
|
RealColumn get balance => real().withDefault(const Constant(0.0))();
|
||||||
|
BoolColumn get isClosed => boolean().withDefault(const Constant(false))();
|
||||||
|
IntColumn get sortOrder => integer().withDefault(const Constant(0))();
|
||||||
|
}
|
||||||
|
|
||||||
|
class CategoryGroups extends Table {
|
||||||
|
IntColumn get id => integer().autoIncrement()();
|
||||||
|
TextColumn get name => text()();
|
||||||
|
IntColumn get sortOrder => integer().withDefault(const Constant(0))();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Categories extends Table {
|
||||||
|
IntColumn get id => integer().autoIncrement()();
|
||||||
|
TextColumn get name => text()();
|
||||||
|
IntColumn get groupId => integer().references(CategoryGroups, #id)();
|
||||||
|
RealColumn get budgeted => real().withDefault(const Constant(0.0))();
|
||||||
|
IntColumn get sortOrder => integer().withDefault(const Constant(0))();
|
||||||
|
TextColumn get icon => text().nullable()();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Transactions extends Table {
|
||||||
|
IntColumn get id => integer().autoIncrement()();
|
||||||
|
IntColumn get accountId => integer().references(Accounts, #id)();
|
||||||
|
IntColumn get categoryId => integer().references(Categories, #id).nullable()();
|
||||||
|
TextColumn get payee => text()();
|
||||||
|
TextColumn get notes => text().nullable()();
|
||||||
|
RealColumn get amount => real()(); // positive = income, negative = expense
|
||||||
|
DateTimeColumn get date => dateTime()();
|
||||||
|
BoolColumn get cleared => boolean().withDefault(const Constant(false))();
|
||||||
|
IntColumn get sortOrder => integer().withDefault(const Constant(0))();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Budgets extends Table {
|
||||||
|
IntColumn get id => integer().autoIncrement()();
|
||||||
|
IntColumn get categoryId => integer().references(Categories, #id)();
|
||||||
|
TextColumn get month => text()(); // "2026-07"
|
||||||
|
RealColumn get budgeted => real().withDefault(const Constant(0.0))();
|
||||||
|
IntColumn get sortOrder => integer().withDefault(const Constant(0))();
|
||||||
|
}
|
||||||
27
lib/main.dart
Normal file
27
lib/main.dart
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'app.dart';
|
||||||
|
import 'theme/app_theme.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
runApp(
|
||||||
|
const ProviderScope(
|
||||||
|
child: YnabLiteApp(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class YnabLiteApp extends StatelessWidget {
|
||||||
|
const YnabLiteApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
title: 'YNAB Lite',
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
theme: ynabDarkTheme,
|
||||||
|
home: const YnabApp(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
107
lib/providers/db_provider.dart
Normal file
107
lib/providers/db_provider.dart
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import '../data/db.dart';
|
||||||
|
export '../data/db.dart' show Account, CategoryGroup, Category, Transaction, Budget;
|
||||||
|
|
||||||
|
// Singleton DB provider
|
||||||
|
final dbProvider = Provider<AppDatabase>((ref) {
|
||||||
|
final db = AppDatabase();
|
||||||
|
ref.onDispose(() => db.close());
|
||||||
|
db.seedIfEmpty();
|
||||||
|
return db;
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Accounts ────────────────────────────────────────────────────────────
|
||||||
|
final accountsProvider = StreamProvider<List<Account>>((ref) {
|
||||||
|
final db = ref.watch(dbProvider);
|
||||||
|
return db.select(db.accounts).watch();
|
||||||
|
});
|
||||||
|
|
||||||
|
final openAccountsProvider = Provider<List<Account>>((ref) {
|
||||||
|
final accounts = ref.watch(accountsProvider).valueOrNull ?? [];
|
||||||
|
return accounts.where((a) => !a.isClosed).toList();
|
||||||
|
});
|
||||||
|
|
||||||
|
final totalBalanceProvider = Provider<double>((ref) {
|
||||||
|
final accounts = ref.watch(openAccountsProvider);
|
||||||
|
return accounts.fold(0.0, (sum, a) => sum + a.balance);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Category Groups ─────────────────────────────────────────────────────
|
||||||
|
final categoryGroupsProvider = StreamProvider<List<CategoryGroup>>((ref) {
|
||||||
|
final db = ref.watch(dbProvider);
|
||||||
|
return (db.select(db.categoryGroups)
|
||||||
|
..orderBy([(g) => OrderingTerm(expression: g.sortOrder)]))
|
||||||
|
.watch();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Categories ──────────────────────────────────────────────────────────
|
||||||
|
final categoriesProvider = StreamProvider<List<Category>>((ref) {
|
||||||
|
final db = ref.watch(dbProvider);
|
||||||
|
return (db.select(db.categories)
|
||||||
|
..orderBy([(c) => OrderingTerm(expression: c.sortOrder)]))
|
||||||
|
.watch();
|
||||||
|
});
|
||||||
|
|
||||||
|
final categoriesByGroupProvider = Provider<Map<int, List<Category>>>((ref) {
|
||||||
|
final cats = ref.watch(categoriesProvider).valueOrNull ?? [];
|
||||||
|
final map = <int, List<Category>>{};
|
||||||
|
for (final c in cats) {
|
||||||
|
map.putIfAbsent(c.groupId, () => []).add(c);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Transactions ────────────────────────────────────────────────────────
|
||||||
|
final allTransactionsProvider = StreamProvider<List<Transaction>>((ref) {
|
||||||
|
final db = ref.watch(dbProvider);
|
||||||
|
return (db.select(db.transactions)
|
||||||
|
..orderBy([(t) => OrderingTerm(expression: t.date, mode: OrderingMode.desc)]))
|
||||||
|
.watch();
|
||||||
|
});
|
||||||
|
|
||||||
|
final recentTransactionsProvider = Provider<List<Transaction>>((ref) {
|
||||||
|
final all = ref.watch(allTransactionsProvider).valueOrNull ?? [];
|
||||||
|
return all.take(10).toList();
|
||||||
|
});
|
||||||
|
|
||||||
|
final monthlyTransactionsProvider = Provider.family<List<Transaction>, String>((ref, month) {
|
||||||
|
final all = ref.watch(allTransactionsProvider).valueOrNull ?? [];
|
||||||
|
return all.where((t) {
|
||||||
|
final ym = '${t.date.year}-${t.date.month.toString().padLeft(2, '0')}';
|
||||||
|
return ym == month;
|
||||||
|
}).toList();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Budget ──────────────────────────────────────────────────────────────
|
||||||
|
final budgetsProvider = StreamProvider<List<Budget>>((ref) {
|
||||||
|
final db = ref.watch(dbProvider);
|
||||||
|
return db.select(db.budgets).watch();
|
||||||
|
});
|
||||||
|
|
||||||
|
final currentMonthProvider = Provider<String>((ref) {
|
||||||
|
final now = DateTime.now();
|
||||||
|
return '${now.year}-${now.month.toString().padLeft(2, '0')}';
|
||||||
|
});
|
||||||
|
|
||||||
|
final monthlyBudgetProvider = Provider.family<double?, int>((ref, categoryId) {
|
||||||
|
final month = ref.watch(currentMonthProvider);
|
||||||
|
final budgets = ref.watch(budgetsProvider).valueOrNull ?? [];
|
||||||
|
final match = budgets.where(
|
||||||
|
(b) => b.categoryId == categoryId && b.month == month,
|
||||||
|
);
|
||||||
|
return match.isNotEmpty ? match.first.budgeted : null;
|
||||||
|
});
|
||||||
|
|
||||||
|
final categorySpentProvider = Provider.family<double, int>((ref, categoryId) {
|
||||||
|
final month = ref.watch(currentMonthProvider);
|
||||||
|
final txs = ref.watch(monthlyTransactionsProvider(month));
|
||||||
|
final filtered = txs.where((t) => t.categoryId == categoryId);
|
||||||
|
return filtered.fold(0.0, (sum, t) => sum + t.amount);
|
||||||
|
});
|
||||||
|
|
||||||
|
final categoryRemainingProvider = Provider.family<double, int>((ref, categoryId) {
|
||||||
|
final budgeted = ref.watch(monthlyBudgetProvider(categoryId)) ?? 0.0;
|
||||||
|
final spent = ref.watch(categorySpentProvider(categoryId));
|
||||||
|
return budgeted + spent; // spent is negative, so budgeted + spent = remaining
|
||||||
|
});
|
||||||
89
lib/screens/accounts_screen.dart
Normal file
89
lib/screens/accounts_screen.dart
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import '../providers/db_provider.dart';
|
||||||
|
import '../theme/app_theme.dart';
|
||||||
|
|
||||||
|
class AccountsScreen extends ConsumerWidget {
|
||||||
|
const AccountsScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final accounts = ref.watch(openAccountsProvider);
|
||||||
|
|
||||||
|
return ListView(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 90),
|
||||||
|
children: [
|
||||||
|
...accounts.map((a) => _AccountCard(account: a)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AccountCard extends StatelessWidget {
|
||||||
|
final Account account;
|
||||||
|
const _AccountCard({required this.account});
|
||||||
|
|
||||||
|
IconData _icon() {
|
||||||
|
switch (account.type) {
|
||||||
|
case 'savings': return Icons.savings_outlined;
|
||||||
|
case 'credit': return Icons.credit_card_outlined;
|
||||||
|
case 'cash': return Icons.money_outlined;
|
||||||
|
default: return Icons.account_balance_outlined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _label() {
|
||||||
|
switch (account.type) {
|
||||||
|
case 'checking': return 'Compte courant';
|
||||||
|
case 'savings': return 'Épargne';
|
||||||
|
case 'credit': return 'Carte de crédit';
|
||||||
|
case 'cash': return 'Espèces';
|
||||||
|
default: return account.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: kSurfaceElevated,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Icon(_icon(), color: kTextSecondary, size: 22),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 14),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(account.name,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium
|
||||||
|
?.copyWith(fontWeight: FontWeight.w600)),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Text(_label(), style: Theme.of(context).textTheme.bodySmall),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
formatCurrency(account.balance),
|
||||||
|
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: account.balance >= 0 ? kGreenAccent : kRedAccent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
170
lib/screens/budget_screen.dart
Normal file
170
lib/screens/budget_screen.dart
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import '../providers/db_provider.dart';
|
||||||
|
import '../theme/app_theme.dart';
|
||||||
|
|
||||||
|
class BudgetScreen extends ConsumerWidget {
|
||||||
|
const BudgetScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final groups = ref.watch(categoryGroupsProvider).valueOrNull ?? [];
|
||||||
|
final catsByGroup = ref.watch(categoriesByGroupProvider);
|
||||||
|
|
||||||
|
return ListView(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 90),
|
||||||
|
children: [
|
||||||
|
Card(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Budget du mois', style: Theme.of(context).textTheme.titleMedium),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text('Juillet 2026', style: Theme.of(context).textTheme.bodySmall),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
...groups.map((group) {
|
||||||
|
final cats = catsByGroup[group.id] ?? [];
|
||||||
|
if (cats.isEmpty) return const SizedBox.shrink();
|
||||||
|
return _CategoryGroupTile(group: group, categoryIds: cats.map((c) => c.id).toList());
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CategoryGroupTile extends ConsumerWidget {
|
||||||
|
final CategoryGroup group;
|
||||||
|
final List<int> categoryIds;
|
||||||
|
|
||||||
|
const _CategoryGroupTile({required this.group, required this.categoryIds});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final expanded = ref.watch(_expandedGroupProvider(group.id));
|
||||||
|
final cats = ref.watch(categoriesByGroupProvider)[group.id] ?? [];
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
onTap: () => ref.read(_expandedGroupProvider(group.id).notifier).toggle(),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 4),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
expanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right,
|
||||||
|
size: 18,
|
||||||
|
color: kTextSecondary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(group.name,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleSmall
|
||||||
|
?.copyWith(fontWeight: FontWeight.w600)),
|
||||||
|
const Spacer(),
|
||||||
|
Text(
|
||||||
|
formatCurrency(
|
||||||
|
cats.fold(0.0, (s, c) => s + ref.watch(categoryRemainingProvider(c.id))),
|
||||||
|
),
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (expanded)
|
||||||
|
...cats.map((c) => _CategoryRow(categoryId: c.id)),
|
||||||
|
const Divider(height: 1),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CategoryRow extends ConsumerWidget {
|
||||||
|
final int categoryId;
|
||||||
|
|
||||||
|
const _CategoryRow({required this.categoryId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final cats = ref.watch(categoriesByGroupProvider);
|
||||||
|
final category = cats.values
|
||||||
|
.expand((list) => list)
|
||||||
|
.firstWhere((c) => c.id == categoryId, orElse: () => throw StateError('missing'));
|
||||||
|
|
||||||
|
final budgeted = ref.watch(monthlyBudgetProvider(categoryId)) ?? 0.0;
|
||||||
|
final spent = ref.watch(categorySpentProvider(categoryId));
|
||||||
|
final remaining = ref.watch(categoryRemainingProvider(categoryId));
|
||||||
|
final pct = budgeted > 0 ? ((budgeted + spent) / budgeted).clamp(0.0, 1.0) : 0.0;
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8, bottom: 4),
|
||||||
|
child: Card(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(category.name, style: Theme.of(context).textTheme.bodyMedium),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
formatCurrency(remaining),
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: remaining >= 0 ? kGreenAccent : kRedAccent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
child: LinearProgressIndicator(
|
||||||
|
value: pct,
|
||||||
|
backgroundColor: kDivider,
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
|
remaining >= 0 ? kGreenAccent : kRedAccent,
|
||||||
|
),
|
||||||
|
minHeight: 4,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text('Budget: ${formatCurrency(budgeted)}',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text('Dépensé: ${formatCurrency(spent.abs())}',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ExpandedGroupNotifier extends StateNotifier<bool> {
|
||||||
|
_ExpandedGroupNotifier() : super(true);
|
||||||
|
|
||||||
|
void toggle() => state = !state;
|
||||||
|
}
|
||||||
|
|
||||||
|
final _expandedGroupProvider =
|
||||||
|
StateNotifierProvider.family<_ExpandedGroupNotifier, bool, int>(
|
||||||
|
(ref, id) => _ExpandedGroupNotifier());
|
||||||
152
lib/screens/dashboard_screen.dart
Normal file
152
lib/screens/dashboard_screen.dart
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import '../providers/db_provider.dart';
|
||||||
|
import '../theme/app_theme.dart';
|
||||||
|
|
||||||
|
class DashboardScreen extends ConsumerWidget {
|
||||||
|
const DashboardScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final totalBalance = ref.watch(totalBalanceProvider);
|
||||||
|
final accounts = ref.watch(openAccountsProvider);
|
||||||
|
final recentTxs = ref.watch(recentTransactionsProvider);
|
||||||
|
|
||||||
|
return ListView(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 90),
|
||||||
|
children: [
|
||||||
|
// Total Balance
|
||||||
|
_BalanceCard(balance: totalBalance),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
// By accounts
|
||||||
|
Text('Comptes', style: Theme.of(context).textTheme.titleMedium),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
...accounts.map((a) => _AccountMiniCard(account: a)),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
// Recent transactions
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text('Transactions récentes', style: Theme.of(context).textTheme.titleMedium),
|
||||||
|
TextButton.icon(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: const Icon(Icons.arrow_forward_ios, size: 14),
|
||||||
|
label: const Text('Voir tout'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
if (recentTxs.isEmpty)
|
||||||
|
Card(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(32),
|
||||||
|
child: Center(child: Text('Aucune transaction', style: Theme.of(context).textTheme.bodyMedium)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
...recentTxs.map((t) => _TxItem(transaction: t)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BalanceCard extends StatelessWidget {
|
||||||
|
final double balance;
|
||||||
|
const _BalanceCard({required this.balance});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('Solde total', style: Theme.of(context).textTheme.bodyMedium),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
formatCurrency(balance),
|
||||||
|
style: Theme.of(context).textTheme.headlineLarge?.copyWith(
|
||||||
|
fontSize: 36,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: balance >= 0 ? kGreenAccent : kRedAccent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AccountMiniCard extends StatelessWidget {
|
||||||
|
final Account account;
|
||||||
|
const _AccountMiniCard({required this.account});
|
||||||
|
|
||||||
|
IconData _icon() {
|
||||||
|
switch (account.type) {
|
||||||
|
case 'savings': return Icons.savings_outlined;
|
||||||
|
case 'credit': return Icons.credit_card_outlined;
|
||||||
|
case 'cash': return Icons.money_outlined;
|
||||||
|
default: return Icons.account_balance_outlined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.only(bottom: 6),
|
||||||
|
child: ListTile(
|
||||||
|
dense: true,
|
||||||
|
leading: Icon(_icon(), color: kTextSecondary),
|
||||||
|
title: Text(account.name, style: Theme.of(context).textTheme.bodyMedium),
|
||||||
|
trailing: Text(
|
||||||
|
formatCurrency(account.balance),
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: account.balance >= 0 ? kGreenAccent : kRedAccent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TxItem extends StatelessWidget {
|
||||||
|
final Transaction transaction;
|
||||||
|
const _TxItem({required this.transaction});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.only(bottom: 4),
|
||||||
|
child: ListTile(
|
||||||
|
dense: true,
|
||||||
|
leading: CircleAvatar(
|
||||||
|
radius: 18,
|
||||||
|
backgroundColor: kSurfaceElevated,
|
||||||
|
child: Icon(
|
||||||
|
transaction.amount >= 0 ? Icons.arrow_downward : Icons.arrow_upward,
|
||||||
|
size: 16,
|
||||||
|
color: transaction.amount >= 0 ? kGreenAccent : kRedAccent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(transaction.payee, style: Theme.of(context).textTheme.bodyMedium),
|
||||||
|
subtitle: Text(
|
||||||
|
DateFormat('dd/MM/yy').format(transaction.date),
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
trailing: Text(
|
||||||
|
formatCurrency(transaction.amount),
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: transaction.amount >= 0 ? kGreenAccent : kRedAccent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
55
lib/screens/transactions_screen.dart
Normal file
55
lib/screens/transactions_screen.dart
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import '../providers/db_provider.dart';
|
||||||
|
import '../theme/app_theme.dart';
|
||||||
|
|
||||||
|
class TransactionsScreen extends ConsumerWidget {
|
||||||
|
const TransactionsScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final all = ref.watch(allTransactionsProvider).valueOrNull ?? [];
|
||||||
|
|
||||||
|
return ListView.builder(
|
||||||
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 90),
|
||||||
|
itemCount: all.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final t = all[index];
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.only(bottom: 4),
|
||||||
|
child: ListTile(
|
||||||
|
dense: true,
|
||||||
|
leading: CircleAvatar(
|
||||||
|
radius: 18,
|
||||||
|
backgroundColor: kSurfaceElevated,
|
||||||
|
child: Icon(
|
||||||
|
t.amount >= 0 ? Icons.arrow_downward : Icons.arrow_upward,
|
||||||
|
size: 16,
|
||||||
|
color: t.amount >= 0 ? kGreenAccent : kRedAccent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(t.payee, style: Theme.of(context).textTheme.bodyMedium),
|
||||||
|
subtitle: Text(
|
||||||
|
DateFormat('dd/MM/yy').format(t.date),
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
trailing: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
formatCurrency(t.amount),
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: t.amount >= 0 ? kGreenAccent : kRedAccent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
144
lib/theme/app_theme.dart
Normal file
144
lib/theme/app_theme.dart
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
|
||||||
|
// ── Couleurs custom ───────────────────────────────────────────────────────────
|
||||||
|
// Fond très sombre mais pas noir, vert fluo pour accent, elevation uniquement.
|
||||||
|
const kSurfaceBg = Color(0xFF12161C);
|
||||||
|
const kSurfaceCard = Color(0xFF1A1E26);
|
||||||
|
const kSurfaceElevated = Color(0xFF232730);
|
||||||
|
const kGreenAccent = Color(0xFF00E676);
|
||||||
|
const kGreenAccentDim = Color(0xFF00B85C);
|
||||||
|
const kRedAccent = Color(0xFFFF5252);
|
||||||
|
const kTextPrimary = Color(0xFFE8EAED);
|
||||||
|
const kTextSecondary = Color(0xFF9AA0A6);
|
||||||
|
const kDivider = Color(0xFF2C3038);
|
||||||
|
|
||||||
|
final ThemeData ynabDarkTheme = ThemeData(
|
||||||
|
useMaterial3: true,
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
colorScheme: ColorScheme.fromSeed(
|
||||||
|
seedColor: kGreenAccent,
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
surface: kSurfaceBg,
|
||||||
|
primary: kGreenAccent,
|
||||||
|
secondary: kGreenAccentDim,
|
||||||
|
error: kRedAccent,
|
||||||
|
onSurface: kTextPrimary,
|
||||||
|
onSurfaceVariant: kTextSecondary,
|
||||||
|
outline: kDivider,
|
||||||
|
),
|
||||||
|
scaffoldBackgroundColor: kSurfaceBg,
|
||||||
|
cardTheme: CardThemeData(
|
||||||
|
color: kSurfaceCard,
|
||||||
|
elevation: 2,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
),
|
||||||
|
navigationRailTheme: NavigationRailThemeData(
|
||||||
|
backgroundColor: kSurfaceCard,
|
||||||
|
indicatorColor: kGreenAccent.withAlpha(40),
|
||||||
|
unselectedIconTheme: const IconThemeData(color: kTextSecondary),
|
||||||
|
selectedIconTheme: const IconThemeData(color: kGreenAccent),
|
||||||
|
unselectedLabelTextStyle: GoogleFonts.inter(
|
||||||
|
color: kTextSecondary,
|
||||||
|
fontSize: 11,
|
||||||
|
),
|
||||||
|
selectedLabelTextStyle: GoogleFonts.inter(
|
||||||
|
color: kGreenAccent,
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
labelType: NavigationRailLabelType.none,
|
||||||
|
),
|
||||||
|
appBarTheme: AppBarTheme(
|
||||||
|
backgroundColor: kSurfaceBg,
|
||||||
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: 1,
|
||||||
|
centerTitle: false,
|
||||||
|
titleTextStyle: GoogleFonts.inter(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: kTextPrimary,
|
||||||
|
),
|
||||||
|
iconTheme: const IconThemeData(color: kTextSecondary),
|
||||||
|
),
|
||||||
|
textTheme: GoogleFonts.interTextTheme(
|
||||||
|
ThemeData.dark().textTheme.copyWith(
|
||||||
|
headlineLarge: const TextStyle(color: kTextPrimary),
|
||||||
|
headlineMedium: const TextStyle(color: kTextPrimary),
|
||||||
|
headlineSmall: const TextStyle(color: kTextPrimary),
|
||||||
|
titleLarge: const TextStyle(color: kTextPrimary),
|
||||||
|
titleMedium: const TextStyle(color: kTextPrimary),
|
||||||
|
titleSmall: const TextStyle(color: kTextPrimary),
|
||||||
|
bodyLarge: const TextStyle(color: kTextPrimary),
|
||||||
|
bodyMedium: const TextStyle(color: kTextPrimary, fontSize: 14),
|
||||||
|
bodySmall: const TextStyle(color: kTextSecondary, fontSize: 12),
|
||||||
|
labelLarge: const TextStyle(color: kTextPrimary),
|
||||||
|
labelMedium: const TextStyle(color: kTextSecondary),
|
||||||
|
labelSmall: const TextStyle(color: kTextSecondary),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
dividerTheme: const DividerThemeData(color: kDivider, thickness: 1),
|
||||||
|
floatingActionButtonTheme: FloatingActionButtonThemeData(
|
||||||
|
backgroundColor: kGreenAccent,
|
||||||
|
foregroundColor: kSurfaceBg,
|
||||||
|
elevation: 4,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||||
|
),
|
||||||
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
|
filled: true,
|
||||||
|
fillColor: kSurfaceElevated,
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: BorderSide.none,
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: const BorderSide(color: kGreenAccent, width: 1.5),
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||||
|
),
|
||||||
|
bottomSheetTheme: const BottomSheetThemeData(
|
||||||
|
backgroundColor: kSurfaceCard,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
sliderTheme: SliderThemeData(
|
||||||
|
activeTrackColor: kGreenAccent,
|
||||||
|
thumbColor: kGreenAccent,
|
||||||
|
inactiveTrackColor: kDivider,
|
||||||
|
overlayColor: kGreenAccent.withAlpha(30),
|
||||||
|
),
|
||||||
|
dialogTheme: DialogThemeData(
|
||||||
|
backgroundColor: kSurfaceCard,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
String formatCurrency(double amount) {
|
||||||
|
final sign = amount < 0 ? '-' : '';
|
||||||
|
final abs = amount.abs();
|
||||||
|
final parts = abs.toStringAsFixed(2).split('.');
|
||||||
|
final intPart = parts[0];
|
||||||
|
final decPart = parts[1];
|
||||||
|
final buf = StringBuffer();
|
||||||
|
int count = 0;
|
||||||
|
for (int i = intPart.length - 1; i >= 0; i--) {
|
||||||
|
if (count > 0 && count % 3 == 0) buf.write(' ');
|
||||||
|
buf.write(intPart[i]);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
final formatted = '${buf.toString().split('').reversed.join()},$decPart';
|
||||||
|
return '$sign$formatted €';
|
||||||
|
}
|
||||||
|
|
||||||
|
String formatCurrencyShort(double amount) {
|
||||||
|
final abs = amount.abs();
|
||||||
|
if (abs >= 1000000) return '${(abs / 1000000).toStringAsFixed(1)} M';
|
||||||
|
if (abs >= 1000) return '${(abs / 1000).toStringAsFixed(1)} k';
|
||||||
|
return '${abs.toStringAsFixed(2)} €';
|
||||||
|
}
|
||||||
954
pubspec.lock
Normal file
954
pubspec.lock
Normal file
|
|
@ -0,0 +1,954 @@
|
||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
_fe_analyzer_shared:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: _fe_analyzer_shared
|
||||||
|
sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "85.0.0"
|
||||||
|
analyzer:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: analyzer
|
||||||
|
sha256: f4ad0fea5f102201015c9aae9d93bc02f75dd9491529a8c21f88d17a8523d44c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.6.0"
|
||||||
|
analyzer_plugin:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: analyzer_plugin
|
||||||
|
sha256: a5ab7590c27b779f3d4de67f31c4109dbe13dd7339f86461a6f2a8ab2594d8ce
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.13.4"
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.7.0"
|
||||||
|
async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: async
|
||||||
|
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.13.1"
|
||||||
|
boolean_selector:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: boolean_selector
|
||||||
|
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
build:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build
|
||||||
|
sha256: "51dc711996cbf609b90cbe5b335bbce83143875a9d58e4b5c6d3c4f684d3dda7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.4"
|
||||||
|
build_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_config
|
||||||
|
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
build_daemon:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_daemon
|
||||||
|
sha256: fd754058c342243718d5171a95f352cfc9fcf0cba8cfa26df67cb13a5836db78
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.2"
|
||||||
|
build_resolvers:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_resolvers
|
||||||
|
sha256: ee4257b3f20c0c90e72ed2b57ad637f694ccba48839a821e87db762548c22a62
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.4"
|
||||||
|
build_runner:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: build_runner
|
||||||
|
sha256: "382a4d649addbfb7ba71a3631df0ec6a45d5ab9b098638144faf27f02778eb53"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.4"
|
||||||
|
build_runner_core:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_runner_core
|
||||||
|
sha256: "85fbbb1036d576d966332a3f5ce83f2ce66a40bea1a94ad2d5fc29a19a0d3792"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.1.2"
|
||||||
|
built_collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: built_collection
|
||||||
|
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.1"
|
||||||
|
built_value:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: built_value
|
||||||
|
sha256: "34e4067d30ce212937df995f03b69992eea683539ceeac7f679a1f1eba055b56"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.12.6"
|
||||||
|
characters:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: characters
|
||||||
|
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.1"
|
||||||
|
charcode:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: charcode
|
||||||
|
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
|
checked_yaml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: checked_yaml
|
||||||
|
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.4"
|
||||||
|
ci:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ci
|
||||||
|
sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.0"
|
||||||
|
cli_util:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cli_util
|
||||||
|
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.2"
|
||||||
|
clock:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: clock
|
||||||
|
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
code_assets:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: code_assets
|
||||||
|
sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
code_builder:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: code_builder
|
||||||
|
sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.11.1"
|
||||||
|
collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.19.1"
|
||||||
|
convert:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: convert
|
||||||
|
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.2"
|
||||||
|
crypto:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.7"
|
||||||
|
cupertino_icons:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: cupertino_icons
|
||||||
|
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.9"
|
||||||
|
custom_lint:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: custom_lint
|
||||||
|
sha256: "9656925637516c5cf0f5da018b33df94025af2088fe09c8ae2ca54c53f2d9a84"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.6"
|
||||||
|
custom_lint_builder:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: custom_lint_builder
|
||||||
|
sha256: "6cdc8e87e51baaaba9c43e283ed8d28e59a0c4732279df62f66f7b5984655414"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.6"
|
||||||
|
custom_lint_core:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: custom_lint_core
|
||||||
|
sha256: "31110af3dde9d29fb10828ca33f1dce24d2798477b167675543ce3d208dee8be"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.5"
|
||||||
|
custom_lint_visitor:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: custom_lint_visitor
|
||||||
|
sha256: "4a86a0d8415a91fbb8298d6ef03e9034dc8e323a599ddc4120a0e36c433983a2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0+7.7.0"
|
||||||
|
dart_style:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dart_style
|
||||||
|
sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.1"
|
||||||
|
drift:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: drift
|
||||||
|
sha256: "540cf382a3bfa99b76e51514db5b0ebcd81ce3679b7c1c9cb9478ff3735e47a1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.28.2"
|
||||||
|
drift_dev:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: drift_dev
|
||||||
|
sha256: "68c138e884527d2bd61df2ade276c3a144df84d1adeb0ab8f3196b5afe021bd4"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.28.0"
|
||||||
|
drift_flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: drift_flutter
|
||||||
|
sha256: b7534bf320aac5213259aac120670ba67b63a1fd010505babc436ff86083818f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.7"
|
||||||
|
equatable:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: equatable
|
||||||
|
sha256: "3bce007a596ff8b3119c45d68aaef631272537c03d30e5d4534dd24bf4c5eaa2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
fake_async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fake_async
|
||||||
|
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.3"
|
||||||
|
ffi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi
|
||||||
|
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.1"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
fl_chart:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: fl_chart
|
||||||
|
sha256: "00b74ae680df6b1135bdbea00a7d1fc072a9180b7c3f3702e4b19a9943f5ed7d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.66.2"
|
||||||
|
flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_lints:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_lints
|
||||||
|
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.0"
|
||||||
|
flutter_riverpod:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_riverpod
|
||||||
|
sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.6.1"
|
||||||
|
flutter_slidable:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_slidable
|
||||||
|
sha256: a857de7ea701f276fd6a6c4c67ae885b60729a3449e42766bb0e655171042801
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.2"
|
||||||
|
flutter_test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_web_plugins:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
freezed_annotation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: freezed_annotation
|
||||||
|
sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
|
frontend_server_client:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: frontend_server_client
|
||||||
|
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.0"
|
||||||
|
glob:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: glob
|
||||||
|
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.3"
|
||||||
|
go_router:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: go_router
|
||||||
|
sha256: b465e99ce64ba75e61c8c0ce3d87b66d8ac07f0b35d0a7e0263fcfc10f99e836
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "13.2.5"
|
||||||
|
google_fonts:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: google_fonts
|
||||||
|
sha256: ba03d03bcaa2f6cb7bd920e3b5027181db75ab524f8891c8bc3aa603885b8055
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.3.3"
|
||||||
|
graphs:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: graphs
|
||||||
|
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
hooks:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: hooks
|
||||||
|
sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.2"
|
||||||
|
hotreloader:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: hotreloader
|
||||||
|
sha256: "66871df468fc24eee81f1a0a7cb98acc104716f9b7376d355437b48d633c4ebf"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.4.0"
|
||||||
|
http:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.6.0"
|
||||||
|
http_multi_server:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_multi_server
|
||||||
|
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.2"
|
||||||
|
http_parser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_parser
|
||||||
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.2"
|
||||||
|
intl:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: intl
|
||||||
|
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.19.0"
|
||||||
|
io:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: io
|
||||||
|
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.5"
|
||||||
|
jni:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: jni
|
||||||
|
sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
jni_flutter:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: jni_flutter
|
||||||
|
sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.2"
|
||||||
|
json_annotation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: json_annotation
|
||||||
|
sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.12.0"
|
||||||
|
leak_tracker:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker
|
||||||
|
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "11.0.2"
|
||||||
|
leak_tracker_flutter_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_flutter_testing
|
||||||
|
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.10"
|
||||||
|
leak_tracker_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_testing
|
||||||
|
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
|
lints:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: lints
|
||||||
|
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.1"
|
||||||
|
logging:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: logging
|
||||||
|
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
|
matcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: matcher
|
||||||
|
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.19"
|
||||||
|
material_color_utilities:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_color_utilities
|
||||||
|
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.13.0"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.18.0"
|
||||||
|
mime:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: mime
|
||||||
|
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
|
objective_c:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: objective_c
|
||||||
|
sha256: "6cb691c686fa2838c6deb34980d426145c2a5d537491cb83d463c33cdbc726ed"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.4.1"
|
||||||
|
package_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_config
|
||||||
|
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
path:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path
|
||||||
|
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.9.1"
|
||||||
|
path_provider:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: path_provider
|
||||||
|
sha256: a7f4874f987173da295a61c181b8ee71dab59b332a486b391babf26a1b884825
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.6"
|
||||||
|
path_provider_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_android
|
||||||
|
sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.1"
|
||||||
|
path_provider_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_foundation
|
||||||
|
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.6.0"
|
||||||
|
path_provider_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_linux
|
||||||
|
sha256: "58c2005f147315b11e9b4a7bc889cd5203e250cba8e3f012dae259b4972b5c16"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.2"
|
||||||
|
path_provider_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_platform_interface
|
||||||
|
sha256: "484838772624c3a4b94f1e44a3e19897fee738f2d5c4ce448443b0417f7c9dda"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.3"
|
||||||
|
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:
|
||||||
|
name: plugin_platform_interface
|
||||||
|
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.8"
|
||||||
|
pool:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pool
|
||||||
|
sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.2"
|
||||||
|
pub_semver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pub_semver
|
||||||
|
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
pubspec_parse:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pubspec_parse
|
||||||
|
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.0"
|
||||||
|
recase:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: recase
|
||||||
|
sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.0"
|
||||||
|
record_use:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: record_use
|
||||||
|
sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.0"
|
||||||
|
riverpod:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: riverpod
|
||||||
|
sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.6.1"
|
||||||
|
riverpod_analyzer_utils:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: riverpod_analyzer_utils
|
||||||
|
sha256: "03a17170088c63aab6c54c44456f5ab78876a1ddb6032ffde1662ddab4959611"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.10"
|
||||||
|
riverpod_annotation:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: riverpod_annotation
|
||||||
|
sha256: e14b0bf45b71326654e2705d462f21b958f987087be850afd60578fcd502d1b8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.6.1"
|
||||||
|
riverpod_generator:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: riverpod_generator
|
||||||
|
sha256: "44a0992d54473eb199ede00e2260bd3c262a86560e3c6f6374503d86d0580e36"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.6.5"
|
||||||
|
riverpod_lint:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: riverpod_lint
|
||||||
|
sha256: "89a52b7334210dbff8605c3edf26cfe69b15062beed5cbfeff2c3812c33c9e35"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.6.5"
|
||||||
|
rxdart:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: rxdart
|
||||||
|
sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.28.0"
|
||||||
|
shelf:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf
|
||||||
|
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.2"
|
||||||
|
shelf_web_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_web_socket
|
||||||
|
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
|
sky_engine:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
source_gen:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_gen
|
||||||
|
sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
|
source_span:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_span
|
||||||
|
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.10.2"
|
||||||
|
sqlite3:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqlite3
|
||||||
|
sha256: "3145bd74dcdb4fd6f5c6dda4d4e4490a8087d7f286a14dee5d37087290f0f8a2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.9.4"
|
||||||
|
sqlite3_flutter_libs:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqlite3_flutter_libs
|
||||||
|
sha256: eeb9e3a45207649076b808f8a5a74d68770d0b7f26ccef6d5f43106eee5375ad
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.42"
|
||||||
|
sqlparser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqlparser
|
||||||
|
sha256: "57090342af1ce32bb499aa641f4ecdd2d6231b9403cea537ac059e803cc20d67"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.41.2"
|
||||||
|
stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stack_trace
|
||||||
|
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.12.1"
|
||||||
|
state_notifier:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: state_notifier
|
||||||
|
sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
stream_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_channel
|
||||||
|
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
stream_transform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_transform
|
||||||
|
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
string_scanner:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: string_scanner
|
||||||
|
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.1"
|
||||||
|
term_glyph:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: term_glyph
|
||||||
|
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.2"
|
||||||
|
test_api:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_api
|
||||||
|
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.11"
|
||||||
|
timing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: timing
|
||||||
|
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
|
typed_data:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: typed_data
|
||||||
|
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
|
uuid:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: uuid
|
||||||
|
sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.5.3"
|
||||||
|
vector_math:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_math
|
||||||
|
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
vm_service:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vm_service
|
||||||
|
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "15.2.0"
|
||||||
|
watcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: watcher
|
||||||
|
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web
|
||||||
|
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
web_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web_socket
|
||||||
|
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
|
web_socket_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web_socket_channel
|
||||||
|
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.3"
|
||||||
|
xdg_directories:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xdg_directories
|
||||||
|
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
|
yaml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: yaml
|
||||||
|
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.3"
|
||||||
|
sdks:
|
||||||
|
dart: ">=3.12.2 <4.0.0"
|
||||||
|
flutter: ">=3.38.4"
|
||||||
34
pubspec.yaml
Normal file
34
pubspec.yaml
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
name: ynab_lite
|
||||||
|
description: "Budget manager — YNAB-like"
|
||||||
|
publish_to: 'none'
|
||||||
|
version: 1.0.0+1
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ^3.12.2
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
cupertino_icons: ^1.0.8
|
||||||
|
flutter_riverpod: ^2.5.1
|
||||||
|
riverpod_annotation: ^2.3.0
|
||||||
|
drift: ^2.20.0
|
||||||
|
drift_flutter: ^0.2.0
|
||||||
|
path_provider: ^2.1.3
|
||||||
|
go_router: ^13.0.1
|
||||||
|
intl: ^0.19.0
|
||||||
|
fl_chart: ^0.66.2
|
||||||
|
flutter_slidable: ^3.1.1
|
||||||
|
google_fonts: ^6.1.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_lints: ^5.0.0
|
||||||
|
build_runner: ^2.4.9
|
||||||
|
drift_dev: ^2.20.0
|
||||||
|
riverpod_generator: ^2.4.0
|
||||||
|
riverpod_lint: ^2.3.0
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
||||||
21
test/widget_test.dart
Normal file
21
test/widget_test.dart
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:ynab_lite/app.dart';
|
||||||
|
import 'package:ynab_lite/theme/app_theme.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('App renders dashboard by default', (tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
const ProviderScope(
|
||||||
|
child: MaterialApp(
|
||||||
|
theme: ynabDarkTheme,
|
||||||
|
home: YnabApp(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text('Tableau de bord'), findsOneWidget);
|
||||||
|
expect(find.text('Solde total'), findsOneWidget);
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue