Compare commits

...

5 commits

Author SHA1 Message Date
afa09b85ff Updates 2025-12-15 17:37:44 +08:00
171635c753 Updated widgets 2025-12-15 17:37:38 +08:00
cbb142b0cf Updated pages 2025-12-15 17:37:26 +08:00
2bd32df8fd Changed animation on route 2025-12-15 17:37:18 +08:00
6fc81869a3 Added icon 2025-12-15 17:36:54 +08:00
9 changed files with 267 additions and 37 deletions

BIN
assets/icon/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

3
devtools_options.yaml Normal file
View file

@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

View file

@ -18,9 +18,38 @@ final _router = GoRouter(
initialLocation: '/', initialLocation: '/',
routes: [ routes: [
GoRoute(name: 'index', path: '/', builder: (context, state) => const IndexPage()), GoRoute(name: 'index', path: '/', builder: (context, state) => const IndexPage()),
GoRoute(name: 'login', path: '/login', builder: (context, state) => const LoginPage()), GoRoute(
name: 'login',
path: '/login',
builder: (context, state) => const LoginPage(),
pageBuilder: (BuildContext context, GoRouterState state) => CustomTransitionPage<void>(
key: state.pageKey,
child: const LoginPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return SlideTransition(
position: Tween<Offset>(begin: Offset(-1.0, 0.0), end: Offset.zero).animate(animation),
child: child,
);
},
),
),
GoRoute(name: 'approval', path: '/approval', builder: (context, state) => const ApprovalPage()), GoRoute(name: 'approval', path: '/approval', builder: (context, state) => const ApprovalPage()),
GoRoute(name: 'validate', path: '/validate', builder: (context, state) => const ValidatePage()), GoRoute(
name: 'validate',
path: '/validate',
builder: (context, state) => const ValidatePage(),
pageBuilder: (BuildContext context, GoRouterState state) => CustomTransitionPage<void>(
key: state.pageKey,
child: const ValidatePage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return SlideTransition(
position: Tween<Offset>(begin: Offset(1.0, 0.0), end: Offset.zero).animate(animation),
child: child,
);
},
),
),
// GoRoute(name: 'validate', path: '/validate', builder: (context, state) => const ValidatePage()),
GoRoute(name: 'result', path: '/result', builder: (context, state) => const ValidationResultPage()), GoRoute(name: 'result', path: '/result', builder: (context, state) => const ValidationResultPage()),
], ],
); );

View file

@ -53,14 +53,14 @@ class IndexPage extends StatelessWidget {
), ),
Padding( Padding(
padding: EdgeInsets.only(right: (screenWidth / 2) - 100), padding: EdgeInsets.only(right: (screenWidth / 2) - 100),
child: Row( child: const Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
TextWidget(text: "Mobile", color: Color.fromARGB(255, 244, 243, 243), bold: false, size: 16), TextWidget(text: "Mobile", color: Color.fromARGB(255, 244, 243, 243), bold: false, size: 16),
], ],
), ),
), ),
Gap(200), const MaxGap(200),
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
spacing: 32, spacing: 32,

View file

