├── .envrc
├── metadata
├── build_number.txt
├── title.txt
├── release_notes.txt
├── short_description.txt
└── full_description.txt
├── android
├── Gemfile
├── fastlane
│ ├── metadata
│ │ └── android
│ │ │ └── en-US
│ │ │ ├── changelogs
│ │ │ ├── 92.txt
│ │ │ ├── 93.txt
│ │ │ ├── 94.txt
│ │ │ ├── 95.txt
│ │ │ ├── 96.txt
│ │ │ ├── 31.txt
│ │ │ ├── 33.txt
│ │ │ ├── 34.txt
│ │ │ ├── 35.txt
│ │ │ ├── 90.txt
│ │ │ ├── 91.txt
│ │ │ ├── 36.txt
│ │ │ ├── 37.txt
│ │ │ ├── 39.txt
│ │ │ ├── 40.txt
│ │ │ ├── 41.txt
│ │ │ ├── 42.txt
│ │ │ ├── 43.txt
│ │ │ ├── 44.txt
│ │ │ ├── 45.txt
│ │ │ ├── 46.txt
│ │ │ ├── 47.txt
│ │ │ ├── 48.txt
│ │ │ ├── 49.txt
│ │ │ ├── 50.txt
│ │ │ ├── 51.txt
│ │ │ ├── 52.txt
│ │ │ ├── 53.txt
│ │ │ ├── 54.txt
│ │ │ ├── 55.txt
│ │ │ ├── 56.txt
│ │ │ ├── 57.txt
│ │ │ ├── 58.txt
│ │ │ ├── 59.txt
│ │ │ ├── 60.txt
│ │ │ ├── 61.txt
│ │ │ ├── 62.txt
│ │ │ ├── 63.txt
│ │ │ ├── 64.txt
│ │ │ ├── 65.txt
│ │ │ ├── 66.txt
│ │ │ ├── 67.txt
│ │ │ ├── 68.txt
│ │ │ ├── 69.txt
│ │ │ ├── 70.txt
│ │ │ ├── 71.txt
│ │ │ ├── 72.txt
│ │ │ ├── 73.txt
│ │ │ ├── 74.txt
│ │ │ ├── 75.txt
│ │ │ ├── 76.txt
│ │ │ ├── 77.txt
│ │ │ ├── 78.txt
│ │ │ ├── 79.txt
│ │ │ ├── 80.txt
│ │ │ ├── 81.txt
│ │ │ ├── 87.txt
│ │ │ ├── 88.txt
│ │ │ └── 89.txt
│ │ │ ├── title.txt
│ │ │ ├── full_description.txt
│ │ │ ├── short_description.txt
│ │ │ └── images
│ │ │ ├── icon.jpg
│ │ │ └── phoneScreenshots
│ │ │ ├── Pixel_6_01.png
│ │ │ ├── Pixel_6_02.png
│ │ │ ├── Pixel_6_03.png
│ │ │ └── Pixel_6_04.png
│ ├── Appfile
│ ├── README.md
│ └── Fastfile
├── app
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── drawable-hdpi
│ │ │ │ │ ├── ic_launcher_background.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── drawable-mdpi
│ │ │ │ │ ├── ic_launcher_background.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── drawable-xhdpi
│ │ │ │ │ ├── ic_launcher_background.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── drawable-xxhdpi
│ │ │ │ │ ├── ic_launcher_background.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── drawable-xxxhdpi
│ │ │ │ │ ├── ic_launcher_background.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── mipmap-anydpi-v26
│ │ │ │ │ └── ic_launcher.xml
│ │ │ │ ├── drawable
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable-v21
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── values
│ │ │ │ │ └── styles.xml
│ │ │ │ └── values-night
│ │ │ │ │ └── styles.xml
│ │ │ ├── kotlin
│ │ │ │ └── co
│ │ │ │ │ └── timsmart
│ │ │ │ │ └── vouchervault
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ └── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── .gitignore
├── build.gradle
└── settings.gradle
├── ios
├── Runner
│ ├── Runner-Bridging-Header.h
│ ├── Assets.xcassets
│ │ ├── LaunchImage.imageset
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ ├── README.md
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── Main.storyboard
│ │ └── LaunchScreen.storyboard
│ └── Info.plist
├── Flutter
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── AppFrameworkInfo.plist
├── Runner.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
├── .gitignore
└── Podfile
├── lib
├── voucher_form
│ ├── barcode_scanner
│ │ ├── providers
│ │ │ ├── index.dart
│ │ │ └── camera.dart
│ │ ├── lib
│ │ │ ├── index.dart
│ │ │ └── camera_utils.dart
│ │ ├── models
│ │ │ ├── index.dart
│ │ │ ├── barcode_result.dart
│ │ │ ├── ml_context.dart
│ │ │ └── ml_error.dart
│ │ ├── widgets
│ │ │ ├── index.dart
│ │ │ ├── barcode_button.g.dart
│ │ │ ├── barcode_scanner_field.g.dart
│ │ │ ├── scanner_dialog.g.dart
│ │ │ ├── barcode_button.dart
│ │ │ ├── barcode_scanner_field.dart
│ │ │ └── scanner_dialog.dart
│ │ ├── index.dart
│ │ └── service.dart
│ ├── widgets
│ │ ├── index.dart
│ │ ├── dialog.g.dart
│ │ ├── form.g.dart
│ │ └── dialog.dart
│ └── index.dart
├── vouchers
│ ├── models
│ │ ├── index.dart
│ │ ├── state.dart
│ │ ├── voucher.g.dart
│ │ └── voucher.dart
│ ├── menu
│ │ ├── index.dart
│ │ ├── vouchers_menu_container.g.dart
│ │ ├── vouchers_menu.g.dart
│ │ ├── vouchers_menu_container.dart
│ │ └── vouchers_menu.dart
│ ├── list
│ │ ├── index.dart
│ │ ├── vouchers_list_container.g.dart
│ │ ├── voucher_list.g.dart
│ │ ├── voucher_item.g.dart
│ │ ├── voucher_list.dart
│ │ ├── vouchers_list_container.dart
│ │ └── voucher_item.dart
│ ├── dialog
│ │ ├── index.dart
│ │ ├── voucher_spend_dialog.g.dart
│ │ ├── voucher_dialog_container.g.dart
│ │ ├── voucher_spend_dialog.dart
│ │ ├── voucher_dialog.g.dart
│ │ └── voucher_dialog_container.dart
│ ├── index.dart
│ ├── vouchers_screen.g.dart
│ ├── vouchers_screen.dart
│ └── service.dart
├── auth
│ ├── index.dart
│ ├── auth_screen.g.dart
│ ├── auth_screen.dart
│ ├── model.g.dart
│ ├── model.dart
│ └── service.dart
├── shared
│ ├── scaffold
│ │ ├── scaffold.dart
│ │ ├── app_scaffold_simple.dart
│ │ ├── app_scaffold_simple.g.dart
│ │ ├── app_scaffold.g.dart
│ │ └── app_scaffold.dart
│ └── voucher_details
│ │ ├── voucher_details.g.dart
│ │ └── voucher_details.dart
├── hooks
│ ├── index.dart
│ ├── use_system_overlay_style.dart
│ ├── use_full_brightness.dart
│ └── use_route_observer.dart
├── app
│ ├── colors.dart
│ ├── index.dart
│ ├── settings.dart
│ ├── atoms.dart
│ ├── settings.g.dart
│ ├── voucher_vault_app.g.dart
│ ├── theme.dart
│ └── voucher_vault_app.dart
├── lib
│ ├── lib.dart
│ ├── datetime.dart
│ ├── intersperse.dart
│ ├── option.dart
│ ├── milliunits.dart
│ ├── barcode.dart
│ └── files.dart
├── l10n
│ ├── app_en.arb
│ └── app_fr.arb
└── main.dart
├── assets
├── icon
│ ├── icon.png
│ ├── icon_bg.png
│ └── icon_fg.png
└── fonts
│ ├── AlegreyaSans-Black.ttf
│ ├── AlegreyaSans-Bold.ttf
│ ├── AlegreyaSans-Italic.ttf
│ ├── AlegreyaSans-Regular.ttf
│ ├── AlegreyaSans-BoldItalic.ttf
│ ├── AlegreyaSans-ExtraBold.ttf
│ ├── AlegreyaSans-BlackItalic.ttf
│ └── AlegreyaSans-ExtraBoldItalic.ttf
├── l10n.yaml
├── .vscode
├── settings.json
└── launch.json
├── .sops.yaml
├── README.md
├── analysis_options.yaml
├── test
└── widget_test.dart
├── tools
└── screenshots.dart
├── .devcontainer
├── devcontainer.json
└── Dockerfile
├── .gitignore
├── .metadata
├── Makefile
├── flake.nix
├── nix
└── flutter.nix
├── flake.lock
├── test_driver
├── main.dart
└── main_test.dart
├── pubspec.yaml
├── PRIVACY.md
└── secrets
└── fastlane-google-key
/.envrc:
--------------------------------------------------------------------------------
1 | use flake;
--------------------------------------------------------------------------------
/metadata/build_number.txt:
--------------------------------------------------------------------------------
1 | 96
2 |
--------------------------------------------------------------------------------
/metadata/title.txt:
--------------------------------------------------------------------------------
1 | Voucher Vault
2 |
--------------------------------------------------------------------------------
/metadata/release_notes.txt:
--------------------------------------------------------------------------------
1 | Refreshed look and feel!
--------------------------------------------------------------------------------
/android/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gem "fastlane"
4 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/92.txt:
--------------------------------------------------------------------------------
1 | Update look and feel.
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/93.txt:
--------------------------------------------------------------------------------
1 | Update look and feel.
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/94.txt:
--------------------------------------------------------------------------------
1 | Update look and feel.
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/95.txt:
--------------------------------------------------------------------------------
1 | Update look and feel.
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/96.txt:
--------------------------------------------------------------------------------
1 | Update look and feel.
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/31.txt:
--------------------------------------------------------------------------------
1 | Released on Google Play!
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/33.txt:
--------------------------------------------------------------------------------
1 | Released on Google Play!
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/34.txt:
--------------------------------------------------------------------------------
1 | Released on Google Play!
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/35.txt:
--------------------------------------------------------------------------------
1 | Released on Google Play!
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/title.txt:
--------------------------------------------------------------------------------
1 | ../../../../../metadata/title.txt
--------------------------------------------------------------------------------
/lib/voucher_form/barcode_scanner/providers/index.dart:
--------------------------------------------------------------------------------
1 | export 'camera.dart';
2 |
--------------------------------------------------------------------------------
/lib/vouchers/models/index.dart:
--------------------------------------------------------------------------------
1 | export 'state.dart';
2 | export 'voucher.dart';
3 |
--------------------------------------------------------------------------------
/metadata/short_description.txt:
--------------------------------------------------------------------------------
1 | A vault for all your vouchers and gift cards.
2 |
--------------------------------------------------------------------------------
/lib/voucher_form/widgets/index.dart:
--------------------------------------------------------------------------------
1 | export 'dialog.dart';
2 | export 'form.dart';
3 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/90.txt:
--------------------------------------------------------------------------------
1 | Bug fixes and performance improvements.
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/91.txt:
--------------------------------------------------------------------------------
1 | Bug fixes and performance improvements.
--------------------------------------------------------------------------------
/assets/icon/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/assets/icon/icon.png
--------------------------------------------------------------------------------
/lib/auth/index.dart:
--------------------------------------------------------------------------------
1 | export 'auth_screen.dart';
2 | export 'model.dart';
3 | export 'service.dart';
4 |
--------------------------------------------------------------------------------
/lib/voucher_form/index.dart:
--------------------------------------------------------------------------------
1 | export 'barcode_scanner/index.dart';
2 | export 'widgets/index.dart';
3 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/full_description.txt:
--------------------------------------------------------------------------------
1 | ../../../../../metadata/full_description.txt
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/short_description.txt:
--------------------------------------------------------------------------------
1 | ../../../../../metadata/short_description.txt
--------------------------------------------------------------------------------
/assets/icon/icon_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/assets/icon/icon_bg.png
--------------------------------------------------------------------------------
/assets/icon/icon_fg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/assets/icon/icon_fg.png
--------------------------------------------------------------------------------
/lib/shared/scaffold/scaffold.dart:
--------------------------------------------------------------------------------
1 | export 'app_scaffold.dart';
2 | export 'app_scaffold_simple.dart';
3 |
--------------------------------------------------------------------------------
/lib/vouchers/menu/index.dart:
--------------------------------------------------------------------------------
1 | export 'vouchers_menu.dart';
2 | export 'vouchers_menu_container.dart';
3 |
--------------------------------------------------------------------------------
/l10n.yaml:
--------------------------------------------------------------------------------
1 | arb-dir: lib/l10n
2 | template-arb-file: app_en.arb
3 | output-localization-file: app_localizations.dart
--------------------------------------------------------------------------------
/lib/voucher_form/barcode_scanner/lib/index.dart:
--------------------------------------------------------------------------------
1 | export 'camera_utils.dart';
2 | export 'extraction.dart';
3 |
--------------------------------------------------------------------------------
/assets/fonts/AlegreyaSans-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/assets/fonts/AlegreyaSans-Black.ttf
--------------------------------------------------------------------------------
/assets/fonts/AlegreyaSans-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/assets/fonts/AlegreyaSans-Bold.ttf
--------------------------------------------------------------------------------
/assets/fonts/AlegreyaSans-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/assets/fonts/AlegreyaSans-Italic.ttf
--------------------------------------------------------------------------------
/assets/fonts/AlegreyaSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/assets/fonts/AlegreyaSans-Regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/AlegreyaSans-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/assets/fonts/AlegreyaSans-BoldItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/AlegreyaSans-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/assets/fonts/AlegreyaSans-ExtraBold.ttf
--------------------------------------------------------------------------------
/lib/vouchers/list/index.dart:
--------------------------------------------------------------------------------
1 | export 'voucher_item.dart';
2 | export 'voucher_list.dart';
3 | export 'vouchers_list_container.dart';
4 |
--------------------------------------------------------------------------------
/assets/fonts/AlegreyaSans-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/assets/fonts/AlegreyaSans-BlackItalic.ttf
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/lib/hooks/index.dart:
--------------------------------------------------------------------------------
1 | export 'use_full_brightness.dart';
2 | export 'use_route_observer.dart';
3 | export 'use_system_overlay_style.dart';
4 |
--------------------------------------------------------------------------------
/lib/voucher_form/barcode_scanner/models/index.dart:
--------------------------------------------------------------------------------
1 | export 'barcode_result.dart';
2 | export 'ml_context.dart';
3 | export 'ml_error.dart';
4 |
--------------------------------------------------------------------------------
/assets/fonts/AlegreyaSans-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/assets/fonts/AlegreyaSans-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/lib/app/colors.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class AppColors {
4 | static const lightGrey = Color(0xFFE5E5E5);
5 | }
6 |
--------------------------------------------------------------------------------
/lib/vouchers/dialog/index.dart:
--------------------------------------------------------------------------------
1 | export 'voucher_dialog.dart';
2 | export 'voucher_dialog_container.dart';
3 | export 'voucher_spend_dialog.dart';
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/lib/app/index.dart:
--------------------------------------------------------------------------------
1 | export 'atoms.dart';
2 | export 'colors.dart';
3 | export 'settings.dart';
4 | export 'theme.dart';
5 | export 'voucher_vault_app.dart';
6 |
--------------------------------------------------------------------------------
/lib/voucher_form/barcode_scanner/widgets/index.dart:
--------------------------------------------------------------------------------
1 | export 'barcode_button.dart';
2 | export 'barcode_scanner_field.dart';
3 | export 'scanner_dialog.dart';
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/images/icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/fastlane/metadata/android/en-US/images/icon.jpg
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/36.txt:
--------------------------------------------------------------------------------
1 | - Fixed bug with voucher form
2 | - Open scanner immediately
3 | - Added smart scanning features (english only)
4 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/37.txt:
--------------------------------------------------------------------------------
1 | - Fixed bug with voucher form
2 | - Open scanner immediately
3 | - Added smart scanning features (english only)
4 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/39.txt:
--------------------------------------------------------------------------------
1 | - Fixed bug with voucher form
2 | - Open scanner immediately
3 | - Added smart scanning features (english only)
4 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.exclude": {
3 | "**/*.freezed.dart": true,
4 | "**/*.g.dart": true
5 | },
6 | "dart.flutterSdkPath": ".flutter/flutter"
7 | }
8 |
--------------------------------------------------------------------------------
/lib/lib/lib.dart:
--------------------------------------------------------------------------------
1 | export 'barcode.dart';
2 | export 'datetime.dart';
3 | export 'files.dart';
4 | export 'intersperse.dart';
5 | export 'milliunits.dart';
6 | export 'option.dart';
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/40.txt:
--------------------------------------------------------------------------------
1 | - Immediately open scanner when adding a new voucher
2 | - Added smart scanning features (english only)
3 | - Fixed some bugs
4 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/41.txt:
--------------------------------------------------------------------------------
1 | - Immediately open scanner when adding a new voucher
2 | - Added smart scanning features (english only)
3 | - Fixed some bugs
4 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/42.txt:
--------------------------------------------------------------------------------
1 | - Immediately open scanner when adding a new voucher
2 | - Added smart scanning features (english only)
3 | - Fixed some bugs
4 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/43.txt:
--------------------------------------------------------------------------------
1 | - Immediately open scanner when adding a new voucher
2 | - Added smart scanning features (english only)
3 | - Fixed some bugs
4 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/44.txt:
--------------------------------------------------------------------------------
1 | - Immediately open scanner when adding a new voucher
2 | - Added smart scanning features (english only)
3 | - Fixed some bugs
4 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/lib/voucher_form/barcode_scanner/index.dart:
--------------------------------------------------------------------------------
1 | export 'lib/index.dart';
2 | export 'models/index.dart';
3 | export 'providers/index.dart';
4 | export 'service.dart';
5 | export 'widgets/index.dart';
6 |
--------------------------------------------------------------------------------
/lib/lib/datetime.dart:
--------------------------------------------------------------------------------
1 | import 'package:dart_date/dart_date.dart';
2 |
3 | String formatExpires(DateTime dt) {
4 | dt = dt.endOfDay;
5 | return dt.isPast ? 'Expired' : dt.timeago(allowFromNow: true);
6 | }
7 |
--------------------------------------------------------------------------------
/lib/vouchers/index.dart:
--------------------------------------------------------------------------------
1 | export 'dialog/index.dart';
2 | export 'list/index.dart';
3 | export 'menu/index.dart';
4 | export 'models/index.dart';
5 | export 'service.dart';
6 | export 'vouchers_screen.dart';
7 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/images/phoneScreenshots/Pixel_6_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/fastlane/metadata/android/en-US/images/phoneScreenshots/Pixel_6_01.png
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/images/phoneScreenshots/Pixel_6_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/fastlane/metadata/android/en-US/images/phoneScreenshots/Pixel_6_02.png
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/images/phoneScreenshots/Pixel_6_03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/fastlane/metadata/android/en-US/images/phoneScreenshots/Pixel_6_03.png
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/images/phoneScreenshots/Pixel_6_04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tim-smart/vouchervault/HEAD/android/fastlane/metadata/android/en-US/images/phoneScreenshots/Pixel_6_04.png
--------------------------------------------------------------------------------
/.sops.yaml:
--------------------------------------------------------------------------------
1 | keys:
2 | - &admin_tim age1cqrnyaj8tcu6svafggn5f45juq5lnvghqlqsazvp5pvzt5wa2els9ak7pv
3 | creation_rules:
4 | - path_regex: secrets/[^/]+$
5 | key_groups:
6 | - age:
7 | - *admin_tim
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/co/timsmart/vouchervault/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package co.timsmart.vouchervault
2 |
3 | import io.flutter.embedding.android.FlutterFragmentActivity
4 |
5 | class MainActivity: FlutterFragmentActivity()
6 |
--------------------------------------------------------------------------------
/android/fastlane/Appfile:
--------------------------------------------------------------------------------
1 | json_key_file("./keys/google.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
2 | package_name("co.timsmart.vouchervault") # e.g. com.krausefx.app
3 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vouchervault
2 |
3 |
4 |
5 | A vault for all your vouchers and gift cards.
6 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/metadata/full_description.txt:
--------------------------------------------------------------------------------
1 | Voucher Vault helps you keep track of all your vouchers, gift cards and
2 | loyalty cards.
3 |
4 | Features include:
5 |
6 | * Supports several types of barcodes, including plain text
7 | * Keep track of expiry dates
8 | * Keep track of gift card balances
9 | * Assign each voucher a different color
10 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:flutter_lints/flutter.yaml
2 |
3 | analyzer:
4 | errors:
5 | no_wildcard_variable_uses: ignore
6 | exclude:
7 | - lib/**/*.freezed.dart
8 | - lib/**/*.g.dart
9 |
10 | linter:
11 | rules:
12 | library_prefixes: false
13 | prefer_function_declarations_over_variables: false
14 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | keys/
13 | **/*.keystore
14 | **/*.jks
15 |
--------------------------------------------------------------------------------
/lib/lib/intersperse.dart:
--------------------------------------------------------------------------------
1 | Iterable Function(Iterable) intersperse(T element) =>
2 | (iterable) sync* {
3 | final iterator = iterable.iterator;
4 | if (iterator.moveNext()) {
5 | yield iterator.current;
6 | while (iterator.moveNext()) {
7 | yield element;
8 | yield iterator.current;
9 | }
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/45.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/46.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/47.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/48.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/49.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/50.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/51.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/52.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/53.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/54.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/55.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/56.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/57.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/58.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/59.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/60.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/61.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/62.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/63.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/64.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/65.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/66.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/67.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/68.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/69.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/70.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/71.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/72.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/73.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/74.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/75.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/76.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/77.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/78.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/79.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/80.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/81.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/87.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/88.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/fastlane/metadata/android/en-US/changelogs/89.txt:
--------------------------------------------------------------------------------
1 | Smart scanning!
2 |
3 | In addition to scanning barcodes, the scanner also tries to extract the voucher's description, expiry date and balance.
4 | At this stage it only supports the English language.
5 |
6 | You can toggle this setting in the menu.
7 |
8 | Other changes:
9 | - Immediately open scanner when adding a new voucher
10 | - Fixed some bugs
11 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | rootProject.buildDir = "../build"
9 | subprojects {
10 | project.buildDir = "${rootProject.buildDir}/${project.name}"
11 | }
12 | subprojects {
13 | project.evaluationDependsOn(":app")
14 | }
15 |
16 | tasks.register("clean", Delete) {
17 | delete rootProject.buildDir
18 | }
19 |
--------------------------------------------------------------------------------
/lib/app/settings.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | part 'settings.freezed.dart';
4 | part 'settings.g.dart';
5 |
6 | @freezed
7 | class VoucherVaultSettings with _$VoucherVaultSettings {
8 | const factory VoucherVaultSettings({
9 | @Default(true) bool smartScan,
10 | }) = _VoucherVaultSettings;
11 |
12 | factory VoucherVaultSettings.fromJson(dynamic json) =>
13 | _$VoucherVaultSettingsFromJson(json);
14 | }
15 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @main
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | // import 'package:flutter_test/flutter_test.dart';
9 |
10 | void main() {}
11 |
--------------------------------------------------------------------------------
/lib/auth/auth_screen.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'auth_screen.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class AuthScreen extends HookWidget {
10 | const AuthScreen({Key? key}) : super(key: key);
11 |
12 | @override
13 | Widget build(BuildContext _context) => authScreen(_context);
14 | }
15 |
--------------------------------------------------------------------------------
/tools/screenshots.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:emulators/emulators.dart';
4 |
5 | Future main() async {
6 | final emu = await Emulators.build();
7 |
8 | // Shutdown all the running emulators
9 | await emu.shutdownAll();
10 |
11 | await emu.forEach(['Pixel_6'])((device) async {
12 | final p = await emu.drive(
13 | device,
14 | 'test_driver/main.dart',
15 | );
16 | stderr.addStream(p.stderr);
17 | await stdout.addStream(p.stdout);
18 | });
19 | }
20 |
--------------------------------------------------------------------------------
/lib/app/atoms.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_elemental/flutter_elemental.dart';
2 | import 'package:vouchervault/app/index.dart';
3 |
4 | final nucleusStorage = atom(
5 | (get) => get(runtimeAtom).runSyncOrThrow(storageLayer.access),
6 | );
7 |
8 | final appSettings = stateAtomWithStorage(
9 | const VoucherVaultSettings(),
10 | key: 'rp_persist_settingsProvider',
11 | storage: nucleusStorage,
12 | fromJson: VoucherVaultSettings.fromJson,
13 | toJson: (s) => s.toJson(),
14 | );
15 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/lib/vouchers/vouchers_screen.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'vouchers_screen.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class VouchersScreen extends StatelessWidget {
10 | const VouchersScreen({Key? key}) : super(key: key);
11 |
12 | @override
13 | Widget build(BuildContext _context) => _vouchersScreen(_context);
14 | }
15 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/vouchers/dialog/voucher_spend_dialog.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'voucher_spend_dialog.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class VoucherSpendDialog extends HookWidget {
10 | const VoucherSpendDialog({Key? key}) : super(key: key);
11 |
12 | @override
13 | Widget build(BuildContext _context) => _voucherSpendDialog(_context);
14 | }
15 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "vouchervault",
9 | "request": "launch",
10 | "type": "dart"
11 | },
12 | {
13 | "name": "vouchervault profile",
14 | "request": "launch",
15 | "type": "dart",
16 | "flutterMode": "profile"
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/lib/vouchers/menu/vouchers_menu_container.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'vouchers_menu_container.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class VouchersMenuContainer extends StatelessWidget {
10 | const VouchersMenuContainer({Key? key}) : super(key: key);
11 |
12 | @override
13 | Widget build(BuildContext _context) => vouchersMenuContainer();
14 | }
15 |
--------------------------------------------------------------------------------
/lib/vouchers/list/vouchers_list_container.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'vouchers_list_container.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class VouchersListContainer extends StatelessWidget {
10 | const VouchersListContainer({Key? key}) : super(key: key);
11 |
12 | @override
13 | Widget build(BuildContext _context) => _vouchersListContainer(_context);
14 | }
15 |
--------------------------------------------------------------------------------
/lib/shared/scaffold/app_scaffold_simple.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_hooks/flutter_hooks.dart';
3 | import 'package:functional_widget_annotation/functional_widget_annotation.dart';
4 | import 'package:vouchervault/hooks/index.dart';
5 |
6 | part 'app_scaffold_simple.g.dart';
7 |
8 | @hwidget
9 | Widget appScaffoldSimple(
10 | BuildContext context, {
11 | required Widget body,
12 | }) {
13 | final style = useSystemOverlayStyle();
14 | return AnnotatedRegion(
15 | value: style,
16 | child: Scaffold(body: body),
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/lib/lib/option.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_elemental/flutter_elemental.dart';
2 |
3 | Option optionOfString(String? s) =>
4 | Option.fromNullable(s).map((s) => s.trim()).filter((s) => s.isNotEmpty);
5 |
6 | final maybeParseInt = optionOfString
7 | .c((_) => _.flatMap((_) => Option.fromNullable(int.tryParse(_))));
8 |
9 | final maybeParseDouble = optionOfString
10 | .c((_) => _.flatMap((_) => Option.fromNullable(double.tryParse(_))));
11 |
12 | extension IfSomeListExt on Option {
13 | Iterable ifSomeList(Iterable Function(A a) f) => match(() => [], f);
14 | }
15 |
--------------------------------------------------------------------------------
/lib/voucher_form/barcode_scanner/models/barcode_result.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_elemental/flutter_elemental.dart';
2 | import 'package:freezed_annotation/freezed_annotation.dart';
3 | import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart';
4 |
5 | part 'barcode_result.freezed.dart';
6 |
7 | @freezed
8 | class BarcodeResult with _$BarcodeResult {
9 | const factory BarcodeResult({
10 | required Barcode barcode,
11 | @Default(Option.none()) Option merchant,
12 | @Default(Option.none()) Option balance,
13 | @Default(Option.none()) Option expires,
14 | }) = _BarcodeResult;
15 | }
16 |
--------------------------------------------------------------------------------
/lib/shared/scaffold/app_scaffold_simple.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'app_scaffold_simple.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class AppScaffoldSimple extends HookWidget {
10 | const AppScaffoldSimple({
11 | Key? key,
12 | required this.body,
13 | }) : super(key: key);
14 |
15 | final Widget body;
16 |
17 | @override
18 | Widget build(BuildContext _context) => appScaffoldSimple(
19 | _context,
20 | body: body,
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/lib/voucher_form/barcode_scanner/models/ml_context.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 | import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart';
3 | import 'package:google_mlkit_entity_extraction/google_mlkit_entity_extraction.dart';
4 | import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
5 |
6 | part 'ml_context.freezed.dart';
7 |
8 | @freezed
9 | class MlContext with _$MlContext {
10 | const factory MlContext({
11 | required TextRecognizer textRecognizer,
12 | required BarcodeScanner barcodeScanner,
13 | required EntityExtractor entityExtractor,
14 | }) = _MlContext;
15 | }
16 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.234.0/containers/dart
3 | {
4 | "name": "Flutter",
5 | "build": {
6 | "dockerfile": "Dockerfile"
7 | },
8 |
9 | // Set *default* container specific settings.json values on container create.
10 | "settings": {},
11 |
12 | // Add the IDs of extensions you want installed when the container is created.
13 | "extensions": ["dart-code.dart-code", "dart-code.flutter"]
14 |
15 | // Use 'forwardPorts' to make a list of ports inside the container available locally.
16 | // "forwardPorts": [],
17 | }
18 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
34 | **/dgph
--------------------------------------------------------------------------------
/lib/voucher_form/widgets/dialog.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'dialog.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class VoucherFormDialog extends HookWidget {
10 | const VoucherFormDialog({
11 | Key? key,
12 | this.initialValue = const Option.none(),
13 | }) : super(key: key);
14 |
15 | final Option initialValue;
16 |
17 | @override
18 | Widget build(BuildContext _context) => _voucherFormDialog(
19 | _context,
20 | initialValue: initialValue,
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/lib/app/settings.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'settings.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$VoucherVaultSettingsImpl _$$VoucherVaultSettingsImplFromJson(
10 | Map json) =>
11 | _$VoucherVaultSettingsImpl(
12 | smartScan: json['smartScan'] as bool? ?? true,
13 | );
14 |
15 | Map _$$VoucherVaultSettingsImplToJson(
16 | _$VoucherVaultSettingsImpl instance) =>
17 | {
18 | 'smartScan': instance.smartScan,
19 | };
20 |
--------------------------------------------------------------------------------
/lib/vouchers/dialog/voucher_dialog_container.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'voucher_dialog_container.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class VoucherDialogContainer extends HookWidget {
10 | const VoucherDialogContainer({
11 | Key? key,
12 | required this.voucher,
13 | }) : super(key: key);
14 |
15 | final Voucher voucher;
16 |
17 | @override
18 | Widget build(BuildContext _context) => _voucherDialogContainer(
19 | _context,
20 | voucher: voucher,
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/lib/lib/milliunits.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_elemental/flutter_elemental.dart';
2 | import 'package:vouchervault/lib/lib.dart';
3 |
4 | int millisFromDouble(double i) => (i * 1000).round();
5 | double millisToDouble(int units) => units / 1000.0;
6 | String millisToString(int i) => millisToDouble(i).toStringAsFixed(2);
7 |
8 | Option millisFromNullableDouble(double? i) =>
9 | Option.fromNullable(i).map(millisFromDouble);
10 |
11 | Option millisFromString(String? s) =>
12 | maybeParseDouble(s).map(millisFromDouble);
13 |
14 | Option maybeMillisToDouble(int? i) =>
15 | Option.fromNullable(i).map(millisToDouble);
16 |
17 | Option maybeMillisToString(int? i) =>
18 | Option.fromNullable(i).map(millisToString);
19 |
--------------------------------------------------------------------------------
/lib/vouchers/list/voucher_list.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'voucher_list.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class VoucherList extends StatelessWidget {
10 | const VoucherList({
11 | Key? key,
12 | required this.vouchers,
13 | required this.onPressed,
14 | }) : super(key: key);
15 |
16 | final IList vouchers;
17 |
18 | final void Function(Voucher) onPressed;
19 |
20 | @override
21 | Widget build(BuildContext _context) => _voucherList(
22 | vouchers: vouchers,
23 | onPressed: onPressed,
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/lib/voucher_form/barcode_scanner/models/ml_error.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | part 'ml_error.freezed.dart';
4 |
5 | @freezed
6 | class MlError with _$MlError {
7 | const MlError._();
8 |
9 | const factory MlError.barcodeNotFound() = _MlErrorBarcodeNotFound;
10 | const factory MlError.pickerError(String message) = _MlErrorPicker;
11 | const factory MlError.mlkitError({
12 | required String op,
13 | required dynamic err,
14 | }) = _MlErrorMlKit;
15 |
16 | String get friendlyMessage => when(
17 | barcodeNotFound: () => 'Could not find a barcode',
18 | pickerError: (msg) => 'Could not load image: $msg',
19 | mlkitError: (op, err) => 'Could not process image in method: $op',
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/lib/voucher_form/widgets/form.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'form.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class VoucherForm extends StatelessWidget {
10 | const VoucherForm({
11 | Key? key,
12 | required this.formKey,
13 | required this.initialValue,
14 | }) : super(key: key);
15 |
16 | final GlobalKey formKey;
17 |
18 | final Voucher initialValue;
19 |
20 | @override
21 | Widget build(BuildContext _context) => voucherForm(
22 | _context,
23 | formKey: formKey,
24 | initialValue: initialValue,
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | def flutterSdkPath = {
3 | def properties = new Properties()
4 | file("local.properties").withInputStream { properties.load(it) }
5 | def flutterSdkPath = properties.getProperty("flutter.sdk")
6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7 | return flutterSdkPath
8 | }()
9 |
10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
11 |
12 | repositories {
13 | google()
14 | mavenCentral()
15 | gradlePluginPortal()
16 | }
17 | }
18 |
19 | plugins {
20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0"
21 | id "com.android.application" version "7.3.0" apply false
22 | id "org.jetbrains.kotlin.android" version "1.7.10" apply false
23 | }
24 |
25 | include ":app"
26 |
--------------------------------------------------------------------------------
/lib/vouchers/list/voucher_item.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'voucher_item.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class VoucherItem extends StatelessWidget {
10 | const VoucherItem({
11 | Key? key,
12 | required this.voucher,
13 | required this.onPressed,
14 | this.bottomPadding = 0,
15 | }) : super(key: key);
16 |
17 | final Voucher voucher;
18 |
19 | final void Function() onPressed;
20 |
21 | final double bottomPadding;
22 |
23 | @override
24 | Widget build(BuildContext _context) => voucherItem(
25 | _context,
26 | voucher: voucher,
27 | onPressed: onPressed,
28 | bottomPadding: bottomPadding,
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/lib/app/voucher_vault_app.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'voucher_vault_app.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class VoucherVaultApp extends StatelessWidget {
10 | const VoucherVaultApp({
11 | Key? key,
12 | this.initialValues = const [],
13 | }) : super(key: key);
14 |
15 | final List> initialValues;
16 |
17 | @override
18 | Widget build(BuildContext _context) => _voucherVaultApp(
19 | _context,
20 | initialValues: initialValues,
21 | );
22 | }
23 |
24 | class _App extends StatelessWidget {
25 | const _App({Key? key}) : super(key: key);
26 |
27 | @override
28 | Widget build(BuildContext _context) => __app();
29 | }
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 | android/fastlane/report.xml
43 |
44 | # macos platform
45 | /macos
46 | .direnv/
47 | /.flutter/
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 12.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/vouchers/menu/vouchers_menu.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'vouchers_menu.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class VouchersMenu extends StatelessWidget {
10 | const VouchersMenu({
11 | Key? key,
12 | required this.onSelected,
13 | required this.values,
14 | required this.disabled,
15 | }) : super(key: key);
16 |
17 | final void Function(VouchersMenuAction) onSelected;
18 |
19 | final Map values;
20 |
21 | final Set disabled;
22 |
23 | @override
24 | Widget build(BuildContext _context) => vouchersMenu(
25 | onSelected: onSelected,
26 | values: values,
27 | disabled: disabled,
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/lib/voucher_form/barcode_scanner/widgets/barcode_button.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'barcode_button.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class BarcodeButton extends StatelessWidget {
10 | const BarcodeButton({
11 | Key? key,
12 | required this.barcodeType,
13 | required this.data,
14 | required this.onPressed,
15 | }) : super(key: key);
16 |
17 | final Option barcodeType;
18 |
19 | final String data;
20 |
21 | final void Function() onPressed;
22 |
23 | @override
24 | Widget build(BuildContext _context) => _barcodeButton(
25 | _context,
26 | barcodeType: barcodeType,
27 | data: data,
28 | onPressed: onPressed,
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/lib/l10n/app_en.arb:
--------------------------------------------------------------------------------
1 | {
2 | "vouchers": "Vouchers",
3 | "edit": "Edit",
4 | "close": "Close",
5 | "copiedToClipboard": "Copied to clipboard",
6 | "areYouSure": "Are you sure?",
7 | "confirmRemoveVoucher": "That you want to remove this voucher?",
8 | "cancel": "Cancel",
9 | "remove": "Remove",
10 | "howMuchSpend": "How much did you spend?",
11 | "amount": "Amount",
12 | "ok": "OK",
13 | "addVoucher": "Add voucher",
14 | "editVoucher": "Edit voucher",
15 | "create": "Create",
16 | "update": "Update",
17 | "scanBarcode": "Scan barcode",
18 | "description": "Description",
19 | "code": "Code",
20 | "expires": "Expires",
21 | "removeOnceExpired": "Remove once expired",
22 | "balance": "Balance",
23 | "notes": "Notes",
24 | "import": "Import",
25 | "export": "Export",
26 | "appLock": "App lock",
27 | "smartScan": "Smart scan"
28 | }
29 |
--------------------------------------------------------------------------------
/lib/vouchers/list/voucher_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_elemental/flutter_elemental.dart';
3 | import 'package:functional_widget_annotation/functional_widget_annotation.dart';
4 | import 'package:vouchervault/vouchers/index.dart';
5 |
6 | part 'voucher_list.g.dart';
7 |
8 | @swidget
9 | Widget _voucherList({
10 | required IList vouchers,
11 | required void Function(Voucher) onPressed,
12 | }) {
13 | final vouchersLength = vouchers.length;
14 |
15 | return SliverList(
16 | delegate: SliverChildBuilderDelegate(
17 | (context, index) {
18 | final v = vouchers[index];
19 | return VoucherItem(
20 | voucher: v,
21 | onPressed: () => onPressed(v),
22 | bottomPadding:
23 | index == vouchersLength - 1 ? 0 : kVoucherItemBorderRadius,
24 | );
25 | },
26 | childCount: vouchersLength,
27 | ),
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/lib/hooks/use_system_overlay_style.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 | import 'package:flutter_hooks/flutter_hooks.dart';
4 |
5 | SystemUiOverlayStyle useSystemOverlayStyle() {
6 | final context = useContext();
7 | final theme = Theme.of(context);
8 |
9 | final style = useMemoized(
10 | () => theme.brightness == Brightness.light
11 | ? SystemUiOverlayStyle.dark.copyWith(
12 | statusBarColor: Colors.transparent,
13 | systemNavigationBarColor: theme.colorScheme.surface,
14 | systemNavigationBarIconBrightness: Brightness.dark,
15 | )
16 | : SystemUiOverlayStyle.light.copyWith(
17 | statusBarColor: Colors.transparent,
18 | systemNavigationBarColor: theme.colorScheme.surface,
19 | systemNavigationBarIconBrightness: Brightness.light,
20 | ),
21 | [theme],
22 | );
23 |
24 | return style;
25 | }
26 |
--------------------------------------------------------------------------------
/lib/l10n/app_fr.arb:
--------------------------------------------------------------------------------
1 | {
2 | "vouchers": "Mes bons",
3 | "edit": "Modifier",
4 | "close": "Fermer",
5 | "copiedToClipboard": "Copié dans le presse-papier",
6 | "areYouSure": "Etes-vous sûr?",
7 | "confirmRemoveVoucher": "De vouloir supprimer ce bon?",
8 | "cancel": "Annuler",
9 | "remove": "Supprimer",
10 | "howMuchSpend": "Quel montant avez-vous depensé?",
11 | "amount": "Montant",
12 | "ok": "Valider",
13 | "addVoucher": "Ajouter un bon",
14 | "editVoucher": "Modifier ce bon",
15 | "create": "Créer",
16 | "update": "Modifier",
17 | "scanBarcode": "Scanner un code barre",
18 | "description": "Description",
19 | "code": "Code",
20 | "expires": "Date d'expiration",
21 | "removeOnceExpired": "Supprimer une fois expiré",
22 | "balance": "Solde",
23 | "notes": "Notes",
24 | "import": "Importer",
25 | "export": "Exporter",
26 | "appLock": "Verrouiller l'appli",
27 | "smartScan": "Scan intelligent"
28 | }
29 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_elemental/flutter_elemental.dart' hide Logger;
3 | import 'package:logging/logging.dart';
4 | import 'package:vouchervault/app/index.dart';
5 | import 'package:vouchervault/auth/index.dart';
6 | import 'package:vouchervault/vouchers/index.dart';
7 |
8 | void main({IList? vouchers}) async {
9 | WidgetsFlutterBinding.ensureInitialized();
10 |
11 | Logger.root.level = Level.ALL;
12 | Logger.root.onRecord.listen((r) =>
13 | // ignore: avoid_print
14 | print('${r.loggerName}: ${r.level.name}: ${r.time}: ${r.message}'));
15 |
16 | final runtime = await runtimeInitialValue([
17 | sharedPrefsLayer,
18 | authLayer,
19 | if (vouchers != null)
20 | vouchersLayer.replace(IO.succeed(VouchersService(
21 | ref: Ref.unsafeMake(VouchersState(vouchers)),
22 | )))
23 | else
24 | vouchersLayer,
25 | ]);
26 |
27 | runApp(VoucherVaultApp(initialValues: [runtime]));
28 | }
29 |
--------------------------------------------------------------------------------
/lib/shared/voucher_details/voucher_details.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'voucher_details.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class _VoucherDetailRow extends StatelessWidget {
10 | const _VoucherDetailRow(
11 | this.icon,
12 | this.text, {
13 | Key? key,
14 | this.alignment = CrossAxisAlignment.center,
15 | this.iconPadding = false,
16 | this.selectable = false,
17 | }) : super(key: key);
18 |
19 | final IconData icon;
20 |
21 | final String text;
22 |
23 | final CrossAxisAlignment alignment;
24 |
25 | final bool iconPadding;
26 |
27 | final bool selectable;
28 |
29 | @override
30 | Widget build(BuildContext _context) => __voucherDetailRow(
31 | _context,
32 | icon,
33 | text,
34 | alignment: alignment,
35 | iconPadding: iconPadding,
36 | selectable: selectable,
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: "7c6b7e9ca485f7eaaed913c6bb50f4be6da47e30"
8 | channel: "beta"
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: 7c6b7e9ca485f7eaaed913c6bb50f4be6da47e30
17 | base_revision: 7c6b7e9ca485f7eaaed913c6bb50f4be6da47e30
18 | - platform: android
19 | create_revision: 7c6b7e9ca485f7eaaed913c6bb50f4be6da47e30
20 | base_revision: 7c6b7e9ca485f7eaaed913c6bb50f4be6da47e30
21 |
22 | # User provided section
23 |
24 | # List of Local paths (relative to this file) that should be
25 | # ignored by the migrate tool.
26 | #
27 | # Files that are not part of the templates will be ignored by default.
28 | unmanaged_files:
29 | - 'lib/main.dart'
30 | - 'ios/Runner.xcodeproj/project.pbxproj'
31 |
--------------------------------------------------------------------------------
/lib/vouchers/models/state.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_elemental/flutter_elemental.dart';
2 | import 'package:freezed_annotation/freezed_annotation.dart';
3 | import 'package:vouchervault/vouchers/index.dart';
4 |
5 | part 'state.freezed.dart';
6 |
7 | @freezed
8 | class VouchersState with _$VouchersState {
9 | VouchersState._();
10 | factory VouchersState(IList vouchers) = _VouchersState;
11 |
12 | late final IList sortedVouchers = vouchers.sort(_compareVoucher);
13 |
14 | dynamic toJson() => vouchers.toJson((v) => v.toJson());
15 | static VouchersState fromJson(dynamic json) => VouchersState(
16 | IList.fromJson(json, Voucher.fromJson),
17 | );
18 | }
19 |
20 | int _unix(Option dt) =>
21 | dt.map((d) => d.millisecondsSinceEpoch).getOrElse(() => 0);
22 |
23 | int _compareVoucher(Voucher a, Voucher b) {
24 | final compare = a.description.compareTo(b.description);
25 | final expiresCompare =
26 | _unix(a.normalizedExpires).compareTo(_unix(b.normalizedExpires));
27 |
28 | return compare != 0 ? compare : expiresCompare;
29 | }
30 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/lib/shared/scaffold/app_scaffold.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'app_scaffold.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class AppScaffold extends HookWidget {
10 | const AppScaffold({
11 | Key? key,
12 | required this.title,
13 | required this.slivers,
14 | this.actions = const [],
15 | this.floatingActionButton = const Option.none(),
16 | this.leading = false,
17 | }) : super(key: key);
18 |
19 | final String title;
20 |
21 | final List slivers;
22 |
23 | final List actions;
24 |
25 | final Option floatingActionButton;
26 |
27 | final bool leading;
28 |
29 | @override
30 | Widget build(BuildContext _context) => appScaffold(
31 | _context,
32 | title: title,
33 | slivers: slivers,
34 | actions: actions,
35 | floatingActionButton: floatingActionButton,
36 | leading: leading,
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/lib/app/theme.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class AppTheme {
4 | static const baseFontSize = 18;
5 | static double rem(double rem) => baseFontSize * rem;
6 | static double px(double px) => rem(px / 18);
7 |
8 | static double get space1 => rem(0.1);
9 | static double get space2 => rem(0.3);
10 | static double get space3 => rem(0.6);
11 | static double get space4 => rem(1);
12 | static double get space5 => rem(1.5);
13 | static double get space6 => rem(2.5);
14 | static double get space7 => rem(4);
15 |
16 | static ThemeData build(
17 | ColorScheme scheme, {
18 | TextTheme? textTheme,
19 | }) {
20 | final theme = ThemeData.from(
21 | useMaterial3: true,
22 | colorScheme: scheme.copyWith(
23 | outline: scheme.outline.withAlpha(100),
24 | ),
25 | textTheme: textTheme,
26 | );
27 | return theme.copyWith(
28 | pageTransitionsTheme: const PageTransitionsTheme(builders: {
29 | TargetPlatform.android: CupertinoPageTransitionsBuilder(),
30 | TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
31 | }),
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/shared/scaffold/app_scaffold.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_elemental/flutter_elemental.dart';
2 | import 'package:flutter_hooks/flutter_hooks.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:functional_widget_annotation/functional_widget_annotation.dart';
5 | import 'package:vouchervault/hooks/index.dart';
6 |
7 | part 'app_scaffold.g.dart';
8 |
9 | @hwidget
10 | Widget appScaffold(
11 | BuildContext context, {
12 | required String title,
13 | required List slivers,
14 | List actions = const [],
15 | Option floatingActionButton = const Option.none(),
16 | bool leading = false,
17 | }) {
18 | final style = useSystemOverlayStyle();
19 |
20 | return Scaffold(
21 | body: AnnotatedRegion(
22 | value: style,
23 | child: NestedScrollView(
24 | headerSliverBuilder: (context, innerBoxIsScrolled) => [
25 | SliverAppBar.large(
26 | systemOverlayStyle: style,
27 | actions: actions,
28 | title: Text(title),
29 | ),
30 | ],
31 | body: CustomScrollView(slivers: slivers),
32 | ),
33 | ),
34 | floatingActionButton: floatingActionButton.toNullable(),
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM debian:stretch-slim
2 |
3 | # Hack to get openjdk to install in a container
4 | RUN mkdir -p /usr/share/man/man1 \
5 | && mkdir -p /usr/share/man/man7
6 |
7 | # Apt
8 | RUN apt update && apt install -y curl wget git xz-utils lib32stdc++6 unzip openjdk-8-jdk-headless
9 |
10 | # Android SDK
11 | RUN wget https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip
12 | RUN mkdir android-sdk && unzip /sdk-tools-linux-4333796.zip -d android-sdk
13 | RUN rm /sdk-tools-linux-4333796.zip
14 | ENV ANDROID_HOME="/android-sdk"
15 | ENV PATH="/android-sdk/tools/bin:/android-sdk/build-tools:/android-sdk/platform-tools:${PATH}"
16 |
17 | # SDK manager
18 | RUN yes | sdkmanager --licenses
19 | RUN sdkmanager "platforms;android-28" "platform-tools" "build-tools;28.0.3"
20 |
21 | # Flutter
22 | ENV FLUTTER_VERSION "3.0.1-stable"
23 | RUN wget "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}.tar.xz"
24 | RUN tar xf "flutter_linux_${FLUTTER_VERSION}.tar.xz"
25 | RUN rm "flutter_linux_${FLUTTER_VERSION}.tar.xz"
26 | ENV PATH="/flutter/bin:${PATH}"
27 |
28 | RUN flutter config --no-analytics
29 |
30 | # Set a useful default shell
31 | ENV SHELL /bin/bash
32 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | CURRENT_BUILD_NUMBER = $(shell cat metadata/build_number.txt | head)
2 | NEXT_BUILD_NUMBER = $(shell expr $(CURRENT_BUILD_NUMBER) + 1)
3 |
4 | .PHONY: default
5 | default: alpha
6 |
7 | .PHONY: clean
8 | clean:
9 | flutter clean
10 |
11 | .PHONY: build_runner
12 | build_runner:
13 | pm2 start --no-daemon "flutter pub run build_runner watch --delete-conflicting-outputs"
14 |
15 | .PHONY: icons
16 | icons:
17 | flutter pub run flutter_launcher_icons:main
18 |
19 | .PHONY: screenshots
20 | screenshots:
21 | dart tools/screenshots.dart
22 |
23 | git reset
24 | git add android/fastlane/metadata/android/en-US/images
25 | git add ios/fastlane/screenshots
26 | git commit -m "Update screenshots"
27 |
28 | .PHONY: increment-build-number
29 | increment-build-number:
30 | @echo $(NEXT_BUILD_NUMBER) > metadata/build_number.txt
31 |
32 | .PHONY: alpha
33 | alpha: clean increment-build-number
34 | cd android && bundle update fastlane && bundle exec fastlane alpha
35 |
36 | .PHONY: beta
37 | beta: clean increment-build-number
38 | cd android && bundle update fastlane && bundle exec fastlane beta
39 |
40 | .PHONY: release
41 | release: clean increment-build-number
42 | cd android && bundle update fastlane && bundle exec fastlane release
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
4 | flake-utils.url = "github:numtide/flake-utils";
5 | };
6 | outputs = {
7 | self,
8 | nixpkgs,
9 | flake-utils,
10 | }:
11 | flake-utils.lib.eachDefaultSystem (system: let
12 | pkgs = import nixpkgs {
13 | inherit system;
14 | config = {
15 | android_sdk.accept_license = true;
16 | allowUnfree = true;
17 | };
18 | };
19 | flutter_path = "$PWD/.flutter";
20 | flutter = import ./nix/flutter.nix {
21 | inherit pkgs system;
22 | path = flutter_path;
23 | };
24 | in {
25 | devShell = with pkgs;
26 | mkShellNoCC {
27 | buildInputs = [
28 | cocoapods
29 | jdk17
30 | ruby
31 | sops
32 | ];
33 | shellHook = ''
34 | ${flutter.unpack}/bin/unpack
35 | export PATH="${flutter_path}/flutter/bin:$PATH"
36 | export ANDROID_SDK_ROOT=$HOME/Library/Android/sdk
37 | mkdir -p android/keys
38 | sops -d secrets/fastlane-google-key > android/keys/google.json
39 | '';
40 | };
41 | });
42 | }
43 |
--------------------------------------------------------------------------------
/android/fastlane/README.md:
--------------------------------------------------------------------------------
1 | fastlane documentation
2 | ----
3 |
4 | # Installation
5 |
6 | Make sure you have the latest version of the Xcode command line tools installed:
7 |
8 | ```sh
9 | xcode-select --install
10 | ```
11 |
12 | For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
13 |
14 | # Available Actions
15 |
16 | ## Android
17 |
18 | ### android build
19 |
20 | ```sh
21 | [bundle exec] fastlane android build
22 | ```
23 |
24 |
25 |
26 | ### android alpha
27 |
28 | ```sh
29 | [bundle exec] fastlane android alpha
30 | ```
31 |
32 | Submit a new build to Google Play internal track
33 |
34 | ### android beta
35 |
36 | ```sh
37 | [bundle exec] fastlane android beta
38 | ```
39 |
40 | Submit a new build to Google Play beta track
41 |
42 | ### android release
43 |
44 | ```sh
45 | [bundle exec] fastlane android release
46 | ```
47 |
48 | Submit a new build to Google Play production track
49 |
50 | ----
51 |
52 | This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
53 |
54 | More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
55 |
56 | The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
57 |
--------------------------------------------------------------------------------
/lib/vouchers/dialog/voucher_spend_dialog.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_hooks/flutter_hooks.dart';
3 | import 'package:functional_widget_annotation/functional_widget_annotation.dart';
4 | import 'package:flutter_gen/gen_l10n/app_localizations.dart';
5 |
6 | part 'voucher_spend_dialog.g.dart';
7 |
8 | @hwidget
9 | Widget _voucherSpendDialog(BuildContext context) {
10 | final amount = useValueNotifier('');
11 | void submit() => Navigator.pop(context, amount.value);
12 |
13 | return AlertDialog(
14 | title: Text(AppLocalizations.of(context)!.howMuchSpend),
15 | content: TextField(
16 | autofocus: true,
17 | decoration: InputDecoration(
18 | border: const OutlineInputBorder(),
19 | labelText: AppLocalizations.of(context)!.amount,
20 | ),
21 | keyboardType: const TextInputType.numberWithOptions(signed: true),
22 | onChanged: (s) => amount.value = s,
23 | onSubmitted: (s) => submit(),
24 | ),
25 | actions: [
26 | TextButton(
27 | onPressed: () => Navigator.pop(context, null),
28 | child: Text(AppLocalizations.of(context)!.cancel),
29 | ),
30 | TextButton(
31 | onPressed: submit,
32 | child: Text(AppLocalizations.of(context)!.ok),
33 | ),
34 | ],
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/nix/flutter.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs ? import {},
3 | path,
4 | system,
5 | }: rec {
6 | source =
7 | if system == "aarch64-darwin"
8 | then
9 | pkgs.fetchurl {
10 | url = "https://storage.googleapis.com/flutter_infra_release/releases/betc/macos/flutter_macos_arm64_3.24.0-0.2.pre-beta.zip";
11 | hash = "";
12 | }
13 | else if system == "x86_64-darwin"
14 | then
15 | pkgs.fetchurl {
16 | url = "https://storage.googleapis.com/flutter_infra_release/releases/beta/macos/flutter_macos_3.24.0-0.2.pre-beta.zip";
17 | hash = "sha256-AbeSWHYRORM74qM9W4v0pyQ9Wc/DO8FJejR2QwReEgY=";
18 | }
19 | else abort "unsupported system";
20 |
21 | unpack = pkgs.writeShellApplication {
22 | name = "unpack";
23 | runtimeInputs = with pkgs; [unzip];
24 | text = ''
25 | flutter_local_dir=${path}
26 | flutter_bin_dir="$flutter_local_dir"/flutter/bin
27 | flutter_bin_file="$flutter_bin_dir"/flutter
28 |
29 | echo "flutter needs local installation? ..."
30 |
31 | if [ -f "$flutter_bin_file" ]; then
32 | echo "flutter is already installed locally in '$flutter_local_dir'"
33 | else
34 | echo "... installing flutter locally in '$flutter_local_dir'"
35 | unzip ${source} -d "$flutter_local_dir"
36 | fi
37 | '';
38 | };
39 | }
40 |
--------------------------------------------------------------------------------
/lib/auth/auth_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_elemental/flutter_elemental.dart';
3 | import 'package:flutter_hooks/flutter_hooks.dart';
4 | import 'package:functional_widget_annotation/functional_widget_annotation.dart';
5 | import 'package:vouchervault/app/index.dart';
6 | import 'package:vouchervault/auth/index.dart';
7 | import 'package:vouchervault/shared/scaffold/app_scaffold_simple.dart';
8 |
9 | part 'auth_screen.g.dart';
10 |
11 | @hwidget
12 | Widget authScreen(BuildContext context) {
13 | final authenticate = useZIO(authLayer.accessWithZIO((_) => _.authenticate));
14 | final theme = Theme.of(context);
15 |
16 | useEffect(() {
17 | authenticate();
18 | return null;
19 | }, []);
20 |
21 | return AppScaffoldSimple(
22 | body: Center(
23 | child: ElevatedButton(
24 | style: ElevatedButton.styleFrom(
25 | backgroundColor: theme.colorScheme.primary,
26 | foregroundColor: theme.colorScheme.onPrimary,
27 | ),
28 | onPressed: authenticate,
29 | child: Row(
30 | mainAxisSize: MainAxisSize.min,
31 | children: [
32 | const Icon(Icons.lock_open),
33 | SizedBox(width: AppTheme.space2),
34 | const Text('Unlock'),
35 | ],
36 | ),
37 | ),
38 | ),
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/lib/auth/model.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'model.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$UnauthenticatedImpl _$$UnauthenticatedImplFromJson(
10 | Map json) =>
11 | _$UnauthenticatedImpl(
12 | $type: json['runtimeType'] as String?,
13 | );
14 |
15 | Map _$$UnauthenticatedImplToJson(
16 | _$UnauthenticatedImpl instance) =>
17 | {
18 | 'runtimeType': instance.$type,
19 | };
20 |
21 | _$AuthenticatedImpl _$$AuthenticatedImplFromJson(Map json) =>
22 | _$AuthenticatedImpl(
23 | $enumDecode(_$AuthenticatedReasonEnumMap, json['reason']),
24 | $type: json['runtimeType'] as String?,
25 | );
26 |
27 | Map _$$AuthenticatedImplToJson(_$AuthenticatedImpl instance) =>
28 | {
29 | 'reason': _$AuthenticatedReasonEnumMap[instance.reason]!,
30 | 'runtimeType': instance.$type,
31 | };
32 |
33 | const _$AuthenticatedReasonEnumMap = {
34 | AuthenticatedReason.NOT_AVAILABLE: 'NOT_AVAILABLE',
35 | AuthenticatedReason.NOT_REQUIRED: 'NOT_REQUIRED',
36 | AuthenticatedReason.SUCCESS: 'SUCCESS',
37 | };
38 |
--------------------------------------------------------------------------------
/android/fastlane/Fastfile:
--------------------------------------------------------------------------------
1 | default_platform(:android)
2 |
3 | build_number = File.open("../../metadata/build_number.txt", "r") { |f| f.read.strip.to_i }
4 |
5 | platform :android do
6 | lane :build do
7 | # Update changelogs
8 | FileUtils.cp(
9 | "../../metadata/release_notes.txt",
10 | "metadata/android/en-US/changelogs/#{build_number}.txt",
11 | )
12 |
13 | Dir.chdir("../..") do
14 | sh(
15 | "flutter",
16 | "build",
17 | "appbundle",
18 | "--release",
19 | "--build-number=#{build_number}",
20 | )
21 | end
22 | end
23 |
24 | desc "Submit a new build to Google Play internal track"
25 | lane :alpha do
26 | build()
27 | upload_to_play_store(
28 | aab: "../build/app/outputs/bundle/release/app-release.aab",
29 | track: "internal",
30 | )
31 | end
32 |
33 | desc "Submit a new build to Google Play beta track"
34 | lane :beta do
35 | alpha()
36 | upload_to_play_store(
37 | track: "internal",
38 | track_promote_to: "beta",
39 | version_code: build_number.to_s,
40 | )
41 | end
42 |
43 | desc "Submit a new build to Google Play production track"
44 | lane :release do
45 | beta()
46 | upload_to_play_store(
47 | track: "beta",
48 | track_promote_to: "production",
49 | version_code: build_number.to_s,
50 | )
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/lib/lib/barcode.dart:
--------------------------------------------------------------------------------
1 | import 'package:barcode_widget/barcode_widget.dart';
2 | import 'package:flutter_elemental/flutter_elemental.dart';
3 | import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart'
4 | show BarcodeFormat;
5 | import 'package:vouchervault/vouchers/index.dart';
6 |
7 | final Map _codeTypeMap = {
8 | VoucherCodeType.AZTEC: Barcode.aztec(minECCPercent: 5),
9 | VoucherCodeType.CODE128: Barcode.code128(),
10 | VoucherCodeType.CODE39: Barcode.code39(),
11 | VoucherCodeType.EAN13: Barcode.ean13(),
12 | VoucherCodeType.PDF417: Barcode.pdf417(),
13 | VoucherCodeType.QR: Barcode.qrCode(),
14 | };
15 | final barcodeFromCodeType = _codeTypeMap.lookup;
16 | final barcodeFromCodeTypeJson = codeTypeFromJson.c(barcodeFromCodeType);
17 |
18 | final Map _barcodeFormatMap = {
19 | BarcodeFormat.aztec: VoucherCodeType.AZTEC,
20 | BarcodeFormat.code128: VoucherCodeType.CODE128,
21 | BarcodeFormat.code39: VoucherCodeType.CODE39,
22 | BarcodeFormat.ean13: VoucherCodeType.EAN13,
23 | BarcodeFormat.pdf417: VoucherCodeType.PDF417,
24 | BarcodeFormat.qrCode: VoucherCodeType.QR,
25 | };
26 | VoucherCodeType codeTypeFromFormat(BarcodeFormat format) =>
27 | _barcodeFormatMap.lookup(format).getOrElse(() => VoucherCodeType.CODE128);
28 |
29 | final codeTypeValueFromFormat = codeTypeFromFormat.c(codeTypeToJson);
30 |
--------------------------------------------------------------------------------
/lib/auth/model.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: constant_identifier_names
2 |
3 | import 'package:freezed_annotation/freezed_annotation.dart';
4 |
5 | part 'model.freezed.dart';
6 | part 'model.g.dart';
7 |
8 | enum AuthenticatedReason {
9 | NOT_AVAILABLE,
10 | NOT_REQUIRED,
11 | SUCCESS,
12 | }
13 |
14 | @freezed
15 | class AuthState with _$AuthState {
16 | const AuthState._();
17 |
18 | const factory AuthState.unauthenticated() = Unauthenticated;
19 | const factory AuthState.authenticated(AuthenticatedReason reason) =
20 | Authenticated;
21 |
22 | static const notAvailable =
23 | AuthState.authenticated(AuthenticatedReason.NOT_AVAILABLE);
24 | static const notRequired =
25 | AuthState.authenticated(AuthenticatedReason.NOT_REQUIRED);
26 | static const success = AuthState.authenticated(AuthenticatedReason.SUCCESS);
27 |
28 | factory AuthState.fromJson(Map json) =>
29 | _$AuthStateFromJson(json);
30 |
31 | bool get available => this != notAvailable;
32 |
33 | bool get enabled => when(
34 | unauthenticated: () => true,
35 | authenticated: (reason) => reason == AuthenticatedReason.SUCCESS,
36 | );
37 |
38 | AuthState enable() =>
39 | available ? const AuthState.unauthenticated() : notAvailable;
40 |
41 | AuthState disable() => available ? notRequired : notAvailable;
42 |
43 | AuthState init() => enabled ? const AuthState.unauthenticated() : this;
44 | }
45 |
--------------------------------------------------------------------------------
/lib/voucher_form/barcode_scanner/widgets/barcode_scanner_field.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'barcode_scanner_field.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class BarcodeScannerField extends HookWidget {
10 | const BarcodeScannerField({
11 | Key? key,
12 | required this.onChange,
13 | required this.initialValue,
14 | required this.barcodeType,
15 | required this.labelText,
16 | this.errorText = const Option.none(),
17 | required this.onScan,
18 | this.launchScannerImmediately = false,
19 | }) : super(key: key);
20 |
21 | final void Function(String) onChange;
22 |
23 | final String initialValue;
24 |
25 | final Option barcodeType;
26 |
27 | final String labelText;
28 |
29 | final Option errorText;
30 |
31 | final void Function(BarcodeResult) onScan;
32 |
33 | final bool launchScannerImmediately;
34 |
35 | @override
36 | Widget build(BuildContext _context) => _barcodeScannerField(
37 | _context,
38 | onChange: onChange,
39 | initialValue: initialValue,
40 | barcodeType: barcodeType,
41 | labelText: labelText,
42 | errorText: errorText,
43 | onScan: onScan,
44 | launchScannerImmediately: launchScannerImmediately,
45 | );
46 | }
47 |
--------------------------------------------------------------------------------
/lib/hooks/use_full_brightness.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart' hide Action;
2 | import 'package:flutter_elemental/flutter_elemental.dart';
3 | import 'package:flutter_hooks/flutter_hooks.dart';
4 | import 'package:screen_brightness/screen_brightness.dart';
5 | import 'package:vouchervault/hooks/index.dart';
6 |
7 | void useFullBrightness(
8 | RouteObserver routeObserver, {
9 | bool enabled = true,
10 | }) {
11 | final brightness = useMemoized(() => ScreenBrightness());
12 | final goBright = useZIO(
13 | EIO
14 | .tryCatch(
15 | () => brightness.setScreenBrightness(1),
16 | (error, stackTrace) => 'Could not set brightness: $error',
17 | )
18 | .ignoreLogged,
19 | [brightness],
20 | );
21 | final goDark = useZIO(
22 | EIO
23 | .tryCatch(
24 | () => brightness.resetScreenBrightness(),
25 | (error, stackTrace) => 'Could not set brightness: $error',
26 | )
27 | .ignoreLogged,
28 | [brightness],
29 | );
30 |
31 | useEffect(() {
32 | if (enabled) {
33 | goBright();
34 | }
35 |
36 | return () {
37 | goDark();
38 | };
39 | }, [enabled]);
40 |
41 | // If something gets pushed on top of the route, then go dark again.
42 | useRouteObserver(
43 | routeObserver,
44 | didPushNext: Option.of(() {
45 | goDark();
46 | }),
47 | didPopNext: Option.of(() {
48 | if (!enabled) return;
49 | goBright();
50 | }),
51 | keys: [enabled],
52 | );
53 | }
54 |
--------------------------------------------------------------------------------
/lib/vouchers/list/vouchers_list_container.dart:
--------------------------------------------------------------------------------
1 | import 'package:dismissible_page/dismissible_page.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_elemental/flutter_elemental.dart';
4 | import 'package:functional_widget_annotation/functional_widget_annotation.dart';
5 | import 'package:vouchervault/vouchers/index.dart';
6 |
7 | part 'vouchers_list_container.g.dart';
8 |
9 | class _DialogRoute extends PageRoute with MaterialRouteTransitionMixin {
10 | _DialogRoute(this.builder) : super(fullscreenDialog: true);
11 |
12 | final WidgetBuilder builder;
13 |
14 | @override
15 | Widget buildContent(BuildContext context) => builder(context);
16 |
17 | @override
18 | bool get opaque => false;
19 |
20 | @override
21 | bool get maintainState => false;
22 |
23 | @override
24 | Color get barrierColor => Colors.black54;
25 | }
26 |
27 | @swidget
28 | Widget _vouchersListContainer(BuildContext context) {
29 | return AtomBuilder(
30 | (context, watch, child) => VoucherList(
31 | vouchers: watch(vouchersAtom),
32 | onPressed: (v) => Navigator.push(
33 | context,
34 | _DialogRoute(
35 | (context) => DismissiblePage(
36 | backgroundColor: Colors.transparent,
37 | direction: DismissiblePageDismissDirection.multi,
38 | minScale: 0.3,
39 | onDismissed: () => Navigator.pop(context),
40 | child: Center(child: VoucherDialogContainer(voucher: v)),
41 | ),
42 | ),
43 | ),
44 | ),
45 | );
46 | }
47 |
--------------------------------------------------------------------------------
/lib/vouchers/vouchers_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_elemental/flutter_elemental.dart';
3 | import 'package:functional_widget_annotation/functional_widget_annotation.dart';
4 | import 'package:vouchervault/app/index.dart';
5 | import 'package:vouchervault/shared/scaffold/scaffold.dart';
6 | import 'package:vouchervault/voucher_form/index.dart';
7 | import 'package:vouchervault/vouchers/index.dart';
8 | import 'package:flutter_gen/gen_l10n/app_localizations.dart';
9 |
10 | part 'vouchers_screen.g.dart';
11 |
12 | @swidget
13 | Widget _vouchersScreen(BuildContext context) {
14 | return AppScaffold(
15 | title: AppLocalizations.of(context)!.vouchers,
16 | actions: const [VouchersMenuContainer()],
17 | slivers: [
18 | SliverPadding(
19 | padding: EdgeInsets.only(
20 | top: AppTheme.rem(1.5),
21 | bottom: AppTheme.space6,
22 | ),
23 | sliver: const VouchersListContainer(),
24 | ),
25 | ],
26 | floatingActionButton: Option.of(FloatingActionButton(
27 | onPressed: () => Navigator.of(context)
28 | .pushIO(MaterialPageRoute(
29 | builder: (context) => const VoucherFormDialog(),
30 | fullscreenDialog: true,
31 | ))
32 | .tap(_createVoucher)
33 | .provide(context)
34 | .runContext(context),
35 | child: const Icon(Icons.add),
36 | )),
37 | );
38 | }
39 |
40 | BuildContextIO _createVoucher(Voucher v) =>
41 | vouchersLayer.accessWithZIO((_) => _.create(v).lift());
42 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.android.application"
3 | id "kotlin-android"
4 | id "dev.flutter.flutter-gradle-plugin"
5 | }
6 |
7 | def keystoreProperties = new Properties()
8 | def keystorePropertiesFile = rootProject.file('key.properties')
9 | if (keystorePropertiesFile.exists()) {
10 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
11 | }
12 |
13 | android {
14 | namespace = "co.timsmart.vouchervault"
15 | compileSdk = 34
16 | ndkVersion = flutter.ndkVersion
17 |
18 | compileOptions {
19 | sourceCompatibility = JavaVersion.VERSION_1_8
20 | targetCompatibility = JavaVersion.VERSION_1_8
21 | }
22 |
23 | kotlinOptions {
24 | jvmTarget = JavaVersion.VERSION_1_8
25 | }
26 |
27 | defaultConfig {
28 | applicationId = "co.timsmart.vouchervault"
29 | minSdk = flutter.minSdkVersion
30 | targetSdk = 34
31 | versionCode = flutter.versionCode
32 | versionName = flutter.versionName
33 | }
34 |
35 | signingConfigs {
36 | release {
37 | keyAlias keystoreProperties['keyAlias']
38 | keyPassword keystoreProperties['keyPassword']
39 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
40 | storePassword keystoreProperties['storePassword']
41 | }
42 | }
43 |
44 | buildTypes {
45 | release {
46 | signingConfig signingConfigs.release
47 |
48 | minifyEnabled true
49 | shrinkResources true
50 | }
51 | }
52 | }
53 |
54 | flutter {
55 | source = "../.."
56 | }
57 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | platform :ios, '13.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 |
41 | target.build_configurations.each do |config|
42 | config.build_settings["IPHONEOS_DEPLOYMENT_TARGET"] = "13.0"
43 | end
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/lib/vouchers/menu/vouchers_menu_container.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_elemental/flutter_elemental.dart';
3 | import 'package:functional_widget_annotation/functional_widget_annotation.dart';
4 | import 'package:vouchervault/app/index.dart';
5 | import 'package:vouchervault/auth/index.dart';
6 | import 'package:vouchervault/vouchers/index.dart';
7 |
8 | part 'vouchers_menu_container.g.dart';
9 |
10 | extension _VoucherMenuActions on VouchersMenuAction {
11 | void execute(BuildContext x) {
12 | switch (this) {
13 | case VouchersMenuAction.import:
14 | vouchersLayer.accessWithZIO((_) => _.import).runContext(x);
15 | return;
16 | case VouchersMenuAction.export:
17 | vouchersLayer.accessWithZIO((_) => _.export).runContext(x);
18 | return;
19 | case VouchersMenuAction.authentication:
20 | authLayer.accessWithZIO((_) => _.toggle).runContext(x);
21 | return;
22 | case VouchersMenuAction.smartScan:
23 | x.updateAtom(appSettings)((s) => s.copyWith(smartScan: !s.smartScan));
24 | return;
25 | }
26 | }
27 | }
28 |
29 | @swidget
30 | Widget vouchersMenuContainer() {
31 | return AtomBuilder((context, watch, child) {
32 | final authAvailable = watch(authAvailableAtom);
33 |
34 | return VouchersMenu(
35 | onSelected: (action) => action.execute(context),
36 | values: {
37 | VouchersMenuAction.authentication: watch(authEnabledAtom),
38 | VouchersMenuAction.smartScan: watch(appSettings).smartScan,
39 | },
40 | disabled: {
41 | if (!authAvailable) VouchersMenuAction.authentication,
42 | },
43 | );
44 | });
45 | }
46 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "flake-utils": {
4 | "inputs": {
5 | "systems": "systems"
6 | },
7 | "locked": {
8 | "lastModified": 1710146030,
9 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
10 | "owner": "numtide",
11 | "repo": "flake-utils",
12 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
13 | "type": "github"
14 | },
15 | "original": {
16 | "owner": "numtide",
17 | "repo": "flake-utils",
18 | "type": "github"
19 | }
20 | },
21 | "nixpkgs": {
22 | "locked": {
23 | "lastModified": 1722640603,
24 | "narHash": "sha256-TcXjLVNd3VeH1qKPH335Tc4RbFDbZQX+d7rqnDUoRaY=",
25 | "owner": "NixOS",
26 | "repo": "nixpkgs",
27 | "rev": "81610abc161d4021b29199aa464d6a1a521e0cc9",
28 | "type": "github"
29 | },
30 | "original": {
31 | "owner": "NixOS",
32 | "ref": "nixpkgs-unstable",
33 | "repo": "nixpkgs",
34 | "type": "github"
35 | }
36 | },
37 | "root": {
38 | "inputs": {
39 | "flake-utils": "flake-utils",
40 | "nixpkgs": "nixpkgs"
41 | }
42 | },
43 | "systems": {
44 | "locked": {
45 | "lastModified": 1681028828,
46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
47 | "owner": "nix-systems",
48 | "repo": "default",
49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
50 | "type": "github"
51 | },
52 | "original": {
53 | "owner": "nix-systems",
54 | "repo": "default",
55 | "type": "github"
56 | }
57 | }
58 | },
59 | "root": "root",
60 | "version": 7
61 | }
62 |
--------------------------------------------------------------------------------
/test_driver/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_driver/driver_extension.dart';
3 | import 'package:flutter_elemental/flutter_elemental.dart';
4 | import 'package:uuid/uuid.dart';
5 | import 'package:vouchervault/main.dart' as app;
6 | import 'package:vouchervault/vouchers/index.dart';
7 |
8 | void main() async {
9 | WidgetsApp.debugAllowBannerOverride = false;
10 | enableFlutterDriverExtension();
11 |
12 | app.main(
13 | vouchers: IList([
14 | Voucher(
15 | uuid: Option.of(const Uuid().v4()),
16 | description: "Walmart",
17 | code: const Option.of('12345'),
18 | codeType: VoucherCodeType.QR,
19 | color: VoucherColor.BLUE,
20 | balanceMilliunits: const Option.of(77 * 1000),
21 | expires: DateTime.now().add(const Duration(days: 120)).chain(Option.of),
22 | ),
23 | Voucher(
24 | uuid: const Uuid().v4().chain(Option.of),
25 | description: "Starbucks",
26 | code: "12345".chain(Option.of),
27 | codeType: VoucherCodeType.CODE128,
28 | color: VoucherColor.GREEN,
29 | balanceMilliunits: const Option.of(50 * 1000),
30 | ),
31 | Voucher(
32 | uuid: const Uuid().v4().chain(Option.of),
33 | description: "New World Clubcard",
34 | code: "12345".chain(Option.of),
35 | codeType: VoucherCodeType.QR,
36 | color: VoucherColor.RED,
37 | ),
38 | Voucher(
39 | uuid: const Uuid().v4().chain(Option.of),
40 | description: "Barkers",
41 | code: "12345".chain(Option.of),
42 | codeType: VoucherCodeType.QR,
43 | color: VoucherColor.GREY,
44 | balanceMilliunits: const Option.of(100 * 1000),
45 | ),
46 | ]),
47 | );
48 | }
49 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: vouchervault
2 | description: An app for storing vouchers and loyalty cards
3 | publish_to: "none"
4 | version: 1.2.0
5 |
6 | environment:
7 | sdk: ">=3.0.0 <4.0.0"
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 | auto_size_text: ^3.0.0
13 | barcode_widget: ^2.0.0
14 | camera: ^0.11.0+1
15 | dart_date: ^1.1.0
16 | file_picker: ^8.0.7
17 | flutter_form_builder: ^9.3.0
18 | flutter_hooks: ^0.20.5
19 | flutter_localizations:
20 | sdk: flutter
21 | flutter_elemental: ^0.3.1
22 | fluttertoast: ^8.2.6
23 | form_builder_validators: ^11.0.0
24 | freezed_annotation: ^2.0.3
25 | functional_widget_annotation: ^0.10.0
26 | google_mlkit_barcode_scanning: ^0.12.0
27 | google_mlkit_entity_extraction: ^0.13.0
28 | google_mlkit_text_recognition: ^0.13.0
29 | intl: ^0.19.0
30 | json_annotation: ^4.9.0
31 | local_auth: ^2.1.0
32 | logging: ^1.0.2
33 | path_provider: ^2.0.1
34 | recase: ^4.0.0
35 | rxdart: ^0.28.0
36 | share: ^2.0.0
37 | screen_brightness: ^1.0.1
38 | uuid: ^4.4.2
39 | shared_preferences: ^2.0.13
40 | image_picker: ^1.0.2
41 | dynamic_color: ^1.7.0
42 | dismissible_page: ^1.0.2
43 |
44 | dev_dependencies:
45 | build_runner: ^2.0.1
46 | emulators: ^0.6.0
47 | flutter_driver:
48 | sdk: flutter
49 | flutter_launcher_icons: ^0.13.1
50 | flutter_lints: ^4.0.0
51 | flutter_test:
52 | sdk: flutter
53 | freezed: ^2.0.3
54 | functional_widget: ^0.10.0
55 | json_serializable: ^6.0.1
56 | test: any
57 |
58 | flutter:
59 | uses-material-design: true
60 | generate: true
61 |
62 | flutter_icons:
63 | android: true
64 | ios: true
65 | image_path: assets/icon/icon.png
66 | adaptive_icon_background: assets/icon/icon_bg.png
67 | adaptive_icon_foreground: assets/icon/icon_fg.png
68 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | Voucher Vault
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | vouchervault
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(FLUTTER_BUILD_NUMBER)
25 | LSRequiresIPhoneOS
26 |
27 | NSCameraUsageDescription
28 | Camera permission is required for barcode scanning.
29 | UILaunchStoryboardName
30 | LaunchScreen
31 | UIMainStoryboardFile
32 | Main
33 | UIStatusBarStyle
34 | UIStatusBarStyleLightContent
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 |
43 | UIViewControllerBasedStatusBarAppearance
44 |
45 | NSFaceIDUsageDescription
46 | Face ID is being used to protect your vouchers.
47 | CADisableMinimumFrameDurationOnPhone
48 |
49 | UIApplicationSupportsIndirectInputEvents
50 |
51 |
52 |
--------------------------------------------------------------------------------
/lib/voucher_form/barcode_scanner/widgets/scanner_dialog.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'scanner_dialog.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class ScannerDialog extends HookWidget {
10 | const ScannerDialog({
11 | Key? key,
12 | required this.onScan,
13 | }) : super(key: key);
14 |
15 | final void Function(BarcodeResult) onScan;
16 |
17 | @override
18 | Widget build(BuildContext _context) => _scannerDialog(
19 | _context,
20 | onScan: onScan,
21 | );
22 | }
23 |
24 | class _PreviewDialog extends StatelessWidget {
25 | const _PreviewDialog({
26 | Key? key,
27 | required this.controller,
28 | required this.onPressedPicker,
29 | required this.onPressedFlash,
30 | }) : super(key: key);
31 |
32 | final Option controller;
33 |
34 | final void Function() onPressedPicker;
35 |
36 | final void Function() onPressedFlash;
37 |
38 | @override
39 | Widget build(BuildContext _context) => __previewDialog(
40 | _context,
41 | controller: controller,
42 | onPressedPicker: onPressedPicker,
43 | onPressedFlash: onPressedFlash,
44 | );
45 | }
46 |
47 | class _CameraPreview extends StatelessWidget {
48 | const _CameraPreview({
49 | Key? key,
50 | required this.controller,
51 | }) : super(key: key);
52 |
53 | final CameraController controller;
54 |
55 | @override
56 | Widget build(BuildContext _context) =>
57 | __cameraPreview(controller: controller);
58 | }
59 |
60 | class _FlashIcon extends HookWidget {
61 | const _FlashIcon({
62 | Key? key,
63 | required this.controller,
64 | }) : super(key: key);
65 |
66 | final CameraController controller;
67 |
68 | @override
69 | Widget build(BuildContext _context) => __flashIcon(controller: controller);
70 | }
71 |
--------------------------------------------------------------------------------
/lib/vouchers/dialog/voucher_dialog.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'voucher_dialog.dart';
4 |
5 | // **************************************************************************
6 | // FunctionalWidgetGenerator
7 | // **************************************************************************
8 |
9 | class VoucherDialog extends StatelessWidget {
10 | const VoucherDialog({
11 | Key? key,
12 | required this.voucher,
13 | required this.onTapBarcode,
14 | required this.onEdit,
15 | required this.onClose,
16 | required this.onRemove,
17 | required this.onSpend,
18 | }) : super(key: key);
19 |
20 | final Voucher voucher;
21 |
22 | final void Function() onTapBarcode;
23 |
24 | final void Function() onEdit;
25 |
26 | final void Function() onClose;
27 |
28 | final void Function() onRemove;
29 |
30 | final void Function() onSpend;
31 |
32 | @override
33 | Widget build(BuildContext _context) => _voucherDialog(
34 | _context,
35 | voucher: voucher,
36 | onTapBarcode: onTapBarcode,
37 | onEdit: onEdit,
38 | onClose: onClose,
39 | onRemove: onRemove,
40 | onSpend: onSpend,
41 | );
42 | }
43 |
44 | class _DialogWrap extends StatelessWidget {
45 | const _DialogWrap({
46 | Key? key,
47 | required this.theme,
48 | required this.child,
49 | }) : super(key: key);
50 |
51 | final ThemeData theme;
52 |
53 | final Widget child;
54 |
55 | @override
56 | Widget build(BuildContext _context) => __dialogWrap(
57 | _context,
58 | theme: theme,
59 | child: child,
60 | );
61 | }
62 |
63 | class _Barcode extends StatelessWidget {
64 | const _Barcode({
65 | Key? key,
66 | required this.type,
67 | required this.data,
68 | required this.onTap,
69 | }) : super(key: key);
70 |
71 | final VoucherCodeType type;
72 |
73 | final String data;
74 |
75 | final void Function() onTap;
76 |
77 | @override
78 | Widget build(BuildContext _context) => __barcode(
79 | _context,
80 | type: type,
81 | data: data,
82 | onTap: onTap,
83 | );
84 | }
85 |
--------------------------------------------------------------------------------
/lib/voucher_form/barcode_scanner/widgets/barcode_button.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: unnecessary_cast
2 | import 'package:auto_size_text/auto_size_text.dart';
3 | import 'package:barcode_widget/barcode_widget.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_elemental/flutter_elemental.dart';
6 | import 'package:functional_widget_annotation/functional_widget_annotation.dart';
7 | import 'package:vouchervault/app/index.dart';
8 | import 'package:vouchervault/lib/lib.dart';
9 | import 'package:flutter_gen/gen_l10n/app_localizations.dart';
10 |
11 | part 'barcode_button.g.dart';
12 |
13 | Option _barcodeWidget(Option type, Option code) =>
14 | type.map2(
15 | code,
16 | (type, String code) => Padding(
17 | padding: EdgeInsets.symmetric(horizontal: AppTheme.rem(0.5)),
18 | child: SizedBox(
19 | height: AppTheme.rem(5),
20 | child: BarcodeWidget(
21 | data: code,
22 | barcode: type,
23 | errorBuilder: (context, err) => const Text('Code not valid'),
24 | ),
25 | ),
26 | ),
27 | );
28 |
29 | Option _autoSizeText(String text) =>
30 | optionOfString(text).map((text) => AutoSizeText(
31 | text,
32 | style: const TextStyle(fontSize: 40),
33 | maxLines: 1,
34 | ) as Widget);
35 |
36 | @swidget
37 | Widget _barcodeButton(
38 | BuildContext context, {
39 | required Option barcodeType,
40 | required String data,
41 | required void Function() onPressed,
42 | }) =>
43 | TextButton(
44 | style: TextButton.styleFrom(
45 | backgroundColor: AppColors.lightGrey,
46 | foregroundColor: Colors.black,
47 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
48 | minimumSize: Size.fromHeight(AppTheme.rem(7)),
49 | ),
50 | onPressed: onPressed,
51 | child: Center(
52 | child: _barcodeWidget(barcodeType, optionOfString(data))
53 | .alt(() => _autoSizeText(data))
54 | .getOrElse(
55 | () => Text(AppLocalizations.of(context)!.scanBarcode),
56 | ),
57 | ),
58 | );
59 |
--------------------------------------------------------------------------------
/lib/vouchers/menu/vouchers_menu.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:functional_widget_annotation/functional_widget_annotation.dart';
3 | import 'package:flutter_gen/gen_l10n/app_localizations.dart';
4 |
5 | part 'vouchers_menu.g.dart';
6 |
7 | enum VouchersMenuAction {
8 | import,
9 | export,
10 | authentication(hasCheckbox: true),
11 | smartScan(hasCheckbox: true);
12 |
13 | const VouchersMenuAction({
14 | this.hasCheckbox = false,
15 | });
16 |
17 | String label(AppLocalizations locales) {
18 | switch (this) {
19 | case VouchersMenuAction.import:
20 | return locales.import;
21 | case VouchersMenuAction.export:
22 | return locales.export;
23 | case VouchersMenuAction.authentication:
24 | return locales.appLock;
25 | case VouchersMenuAction.smartScan:
26 | return locales.smartScan;
27 | }
28 | }
29 |
30 | final bool hasCheckbox;
31 | }
32 |
33 | @swidget
34 | Widget vouchersMenu({
35 | required void Function(VouchersMenuAction) onSelected,
36 | required Map values,
37 | required Set disabled,
38 | }) =>
39 | PopupMenuButton(
40 | onSelected: onSelected,
41 | itemBuilder: (context) => VouchersMenuAction.values
42 | .map>((a) => PopupMenuItem(
43 | value: a,
44 | enabled: !disabled.contains(a),
45 | child: Row(
46 | children: [
47 | Text(a.label(AppLocalizations.of(context)!)),
48 | if (a.hasCheckbox) ...[
49 | const Spacer(),
50 | Checkbox(
51 | value: values[a] ?? false,
52 | onChanged: disabled.contains(a)
53 | ? null
54 | : (_) {
55 | onSelected(a);
56 | Navigator.of(context).pop();
57 | },
58 | ),
59 | ]
60 | ],
61 | ),
62 | ))
63 | .toList(),
64 | );
65 |
--------------------------------------------------------------------------------
/test_driver/main_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:emulators/emulators.dart';
2 | import 'package:flutter_driver/flutter_driver.dart';
3 | import 'package:test/test.dart';
4 |
5 | Future main() async {
6 | final driver = await FlutterDriver.connect();
7 | final emu = await Emulators.build();
8 | final screenshot = emu.screenshotHelper(
9 | androidPath:
10 | 'android/fastlane/metadata/android/en-US/images/phoneScreenshots',
11 | iosPath: 'ios/fastlane/screenshots/en-AU',
12 | );
13 |
14 | setUpAll(() async {
15 | await driver.waitUntilFirstFrameRasterized();
16 | await screenshot.cleanStatusBar();
17 | await emu.toolchain.adb([
18 | "-s",
19 | screenshot.device!.state.id,
20 | "shell",
21 | "pm",
22 | "grant",
23 | "co.timsmart.vouchervault",
24 | "android.permission.CAMERA",
25 | ]).string();
26 | });
27 |
28 | // Close the connection to the driver after the tests have completed.
29 | tearDownAll(() async {
30 | await driver.close();
31 | });
32 |
33 | group('Screenshots', () {
34 | test('home screen', () async {
35 | await driver.waitFor(find.text('Vouchers'));
36 | await screenshot.capture('01');
37 | });
38 |
39 | test('walmart', () async {
40 | await driver.tap(find.ancestor(
41 | of: find.text('Walmart'),
42 | matching: find.byType('VoucherItem'),
43 | ));
44 | await driver.waitUntilNoTransientCallbacks();
45 | await screenshot.capture('02');
46 | });
47 |
48 | test('walmart spend', () async {
49 | await driver.tap(find.byValueKey('SpendIconButton'));
50 | await driver.waitUntilNoTransientCallbacks();
51 | await screenshot.capture('03');
52 |
53 | await driver.tap(find.text('Cancel'));
54 | await driver.waitUntilNoTransientCallbacks();
55 |
56 | await driver.tap(find.text('Close'));
57 | await driver.waitUntilNoTransientCallbacks();
58 | });
59 |
60 | final buttonFinder = find.byType('FloatingActionButton');
61 | test('form', () async {
62 | await driver.tap(buttonFinder);
63 | await driver.waitUntilNoTransientCallbacks();
64 | await driver.tap(find.text('Cancel'));
65 | await driver.waitUntilNoTransientCallbacks();
66 | await screenshot.capture('04');
67 | });
68 | });
69 | }
70 |
--------------------------------------------------------------------------------
/lib/app/voucher_vault_app.dart:
--------------------------------------------------------------------------------
1 | import 'package:dynamic_color/dynamic_color.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_elemental/flutter_elemental.dart';
4 | import 'package:form_builder_validators/form_builder_validators.dart';
5 | import 'package:functional_widget_annotation/functional_widget_annotation.dart';
6 | import 'package:vouchervault/app/index.dart';
7 | import 'package:vouchervault/auth/index.dart';
8 | import 'package:vouchervault/vouchers/index.dart';
9 | import 'package:flutter_localizations/flutter_localizations.dart';
10 | import 'package:flutter_gen/gen_l10n/app_localizations.dart';
11 |
12 | part 'voucher_vault_app.g.dart';
13 |
14 | final routeObserver = RouteObserver();
15 |
16 | @swidget
17 | Widget _voucherVaultApp(
18 | BuildContext context, {
19 | List initialValues = const [],
20 | }) =>
21 | AtomScope(
22 | initialValues: initialValues,
23 | child: const _App(),
24 | );
25 |
26 | @swidget
27 | Widget __app() {
28 | return DynamicColorBuilder(builder: (lightThemeSystem, darkThemeSystem) {
29 | final lightTheme = lightThemeSystem ??
30 | ColorScheme.fromSeed(
31 | brightness: Brightness.light,
32 | seedColor: Colors.red,
33 | );
34 | final darkTheme = darkThemeSystem ??
35 | ColorScheme.fromSeed(
36 | brightness: Brightness.dark,
37 | seedColor: Colors.red,
38 | );
39 |
40 | return AtomBuilder((context, watch, child) {
41 | final auth = watch(authState);
42 |
43 | return MaterialApp(
44 | debugShowCheckedModeBanner: false,
45 | theme: AppTheme.build(lightTheme),
46 | darkTheme: AppTheme.build(darkTheme),
47 | home: auth.when(
48 | unauthenticated: () => const AuthScreen(),
49 | authenticated: (_) => const VouchersScreen(),
50 | ),
51 | navigatorObservers: [routeObserver],
52 | localizationsDelegates: const [
53 | AppLocalizations.delegate,
54 | GlobalMaterialLocalizations.delegate,
55 | GlobalWidgetsLocalizations.delegate,
56 | GlobalCupertinoLocalizations.delegate,
57 | FormBuilderLocalizations.delegate,
58 | ],
59 | supportedLocales: AppLocalizations.supportedLocales,
60 | );
61 | });
62 | });
63 | }
64 |
--------------------------------------------------------------------------------
/lib/voucher_form/barcode_scanner/lib/camera_utils.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:ui';
3 |
4 | import 'package:camera/camera.dart';
5 | import 'package:flutter_elemental/flutter_elemental.dart';
6 | import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart';
7 | import 'package:google_mlkit_entity_extraction/google_mlkit_entity_extraction.dart';
8 | import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
9 | import 'package:rxdart/rxdart.dart';
10 | import 'package:vouchervault/lib/lib.dart';
11 |
12 | typedef CameraControllerWithImage = (CameraController, CameraImage);
13 |
14 | Stream cameraImageStream(
15 | CameraController c, {
16 | Duration throttleTime = const Duration(milliseconds: 250),
17 | int skipFrames = 3,
18 | }) {
19 | late StreamController sc;
20 |
21 | Future onStart() => c.startImageStream((image) => sc.add((c, image)));
22 |
23 | sc = StreamController(
24 | onListen: onStart,
25 | onCancel: c.stopImageStream,
26 | sync: true,
27 | );
28 |
29 | return sc.stream
30 | .throttleTime(throttleTime)
31 | .skip(skipFrames)
32 | .asBroadcastStream();
33 | }
34 |
35 | Option inputImage(
36 | CameraImage image, {
37 | required CameraDescription camera,
38 | }) =>
39 | Option.Do(($) {
40 | final rotation = $(Option.fromNullable(
41 | InputImageRotationValue.fromRawValue(camera.sensorOrientation),
42 | ));
43 | final format = $(Option.fromNullable(
44 | InputImageFormatValue.fromRawValue(image.format.raw),
45 | ));
46 | final plane = $(image.planes.head);
47 | return InputImage.fromBytes(
48 | bytes: plane.bytes,
49 | metadata: InputImageMetadata(
50 | format: format,
51 | rotation: rotation,
52 | size: Size(image.width.toDouble(), image.height.toDouble()),
53 | bytesPerRow: plane.bytesPerRow,
54 | ),
55 | );
56 | });
57 |
58 | final _neverController = StreamController.broadcast(sync: true);
59 | Stream neverStream() => _neverController.stream.cast();
60 |
61 | final pickInputImage = pickImage.flatMap((i) => ZIO
62 | .fromNullable(i.path)
63 | .mapError((_) => 'pickInputImage: pickImage returned empty path')
64 | .map(InputImage.fromFilePath));
65 |
--------------------------------------------------------------------------------
/lib/hooks/use_route_observer.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/widgets.dart';
2 | import 'package:flutter_elemental/flutter_elemental.dart';
3 | import 'package:flutter_hooks/flutter_hooks.dart';
4 |
5 | class _RouteObserverHook extends Hook {
6 | const _RouteObserverHook(
7 | this.routeObserver, {
8 | this.didPopNext = const Option.none(),
9 | this.didPush = const Option.none(),
10 | this.didPop = const Option.none(),
11 | this.didPushNext = const Option.none(),
12 | List