@ -1,7 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:ocbo_esign_validator/widgets/box_widget.dart';
import 'package:ocbo_esign_validator/widgets/text_widget.dart'; import 'package:ocbo_esign_validator/widgets/text_widget.dart';
import 'package:vibration/vibration.dart';
class ValidatePage extends StatelessWidget { class ValidatePage extends StatelessWidget {
const ValidatePage({super.key}); const ValidatePage({super.key});
@ -43,53 +46,114 @@ class _BarcodeScannerScreenState extends State<BarcodeScannerScreen> {
), ),
child: Center( child: Center(
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 88, left: 16, right: 16), padding: const EdgeInsets.only(top: 70, left: 16, right: 16),
child: Column( child: Column(
children: [ children: [
Container( Container(
padding: EdgeInsets.only(top: 8, bottom: 8, left: 20, right: 20), padding: EdgeInsets.only(top: 8, bottom: 8, left: 20, right: 20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Color.fromARGB(149, 16, 22, 28), color: const Color.fromRGBO(9, 13, 16, 0.623),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(32),
), ),
child: TextWidget(text: 'Scan OCBO e-Sign QR Code', size: 14, bold: true), child: const TextWidget(text: 'Scan OCBO e-Sign QR Code', size: 14, bold: true),
), ),
const Gap(50), const Gap(24),
SizedBox( Container(
height: 350, decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
boxShadow: [
const BoxShadow(
color: Color.fromRGBO(5, 5, 8, 0.341),
blurRadius: 8.0,
offset: Offset(4, 4), // left and up
),
const BoxShadow(
color: Color.fromRGBO(92, 71, 97, 0.373),
blurRadius: 8.0,
offset: Offset(-4, -4), // right and down
),
],
),
height: 330,
width: 340,
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(20), // Adjust the radius as needed borderRadius: BorderRadius.circular(24), // Adjust the radius as needed
child: MobileScanner( child: MobileScanner(
fit: BoxFit.cover, fit: BoxFit.cover,
onDetect: (BarcodeCapture capture) { onDetect: (BarcodeCapture capture) async {
final List<Barcode> barcodes = capture.barcodes; final List<Barcode> barcodes = capture.barcodes;
if (barcodes.isNotEmpty && barcodes.first.rawValue != null) { if (barcodes.isNotEmpty && barcodes.first.rawValue != null) {
setState(() { setState(() {
qrResult = barcodes.first.rawValue!; qrResult = barcodes.first.rawValue!;
}); });
if (await Vibration.hasVibrator()) {
Vibration.vibrate(duration: 100);
}
} }
}, },
), ),
), ),
), ),
const Gap(40), const Gap(40),
if (qrResult.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 16, right: 16),
child: Column(
children: [
Row(
children: [
Container( Container(
padding: EdgeInsets.all(0), padding: EdgeInsets.all(0),
width: 120, width: 90,
height: 120, height: 90,
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( color: const Color.fromRGBO(69, 191, 73, 0.1),
color: Color.fromRGBO(59, 169, 62, 1), border: Border.all(color: Color.fromRGBO(69, 191, 73, 1), width: 2),
width: 2,
), // Background color of the container
borderRadius: BorderRadius.circular(99), // Optional: rounded corners borderRadius: BorderRadius.circular(99), // Optional: rounded corners
), ),
child: Icon(Icons.thumb_up, color: const Color.fromRGBO(59, 169, 62, 1), size: 80), child: Icon(Icons.thumb_up, color: const Color.fromRGBO(69, 191, 73, 1), size: 48),
),
const Gap(32),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const TextWidget(
text: 'Verified',
bold: true,
size: 22,
color: Color.fromRGBO(69, 191, 73, 1),
), ),
const Gap(16), const Gap(16),
const TextWidget(text: 'Verified', size: 20, bold: true, color: Color.fromRGBO(59, 169, 62, 1)), const TextWidget(
text: 'QR is a valid OCBO e-Sign',
bold: false,
size: 16,
color: Color.fromRGBO(69, 191, 73, 1),
),
],
),
],
),
const Gap(32),
BoxWidget(
title: '',
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextWidget(text: 'Name', bold: true, size: 14),
const Gap(8),
TextWidget(text: dotenv.env['HEAD']!, bold: false, size: 14),
const Gap(16), const Gap(16),
TextWidget(text: qrResult, size: 20, bold: true), TextWidget(text: 'Role:', bold: true, size: 14),
const Gap(8),
TextWidget(text: 'APPROVER', bold: false, size: 14),
],
),
),
],
),
),
], ],
), ),
), ),

View file

@ -15,18 +15,23 @@ class BoxWidget extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
color: Color.fromRGBO(16, 22, 28, 0.584), color: Color.fromRGBO(16, 22, 28, 0.584),
border: Border.all(color: const Color.fromRGBO(32, 47, 61, 1)), border: Border.all(color: const Color.fromRGBO(41, 60, 78, 0.914)),
), ),
width: MediaQuery.of(context).size.width - 30, width: MediaQuery.of(context).size.width - 30,
// height: MediaQuery.of(context).size.height / 2.2, // height: MediaQuery.of(context).size.height / 2.2,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (title.isNotEmpty)
Column(
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [TextWidget(text: title, bold: true, size: 24)], children: [TextWidget(text: title, bold: true, size: 24)],
), ),
const Gap(16), const Gap(16),
],
),
Padding(padding: const EdgeInsets.all(16), child: content), Padding(padding: const EdgeInsets.all(16), child: content),
], ],
), ),

View file

@ -16,9 +16,9 @@ class MenuWidget extends StatelessWidget {
child: Container( child: Container(
padding: EdgeInsets.all(16), padding: EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(24),
color: Color.fromARGB(149, 16, 22, 28), color: Color.fromARGB(149, 16, 22, 28),
border: Border.all(color: const Color.fromRGBO(32, 47, 61, 1)), border: Border.all(color: const Color.fromRGBO(41, 60, 78, 0.914)),
), ),
width: 120, width: 120,
height: 120, height: 120,

View file

@ -1,6 +1,14 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
archive:
dependency: transitive
description:
name: archive
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
url: "https://pub.dev"
source: hosted
version: "4.0.7"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -41,6 +49,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
url: "https://pub.dev"
source: hosted
version: "2.0.4"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
url: "https://pub.dev"
source: hosted
version: "0.4.2"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@ -89,6 +113,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.11" version: "0.7.11"
device_info_plus:
dependency: transitive
description:
name: device_info_plus
sha256: "4df8babf73058181227e18b08e6ea3520cf5fc5d796888d33b7cb0f33f984b7c"
url: "https://pub.dev"
source: hosted
version: "12.3.0"
device_info_plus_platform_interface:
dependency: transitive
description:
name: device_info_plus_platform_interface
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
url: "https://pub.dev"
source: hosted
version: "7.0.3"
dio: dio:
dependency: "direct main" dependency: "direct main"
description: description:
@ -137,6 +177,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -158,6 +206,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.0" version: "6.0.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7"
url: "https://pub.dev"
source: hosted
version: "0.14.4"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -232,6 +288,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.2" version: "4.1.2"
image:
dependency: transitive
description:
name: image
sha256: "51555e36056541237b15b57afc31a0f53d4f9aefd9bd00873a6dc0090e54e332"
url: "https://pub.dev"
source: hosted
version: "4.6.0"
internet_connection_checker: internet_connection_checker:
dependency: "direct main" dependency: "direct main"
description: description:
@ -240,6 +304,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "3.0.1"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@ -416,6 +488,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.8" version: "2.1.8"
posix:
dependency: transitive
description:
name: posix
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
url: "https://pub.dev"
source: hosted
version: "6.0.3"
provider: provider:
dependency: transitive dependency: transitive
description: description:
@ -493,6 +573,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.2.0"
vibration:
dependency: "direct main"
description:
name: vibration
sha256: "1fd51cb0f91c6d512734ca0e282dd87fbc7f389b6da5f03c77709ba2cf8fa901"
url: "https://pub.dev"
source: hosted
version: "3.1.4"
vibration_platform_interface:
dependency: transitive
description:
name: vibration_platform_interface
sha256: "4134fbfcd427b59a7a91f8733292e4e9b29a7f1e8224ff0d80f5745fbf0743c6"
url: "https://pub.dev"
source: hosted
version: "0.1.1"
vm_service: vm_service:
dependency: transitive dependency: transitive
description: description:
@ -509,6 +605,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.1.1"
win32:
dependency: transitive
description:
name: win32
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
url: "https://pub.dev"
source: hosted
version: "5.15.0"
win32_registry:
dependency: transitive
description:
name: win32_registry
sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -525,6 +637,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.6.1" version: "6.6.1"
yaml:
dependency: transitive
description:
name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.3"
sdks: sdks:
dart: ">=3.9.2 <4.0.0" dart: ">=3.9.2 <4.0.0"
flutter: ">=3.35.0" flutter: ">=3.35.0"

View file

@ -21,12 +21,21 @@ dependencies:
dio_smart_retry: ^7.0.1 dio_smart_retry: ^7.0.1
hashlib: ^2.2.0 hashlib: ^2.2.0
flutter_bloc: ^9.1.1 flutter_bloc: ^9.1.1
vibration: ^3.1.4
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^6.0.0 flutter_lints: ^6.0.0
flutter_launcher_icons: ^0.14.3
flutter_icons:
android: true
ios: true
image_path: "assets/icon/icon.png"
remove_alpha_ios: true
min_sdk_android: 21
flutter: flutter:
uses-material-design: true uses-material-design: true