diff --git a/assets/icons/AR.svg b/assets/icons/AR.svg
new file mode 100644
index 0000000..6377ff7
--- /dev/null
+++ b/assets/icons/AR.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/flash-slash.svg b/assets/icons/flash-slash.svg
new file mode 100644
index 0000000..d9dead8
--- /dev/null
+++ b/assets/icons/flash-slash.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/flash.svg b/assets/icons/flash.svg
new file mode 100644
index 0000000..5b8d304
--- /dev/null
+++ b/assets/icons/flash.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/gallery.svg b/assets/icons/gallery.svg
new file mode 100644
index 0000000..a7b41a0
--- /dev/null
+++ b/assets/icons/gallery.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/keyboard.svg b/assets/icons/keyboard.svg
new file mode 100644
index 0000000..140512f
--- /dev/null
+++ b/assets/icons/keyboard.svg
@@ -0,0 +1,7 @@
+
diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart
index a6563cc..4b1d5bf 100644
--- a/lib/gen/assets.gen.dart
+++ b/lib/gen/assets.gen.dart
@@ -17,6 +17,9 @@ import 'package:vector_graphics/vector_graphics.dart' as _vg;
class $AssetsIconsGen {
const $AssetsIconsGen();
+ /// File path: assets/icons/AR.svg
+ SvgGenImage get ar => const SvgGenImage('assets/icons/AR.svg');
+
/// File path: assets/icons/Iran 1.svg
SvgGenImage get iran1 => const SvgGenImage('assets/icons/Iran 1.svg');
@@ -174,6 +177,13 @@ class $AssetsIconsGen {
/// File path: assets/icons/favorite.svg
SvgGenImage get favorite => const SvgGenImage('assets/icons/favorite.svg');
+ /// File path: assets/icons/flash-slash.svg
+ SvgGenImage get flashSlash =>
+ const SvgGenImage('assets/icons/flash-slash.svg');
+
+ /// File path: assets/icons/flash.svg
+ SvgGenImage get flash => const SvgGenImage('assets/icons/flash.svg');
+
/// File path: assets/icons/flat-color-icons_idea.svg
SvgGenImage get flatColorIconsIdea =>
const SvgGenImage('assets/icons/flat-color-icons_idea.svg');
@@ -190,6 +200,9 @@ class $AssetsIconsGen {
SvgGenImage get galleryAdd =>
const SvgGenImage('assets/icons/gallery-add.svg');
+ /// File path: assets/icons/gallery.svg
+ SvgGenImage get gallery => const SvgGenImage('assets/icons/gallery.svg');
+
/// File path: assets/icons/game 2.svg
SvgGenImage get game2 => const SvgGenImage('assets/icons/game 2.svg');
@@ -252,6 +265,9 @@ class $AssetsIconsGen {
SvgGenImage get iranCircle =>
const SvgGenImage('assets/icons/iran circle.svg');
+ /// File path: assets/icons/keyboard.svg
+ SvgGenImage get keyboard => const SvgGenImage('assets/icons/keyboard.svg');
+
/// File path: assets/icons/language-square.svg
SvgGenImage get languageSquare =>
const SvgGenImage('assets/icons/language-square.svg');
@@ -470,6 +486,7 @@ class $AssetsIconsGen {
/// List of all assets
List get values => [
+ ar,
iran1,
iran,
lBALogo,
@@ -516,10 +533,13 @@ class $AssetsIconsGen {
elementEqual,
emptyWallet,
favorite,
+ flashSlash,
+ flash,
flatColorIconsIdea,
flatColorIconsPlanner,
fluentColorLocationRipple16,
galleryAdd,
+ gallery,
game2,
gameIconsWinterGloves,
game,
@@ -537,6 +557,7 @@ class $AssetsIconsGen {
infoPic,
ionFastFoodOutline,
iranCircle,
+ keyboard,
languageSquare,
like,
link2,
diff --git a/lib/main.dart b/lib/main.dart
index a866c19..98c4a5d 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -37,7 +37,7 @@ class MyApp extends StatelessWidget {
themeManager.isDarkMode ? ThemeMode.dark : ThemeMode.light,
themeAnimationDuration: const Duration(milliseconds: 300),
themeAnimationCurve: Curves.easeInOutCubic,
- home: CoolSplashScreen(
+ home: SplashScreen(
nextScreen: const SimpleAuthGate(),
duration: const Duration(seconds: 3),
),
diff --git a/lib/screens/mains/hunt/providers/hunt_provider.dart b/lib/screens/mains/hunt/providers/hunt_provider.dart
index 85a4f19..f7d3124 100644
--- a/lib/screens/mains/hunt/providers/hunt_provider.dart
+++ b/lib/screens/mains/hunt/providers/hunt_provider.dart
@@ -128,10 +128,10 @@ class HuntState extends ChangeNotifier {
question: 'In the heart of a grand shopping center, where fountains dance and skiers gather in warmth. Look for a café where the scent of coffee and the aroma of books intertwine.',
answer: 'Kino Café',
description: 'A cozy book café in Isfahan City Center',
- targetLatitude: 32.62501010252744,
- targetLongitude: 51.72622026956878,
- hintLatitude: 32.62498106569981,
- hintLongitude: 51.725834603182015,
+ targetLatitude: 32.625042058762496,
+ targetLongitude: 51.7264112781341,
+ hintLatitude: 32.625042058762496,
+ hintLongitude: 51.7264112781341,
hintDescription: 'Our store is located on Chahar Bagh Abbasi Street',
),
HuntCard(
@@ -142,10 +142,10 @@ class HuntState extends ChangeNotifier {
question: 'Where spices tell stories of ancient Persia, and every dish carries the warmth of tradition. Seek the place where saffron meets hospitality.',
answer: 'Shahrzad Restaurant',
description: 'Traditional Persian cuisine restaurant',
- targetLatitude: 32.625,
- targetLongitude: 51.726,
- hintLatitude: 32.625,
- hintLongitude: 51.726,
+ targetLatitude: 32.625042058762496,
+ targetLongitude: 51.7264112781341,
+ hintLatitude: 32.625042058762496,
+ hintLongitude: 51.7264112781341,
hintDescription: 'Find the AR clue near the traditional architecture',
),
HuntCard(
@@ -156,10 +156,10 @@ class HuntState extends ChangeNotifier {
question: 'Where fashion meets elegance, and every thread tells a story of style. Find the boutique that dresses dreams.',
answer: 'Zara Store',
description: 'International fashion retailer',
- targetLatitude: 32.624,
- targetLongitude: 51.725,
- hintLatitude: 32.624,
- hintLongitude: 51.725,
+ targetLatitude: 32.625042058762496,
+ targetLongitude: 51.7264112781341,
+ hintLatitude: 32.625042058762496,
+ hintLongitude: 51.7264112781341,
hintDescription: 'Check the AR marker at the fashion district entrance',
),
HuntCard(
@@ -170,10 +170,10 @@ class HuntState extends ChangeNotifier {
question: 'In the digital realm where innovation never sleeps, discover the place where technology meets tomorrow.',
answer: 'TechnoCity',
description: 'Electronics and gadgets store',
- targetLatitude: 32.623,
- targetLongitude: 51.724,
- hintLatitude: 32.623,
- hintLongitude: 51.724,
+ targetLatitude: 32.625042058762496,
+ targetLongitude: 51.7264112781341,
+ hintLatitude: 32.625042058762496,
+ hintLongitude: 51.7264112781341,
hintDescription: 'Look for the AR guide near the tech display',
),
HuntCard(
@@ -184,10 +184,10 @@ class HuntState extends ChangeNotifier {
question: 'Where words dance on pages and knowledge finds its home. Seek the sanctuary of stories and wisdom.',
answer: 'Ketabsara Bookstore',
description: 'Literary paradise with rare collections',
- targetLatitude: 32.622,
- targetLongitude: 51.723,
- hintLatitude: 32.622,
- hintLongitude: 51.723,
+ targetLatitude: 32.625042058762496,
+ targetLongitude: 51.7264112781341,
+ hintLatitude: 32.625042058762496,
+ hintLongitude: 51.7264112781341,
hintDescription: 'Find the AR clue in the literature section',
),
]..sort((a, b) => b.points.compareTo(a.points));
diff --git a/lib/screens/mains/hunt/widgets/hint_camera_widget.dart b/lib/screens/mains/hunt/widgets/hint_camera_widget.dart
index 651ed02..6897907 100644
--- a/lib/screens/mains/hunt/widgets/hint_camera_widget.dart
+++ b/lib/screens/mains/hunt/widgets/hint_camera_widget.dart
@@ -50,14 +50,22 @@ class _HintCameraWidgetState extends State
double _distanceToTarget = 0.0;
Position? _currentPosition;
bool _isNearTarget = false;
+ bool _wasNearTarget = false;
bool _isMapVisible = false;
Duration _currentRemainingTime = Duration.zero;
late AnimationController _rotationController;
late AnimationController _scanController;
+ late AnimationController _entranceController;
late Animation _rotationAnimation;
late Animation _scanAnimation;
+ late Animation _entranceOpacityAnimation;
+ late Animation _entranceScaleAnimation;
+ late Animation _entranceSlideAnimation;
+
+ late AnimationController _transformController;
+ late Animation _transformAnimation;
final double _cameraHorizontalFov = 60.0;
@@ -112,6 +120,11 @@ class _HintCameraWidgetState extends State
duration: const Duration(milliseconds: 3500),
vsync: this,
)..repeat();
+
+ _entranceController = AnimationController(
+ duration: const Duration(milliseconds: 1500),
+ vsync: this,
+ );
_rotationAnimation = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _rotationController, curve: Curves.linear),
@@ -119,6 +132,44 @@ class _HintCameraWidgetState extends State
_scanAnimation = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _scanController, curve: Curves.easeInOutSine),
);
+
+ _entranceOpacityAnimation = Tween(begin: 0.0, end: 1.0).animate(
+ CurvedAnimation(
+ parent: _entranceController,
+ curve: const Interval(0.0, 0.8, curve: Curves.easeOut),
+ ),
+ );
+
+ _entranceScaleAnimation = Tween(begin: 0.3, end: 1.0).animate(
+ CurvedAnimation(
+ parent: _entranceController,
+ curve: const Interval(0.0, 0.8, curve: Curves.elasticOut),
+ ),
+ );
+
+ _entranceSlideAnimation = Tween(
+ begin: const Offset(0.0, 0.5),
+ end: Offset.zero,
+ ).animate(
+ CurvedAnimation(
+ parent: _entranceController,
+ curve: const Interval(0.2, 1.0, curve: Curves.elasticOut),
+ ),
+ );
+
+ _transformController = AnimationController(
+ duration: const Duration(milliseconds: 800),
+ vsync: this,
+ );
+
+ _transformAnimation = Tween(begin: 0.0, end: 1.0).animate(
+ CurvedAnimation(
+ parent: _transformController,
+ curve: Curves.elasticOut,
+ ),
+ );
+
+ _entranceController.forward();
}
void _startCompassListening() {
@@ -151,7 +202,19 @@ class _HintCameraWidgetState extends State
_currentPosition = position;
_bearingToTarget = bearing;
_distanceToTarget = distance;
- _isNearTarget = isNear;
+
+ if (_isNearTarget != isNear) {
+ _wasNearTarget = _isNearTarget;
+ _isNearTarget = isNear;
+
+ if (isNear && !_wasNearTarget) {
+ _transformController.forward();
+ } else if (!isNear && _wasNearTarget) {
+ _transformController.reverse();
+ }
+ } else {
+ _isNearTarget = isNear;
+ }
});
if (_isMapVisible) {
_updateMiniMapCamera();
@@ -215,10 +278,25 @@ class _HintCameraWidgetState extends State
_controller?.dispose();
_rotationController.dispose();
_scanController.dispose();
+ _entranceController.dispose();
+ _transformController.dispose();
_miniMapController.dispose();
super.dispose();
}
+ void _showARHintInfoDialog() {
+ showDialog(
+ context: context,
+ barrierDismissible: true,
+ barrierColor: Colors.black.withOpacity(0.7),
+ builder: (BuildContext context) {
+ return const _ARHintInfoDialog();
+ },
+ );
+ }
+
+
+
Widget _buildMiniMap() {
if (_currentPosition == null) return const SizedBox.shrink();
final userLocation =
@@ -285,159 +363,198 @@ class _HintCameraWidgetState extends State
@override
Widget build(BuildContext context) {
- return Scaffold(
- backgroundColor: Colors.black,
- extendBodyBehindAppBar: true,
- appBar: PreferredSize(
- preferredSize: const Size.fromHeight(kToolbarHeight + 10),
- child: Container(
- decoration: BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- colors: [
- Colors.black.withOpacity(0.8),
- Colors.black.withOpacity(0.4),
- Colors.transparent,
- ],
- ),
- ),
- child: ClipRRect(
- child: BackdropFilter(
- filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12),
- child: AppBar(
- backgroundColor: Colors.transparent,
- elevation: 0,
- toolbarHeight: kToolbarHeight + 10,
- leading: Container(
- margin: const EdgeInsets.all(8),
- decoration: BoxDecoration(
- gradient: LinearGradient(
- colors: [
- Colors.white.withOpacity(0.15),
- Colors.white.withOpacity(0.05),
- ],
- ),
- shape: BoxShape.circle,
- border: Border.all(
- color: Colors.white.withOpacity(0.3),
- width: 1.5,
- ),
- boxShadow: [
- BoxShadow(
- color: Colors.black.withOpacity(0.3),
- blurRadius: 8,
- spreadRadius: 1,
- ),
- ],
- ),
- child: IconButton(
- onPressed: widget.onClose,
- icon: SvgPicture.asset(Assets.icons.back.path,
- colorFilter:
- const ColorFilter.mode(Colors.white, BlendMode.srcIn)),
- padding: EdgeInsets.zero,
- constraints: const BoxConstraints(),
- ),
- ),
- title: Container(
- padding:
- const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
- decoration: BoxDecoration(
- gradient: LinearGradient(
- colors: [
- AppColors.primary.withOpacity(0.3),
- AppColors.primary.withOpacity(0.1),
- ],
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- ),
- borderRadius: BorderRadius.circular(25),
- border: Border.all(
- color: AppColors.primary.withOpacity(0.4),
- width: 1.5,
- ),
- boxShadow: [
- BoxShadow(
- color: AppColors.primary.withOpacity(0.2),
- blurRadius: 15,
- spreadRadius: 2,
- ),
- BoxShadow(
- color: Colors.black.withOpacity(0.3),
- blurRadius: 8,
- offset: const Offset(0, 4),
- ),
- ],
- ),
- child: const Text(
- 'AR Hint',
- style: TextStyle(
- color: Colors.white,
- fontSize: 15,
- fontWeight: FontWeight.w700,
- letterSpacing: 1.2,
- fontFamily: 'monospace'),
- ),
- ),
- centerTitle: true,
- actions: [
- Container(
- margin: const EdgeInsets.all(10),
+ return AnimatedBuilder(
+ animation: _entranceController,
+ builder: (context, child) {
+ return FadeTransition(
+ opacity: _entranceOpacityAnimation,
+ child: SlideTransition(
+ position: _entranceSlideAnimation,
+ child: ScaleTransition(
+ scale: _entranceScaleAnimation,
+ child: Scaffold(
+ backgroundColor: Colors.black,
+ extendBodyBehindAppBar: true,
+ appBar: PreferredSize(
+ preferredSize: const Size.fromHeight(kToolbarHeight + 10),
+ child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
colors: [
- Colors.white.withOpacity(0.15),
- Colors.white.withOpacity(0.05),
+ Colors.black.withOpacity(0.8),
+ Colors.black.withOpacity(0.4),
+ Colors.transparent,
],
),
- shape: BoxShape.circle,
- border: Border.all(
- color: Colors.white.withOpacity(0.3),
- width: 1.5,
- ),
- boxShadow: [
- BoxShadow(
- color: Colors.black.withOpacity(0.3),
- blurRadius: 8,
- spreadRadius: 1,
- ),
- ],
),
- child: IconButton(
- onPressed: () {},
- icon: SvgPicture.asset(Assets.icons.infoCircle.path,
- colorFilter: const ColorFilter.mode(
- Colors.white, BlendMode.srcIn)),
+ child: ClipRRect(
+ child: BackdropFilter(
+ filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12),
+ child: AppBar(
+ backgroundColor: Colors.transparent,
+ elevation: 0,
+ toolbarHeight: kToolbarHeight + 10,
+ leading: Container(
+ margin: const EdgeInsets.all(8),
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ colors: [
+ Colors.white.withOpacity(0.08),
+ Colors.white.withOpacity(0.05),
+ ],
+ ),
+ shape: BoxShape.circle,
+ border: Border.all(
+ color: Colors.white.withOpacity(0.3),
+ width: 1.5,
+ ),
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(0.3),
+ blurRadius: 8,
+ spreadRadius: 1,
+ ),
+ ],
+ ),
+ child: IconButton(
+ onPressed: widget.onClose,
+ icon: SvgPicture.asset(Assets.icons.back.path,
+ colorFilter:
+ const ColorFilter.mode(Colors.white, BlendMode.srcIn)),
+ padding: EdgeInsets.zero,
+ constraints: const BoxConstraints(),
+ ),
+ ),
+ title: AnimatedBuilder(
+ animation: _entranceController,
+ builder: (context, child) {
+ return Transform.scale(
+ scale: 0.8 + (0.2 * _entranceScaleAnimation.value),
+ child: Container(
+ padding:
+ const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ colors: [
+ AppColors.primary.withOpacity(0.3 * _entranceOpacityAnimation.value),
+ AppColors.primary.withOpacity(0.1 * _entranceOpacityAnimation.value),
+ ],
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ ),
+ borderRadius: BorderRadius.circular(25),
+ border: Border.all(
+ color: AppColors.primary.withOpacity(0.4 * _entranceOpacityAnimation.value),
+ width: 1.5,
+ ),
+ boxShadow: [
+ BoxShadow(
+ color: AppColors.primary.withOpacity(0.2 * _entranceOpacityAnimation.value),
+ blurRadius: 15,
+ spreadRadius: 2,
+ ),
+ BoxShadow(
+ color: Colors.black.withOpacity(0.3),
+ blurRadius: 8,
+ offset: const Offset(0, 4),
+ ),
+ ],
+ ),
+ child: Text(
+ 'AR Hint',
+ style: TextStyle(
+ color: Colors.white.withOpacity(_entranceOpacityAnimation.value),
+ fontSize: 15,
+ fontWeight: FontWeight.w700,
+ letterSpacing: 1.2,
+ fontFamily: 'monospace'),
+ ),
+ ),
+ );
+ },
+ ),
+ centerTitle: true,
+ actions: [
+ Container(
+ margin: const EdgeInsets.all(10),
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ colors: [
+ Colors.white.withOpacity(0.10),
+ Colors.white.withOpacity(0.05),
+ ],
+ ),
+ shape: BoxShape.circle,
+ border: Border.all(
+ color: Colors.white.withOpacity(0.3),
+ width: 1.5,
+ ),
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(0.3),
+ blurRadius: 8,
+ spreadRadius: 1,
+ ),
+ ],
+ ),
+ child: IconButton(
+ onPressed: _showARHintInfoDialog,
+ icon: SvgPicture.asset(Assets.icons.infoCircle.path,
+ colorFilter: const ColorFilter.mode(
+ Colors.white, BlendMode.srcIn)),
+ ),
+ ),
+ ],
+ ),
+ ),
),
),
- ],
+ ),
+ body: Stack(
+ children: [
+ if (_controller != null)
+ MobileScanner(controller: _controller!)
+ else
+ Container(
+ color: Colors.black,
+ child: Center(
+ child: CircularProgressIndicator(color: AppColors.primary),
+ ),
+ ),
+ BackdropFilter(
+ filter: ImageFilter.blur(sigmaX: 0.5, sigmaY: 0.5),
+ child: Container(color: Colors.black.withOpacity(0.1)),
+ ),
+ if (widget.remainingTime != null) _buildTimerWidget(),
+ _buildAROverlay(),
+ _buildMiniMap(),
+ ],
+ ),
+ floatingActionButton: AnimatedBuilder(
+ animation: _entranceController,
+ builder: (context, child) {
+ return Transform.translate(
+ offset: Offset(0, 100 * (1 - _entranceScaleAnimation.value)),
+ child: Opacity(
+ opacity: _entranceOpacityAnimation.value,
+ child: Transform.scale(
+ scale: _entranceScaleAnimation.value,
+ child: Padding(
+ padding: const EdgeInsets.only(right: 8.0),
+ child: _buildMapFAB(),
+ ),
+ ),
+ ),
+ );
+ },
+ ),
),
),
),
- ),
- ),
- body: Stack(
- children: [
- if (_controller != null)
- MobileScanner(controller: _controller!)
- else
- Container(
- color: Colors.black,
- child: Center(
- child: CircularProgressIndicator(color: AppColors.primary),
- ),
- ),
- BackdropFilter(
- filter: ImageFilter.blur(sigmaX: 0.5, sigmaY: 0.5),
- child: Container(color: Colors.black.withOpacity(0.1)),
- ),
- if (widget.remainingTime != null) _buildTimerWidget(),
- _buildAROverlay(),
- _buildMiniMap(),
- ],
- ),
- floatingActionButton: _buildMapFAB(),
+ );
+ },
);
}
@@ -456,44 +573,56 @@ class _HintCameraWidgetState extends State
final isExpired =
_currentRemainingTime.inSeconds <= 0 && widget.remainingTime!.inSeconds > 0;
- return Positioned(
- top: kToolbarHeight + 60,
- right: 16,
- child: ClipRRect(
- borderRadius: BorderRadius.circular(20),
- child: BackdropFilter(
- filter: ImageFilter.blur(sigmaX: 25, sigmaY: 25),
- child: Container(
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
- decoration: BoxDecoration(
- color: Colors.transparent,
- borderRadius: BorderRadius.circular(20),
- border: Border.all(
- color: Colors.white.withOpacity(0.2),
- width: 0.5,
- ),
- ),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text(
- isExpired
- ? 'Expired'
- : hours > 0
- ? '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}'
- : '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}',
- style: TextStyle(
- color: isExpired ? const Color(0xFFFF3B30) : Colors.white,
- fontSize: 15,
- fontWeight: FontWeight.w600,
- letterSpacing: 0.5,
+ return AnimatedBuilder(
+ animation: _entranceController,
+ builder: (context, child) {
+ return Positioned(
+ top: kToolbarHeight + 60,
+ right: 16 - (100 * (1 - _entranceOpacityAnimation.value)),
+ child: Opacity(
+ opacity: _entranceOpacityAnimation.value,
+ child: Transform.scale(
+ scale: _entranceScaleAnimation.value,
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(20),
+ child: BackdropFilter(
+ filter: ImageFilter.blur(sigmaX: 25, sigmaY: 25),
+ child: Container(
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ decoration: BoxDecoration(
+ color: Colors.transparent,
+ borderRadius: BorderRadius.circular(20),
+ border: Border.all(
+ color: Colors.white.withOpacity(0.2 * _entranceOpacityAnimation.value),
+ width: 0.5,
+ ),
+ ),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ isExpired
+ ? 'Expired'
+ : hours > 0
+ ? '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}'
+ : '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}',
+ style: TextStyle(
+ color: (isExpired ? const Color(0xFFFF3B30) : Colors.white)
+ .withOpacity(_entranceOpacityAnimation.value),
+ fontSize: 15,
+ fontWeight: FontWeight.w600,
+ letterSpacing: 0.5,
+ ),
+ ),
+ ],
+ ),
),
),
- ],
+ ),
),
),
- ),
- ),
+ );
+ },
);
}
@@ -535,16 +664,33 @@ class _HintCameraWidgetState extends State
Widget _buildAROverlay() {
if (_currentPosition == null) {
- return const Center(
- child: Text(
- 'ACQUIRING GPS SIGNAL...',
- style: TextStyle(
- color: Colors.white70,
- fontSize: 16,
- fontWeight: FontWeight.bold,
- letterSpacing: 1.5,
- ),
- ),
+ return AnimatedBuilder(
+ animation: _entranceController,
+ builder: (context, child) {
+ return Center(
+ child: FadeTransition(
+ opacity: _entranceOpacityAnimation,
+ child: SlideTransition(
+ position: Tween(
+ begin: const Offset(0.0, -0.3),
+ end: Offset.zero,
+ ).animate(CurvedAnimation(
+ parent: _entranceController,
+ curve: const Interval(0.4, 1.0, curve: Curves.elasticOut),
+ )),
+ child: const Text(
+ 'ACQUIRING GPS SIGNAL...',
+ style: TextStyle(
+ color: Colors.white70,
+ fontSize: 16,
+ fontWeight: FontWeight.bold,
+ letterSpacing: 1.5,
+ ),
+ ),
+ ),
+ ),
+ );
+ },
);
}
@@ -558,22 +704,70 @@ class _HintCameraWidgetState extends State
bool isVisible = angleDifference.abs() < (_cameraHorizontalFov / 2);
- return Stack(
- children: [
- CustomPaint(
- painter: HudPainter(animationValue: 1.0),
- size: Size.infinite,
+ return AnimatedBuilder(
+ animation: _entranceController,
+ builder: (context, child) {
+ return Opacity(
+ opacity: _entranceOpacityAnimation.value,
+ child: Stack(
+ children: [
+ Transform.scale(
+ scale: _entranceScaleAnimation.value,
+ child: CustomPaint(
+ painter: HudPainter(animationValue: _entranceOpacityAnimation.value),
+ size: Size.infinite,
+ ),
+ ),
+ if (!isVisible)
+ _buildDirectionalOverlay(angleDifference),
+ if (!isVisible)
+ _buildOffScreenIndicator(angleDifference)
+ else
+ _buildOnScreenHint(angleDifference),
+ ],
+ ),
+ );
+ },
+ );
+ }
+
+
+
+ Widget _buildDirectionalOverlay(double angleDifference) {
+ final isLeft = angleDifference < 0;
+ final overlayColor = _isNearTarget
+ ? const Color(0xFF00E676)
+ : AppColors.primary;
+
+ return Align(
+ alignment: isLeft ? Alignment.centerLeft : Alignment.centerRight,
+ child: Container(
+ width: MediaQuery.of(context).size.width * 0.13,
+ height: double.infinity,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: isLeft ? Alignment.centerLeft : Alignment.centerRight,
+ end: isLeft ? Alignment.centerRight : Alignment.centerLeft,
+ stops: const [0.0, 0.2, 0.4, 0.7, 1.0],
+ colors: [
+ overlayColor.withOpacity(0.45),
+ overlayColor.withOpacity(0.30),
+ overlayColor.withOpacity(0.15),
+ overlayColor.withOpacity(0.05),
+ Colors.transparent,
+ ],
+ ),
),
- if (!isVisible)
- _buildOffScreenIndicator(angleDifference)
- else
- _buildOnScreenHint(angleDifference),
- ],
+ ),
);
}
Widget _buildOffScreenIndicator(double angleDifference) {
final isLeft = angleDifference < 0;
+ final arrowColor = _isNearTarget
+ ? const Color(0xFF00E676)
+ : AppColors.primary;
+
return Align(
alignment: isLeft ? Alignment.centerLeft : Alignment.centerRight,
child: Padding(
@@ -582,10 +776,10 @@ class _HintCameraWidgetState extends State
isLeft
? Icons.arrow_back_ios_new_rounded
: Icons.arrow_forward_ios_rounded,
- color: AppColors.primary.withOpacity(0.8),
+ color: arrowColor.withOpacity(0.8),
size: 48,
shadows: [
- Shadow(color: AppColors.primary.withOpacity(0.5), blurRadius: 15),
+ Shadow(color: arrowColor.withOpacity(0.5), blurRadius: 15),
],
),
),
@@ -603,7 +797,7 @@ class _HintCameraWidgetState extends State
bottom: 0,
left: (1 + screenX) * (MediaQuery.of(context).size.width / 2) - 100,
child: AnimatedBuilder(
- animation: Listenable.merge([_rotationController, _scanAnimation]),
+ animation: Listenable.merge([_rotationAnimation, _scanAnimation, _transformAnimation]),
builder: (context, child) {
return SizedBox(
width: 200,
@@ -613,8 +807,9 @@ class _HintCameraWidgetState extends State
isNear: _isNearTarget,
distance: distance,
pulseValue: 1.0,
- rotationValue: _rotationController.value,
+ rotationValue: _rotationAnimation.value,
scanValue: _scanAnimation.value,
+ transformValue: _transformAnimation.value,
),
child: Center(
child: ClipRRect(
@@ -624,7 +819,7 @@ class _HintCameraWidgetState extends State
child: Container(
width: 140,
height: 140,
- padding: const EdgeInsets.all(12),
+ padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
shape: BoxShape.circle,
@@ -639,16 +834,26 @@ class _HintCameraWidgetState extends State
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
- Text(
- _isNearTarget ? "TARGET ACQUIRED" : "SCANNING",
- style: TextStyle(
- color: _isNearTarget
- ? const Color(0xFF00E676)
- : const Color(0xFF2196F3),
- fontWeight: FontWeight.w600,
- fontSize: 12,
- letterSpacing: 1.5,
- fontFamily: 'monospace'),
+ Flexible(
+ child: Container(
+ padding: const EdgeInsets.symmetric(horizontal: 4),
+ child: FittedBox(
+ fit: BoxFit.scaleDown,
+ child: Text(
+ _isNearTarget ? "TARGET\nACQUIRED" : "SCANNING",
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ color: _isNearTarget
+ ? const Color(0xFF00E676)
+ : const Color(0xFF2196F3),
+ fontWeight: FontWeight.w600,
+ fontSize: _isNearTarget ? 9 : 12,
+ letterSpacing: _isNearTarget ? 0.5 : 1.5,
+ height: _isNearTarget ? 1.1 : 1.0,
+ fontFamily: 'monospace'),
+ ),
+ ),
+ ),
),
const SizedBox(height: 12),
Text(
@@ -780,6 +985,7 @@ class AdvancedTargetPainter extends CustomPainter {
final double pulseValue;
final double rotationValue;
final double scanValue;
+ final double transformValue;
AdvancedTargetPainter({
required this.isNear,
@@ -787,14 +993,18 @@ class AdvancedTargetPainter extends CustomPainter {
required this.pulseValue,
required this.rotationValue,
required this.scanValue,
+ required this.transformValue,
});
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final paint = Paint()..style = PaintingStyle.stroke;
- final primaryColor =
- isNear ? const Color(0xFF00E676) : const Color(0xFF2196F3);
+
+ final scannerColor = const Color(0xFF2196F3);
+ final targetColor = const Color(0xFF00E676);
+ final primaryColor = Color.lerp(scannerColor, targetColor, transformValue)!;
+
final baseRadius = math.max(60.0, math.min(85.0, 100 - (distance / 2)));
paint
@@ -818,10 +1028,11 @@ class AdvancedTargetPainter extends CustomPainter {
..strokeWidth = 1.5;
canvas.drawCircle(center, baseRadius - 15, paint);
- if (!isNear) {
+ if (!isNear || transformValue < 1.0) {
final sweepAngle = scanValue * 2 * math.pi;
+ final scannerOpacity = isNear ? (1.0 - transformValue) : 1.0;
paint
- ..color = primaryColor
+ ..color = scannerColor.withOpacity(scannerOpacity)
..strokeWidth = 4.0;
canvas.drawArc(
Rect.fromCircle(center: center, radius: baseRadius - 8),
@@ -832,15 +1043,197 @@ class AdvancedTargetPainter extends CustomPainter {
);
}
- if (isNear) {
+ if (isNear || transformValue > 0.0) {
+ final targetOpacity = isNear ? transformValue : (1.0 - transformValue);
+
paint
- ..color = primaryColor.withOpacity(1.0 - pulseValue)
+ ..color = targetColor.withOpacity((1.0 - pulseValue) * targetOpacity)
..strokeWidth = 4.0
..style = PaintingStyle.stroke;
canvas.drawCircle(center, baseRadius + (pulseValue * 15), paint);
+
+ final crosshairScale = transformValue;
+ final crosshairSize = 20.0 * crosshairScale;
+ paint
+ ..color = targetColor.withOpacity(targetOpacity)
+ ..strokeWidth = 3.0;
+
+ canvas.drawLine(
+ Offset(center.dx - crosshairSize, center.dy),
+ Offset(center.dx + crosshairSize, center.dy),
+ paint,
+ );
+
+ canvas.drawLine(
+ Offset(center.dx, center.dy - crosshairSize),
+ Offset(center.dx, center.dy + crosshairSize),
+ paint,
+ );
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
+}
+
+class _ARHintInfoDialog extends StatefulWidget {
+ const _ARHintInfoDialog({Key? key}) : super(key: key);
+
+ @override
+ State<_ARHintInfoDialog> createState() => _ARHintInfoDialogState();
+}
+
+class _ARHintInfoDialogState extends State<_ARHintInfoDialog>
+ with SingleTickerProviderStateMixin {
+ late AnimationController _controller;
+ late Animation _scaleAnimation;
+ late Animation _fadeAnimation;
+ late Animation _iconScaleAnimation;
+
+ @override
+ void initState() {
+ super.initState();
+ _controller = AnimationController(
+ vsync: this,
+ duration: const Duration(milliseconds: 600),
+ );
+
+ _scaleAnimation = Tween(
+ begin: 0.0,
+ end: 1.0,
+ ).animate(CurvedAnimation(
+ parent: _controller,
+ curve: Curves.elasticOut,
+ ));
+
+ _fadeAnimation = Tween(
+ begin: 0.0,
+ end: 1.0,
+ ).animate(CurvedAnimation(
+ parent: _controller,
+ curve: const Interval(0.0, 0.8, curve: Curves.easeOut),
+ ));
+
+ _iconScaleAnimation = Tween(
+ begin: 0.0,
+ end: 1.0,
+ ).animate(CurvedAnimation(
+ parent: _controller,
+ curve: const Interval(0.2, 1.0, curve: Curves.bounceOut),
+ ));
+
+ _controller.forward();
+ }
+
+ @override
+ void dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return FadeTransition(
+ opacity: _fadeAnimation,
+ child: ScaleTransition(
+ scale: _scaleAnimation,
+ child: Dialog(
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(15),
+ ),
+ elevation: 10,
+ backgroundColor: AppColors.surface,
+ child: Stack(
+ clipBehavior: Clip.none,
+ alignment: Alignment.topCenter,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(
+ top: 50,
+ left: 20,
+ right: 20,
+ bottom: 20,
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ const Text(
+ "AR Hint Guide",
+ style: TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.bold,
+ ),
+ textAlign: TextAlign.center,
+ ),
+ const SizedBox(height: 15),
+ Text(
+ "• Point camera around to locate target\n• Blue circle shows target direction\n• Arrows guide you when target is off-screen\n• Circle turns green when target found",
+ style: TextStyle(
+ color: AppColors.popupText,
+ fontSize: 15,
+ height: 1.5,
+ fontWeight: FontWeight.w500,
+ ),
+ textAlign: TextAlign.left,
+ ),
+ const SizedBox(height: 25),
+ SizedBox(
+ width: double.infinity,
+ child: ElevatedButton(
+ style: ElevatedButton.styleFrom(
+ backgroundColor: AppColors.primary,
+ foregroundColor: AppColors.textPrimary,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(8),
+ ),
+ padding: const EdgeInsets.symmetric(vertical: 12),
+ ),
+ onPressed: () async {
+ await _controller.reverse();
+ if (mounted) Navigator.of(context).pop();
+ },
+ child: const Text("Got it"),
+ ),
+ ),
+ ],
+ ),
+ ),
+ Positioned(
+ top: -40,
+ child: Container(
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ color: AppColors.surface,
+ boxShadow: [
+ BoxShadow(
+ color: AppColors.shadowColor,
+ blurRadius: 8,
+ offset: const Offset(0, 4),
+ ),
+ ],
+ ),
+ child: CircleAvatar(
+ backgroundColor: AppColors.surface,
+ radius: 40,
+ child: ScaleTransition(
+ scale: _iconScaleAnimation,
+ child: SvgPicture.asset(
+ Assets.icons.ar.path,
+ colorFilter: ColorFilter.mode(
+ AppColors.primary,
+ BlendMode.srcIn,
+ ),
+ width: 35,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
}
\ No newline at end of file
diff --git a/lib/screens/mains/hunt/widgets/hunt_card_widget.dart b/lib/screens/mains/hunt/widgets/hunt_card_widget.dart
index fc946ca..411536f 100644
--- a/lib/screens/mains/hunt/widgets/hunt_card_widget.dart
+++ b/lib/screens/mains/hunt/widgets/hunt_card_widget.dart
@@ -731,7 +731,7 @@ class _HuntCardWidgetState extends State
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
- Assets.icons.infoCircle.path,
+ Assets.icons.ar.path,
color: AppColors.isDarkMode
? Colors.yellow.shade600
: const Color(0xFFE65100),
diff --git a/lib/screens/mains/profile/profile.dart b/lib/screens/mains/profile/profile.dart
index fc13975..6e0dace 100644
--- a/lib/screens/mains/profile/profile.dart
+++ b/lib/screens/mains/profile/profile.dart
@@ -26,7 +26,7 @@ class _ProfileState extends State with TickerProviderStateMixin {
late AnimationController _animationController;
late List> _animations;
- String _currentLanguage = '🇺🇲 English';
+ String _currentLanguage = '🇺🇲 English';
String _currentFlag = 'assets/icons/usa circle.svg';
final GlobalKey _languageTileKey = GlobalKey();
diff --git a/lib/screens/qr_scanner/qr_scanner_page.dart b/lib/screens/qr_scanner/qr_scanner_page.dart
index 32b2374..b336ebb 100644
--- a/lib/screens/qr_scanner/qr_scanner_page.dart
+++ b/lib/screens/qr_scanner/qr_scanner_page.dart
@@ -1,4 +1,6 @@
import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:lba/gen/assets.gen.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
@@ -179,11 +181,7 @@ class _QRScannerPageState extends State {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
- icon: Icon(
- Icons.arrow_back,
- color: AppColors.textPrimary,
- size: 28,
- ),
+ icon: SvgPicture.asset(Assets.icons.back.path,color: AppColors.textPrimary, width: 28, height: 28),
onPressed: () => Navigator.pop(context),
),
Text(
@@ -195,10 +193,9 @@ class _QRScannerPageState extends State {
),
),
IconButton(
- icon: Icon(
- _isFlashOn ? Icons.flash_on : Icons.flash_off,
+ icon: SvgPicture.asset(
+ _isFlashOn ? Assets.icons.flashSlash.path : Assets.icons.flash.path,
color: AppColors.textPrimary,
- size: 28,
),
onPressed: _toggleFlash,
),
@@ -223,62 +220,62 @@ class _QRScannerPageState extends State {
),
),
- Positioned(
- top: -2,
- left: -2,
- child: Container(
- width: 30,
- height: 30,
- decoration: BoxDecoration(
- border: Border(
- top: BorderSide(color: AppColors.primary, width: 4),
- left: BorderSide(color: AppColors.primary, width: 4),
- ),
- ),
- ),
- ),
- Positioned(
- top: -2,
- right: -2,
- child: Container(
- width: 30,
- height: 30,
- decoration: BoxDecoration(
- border: Border(
- top: BorderSide(color: AppColors.primary, width: 4),
- right: BorderSide(color: AppColors.primary, width: 4),
- ),
- ),
- ),
- ),
- Positioned(
- bottom: -2,
- left: -2,
- child: Container(
- width: 30,
- height: 30,
- decoration: BoxDecoration(
- border: Border(
- bottom: BorderSide(color: AppColors.primary, width: 4),
- left: BorderSide(color: AppColors.primary, width: 4),
- ),
- ),
- ),
- ),
- Positioned(
- bottom: -2,
- right: -2,
- child: Container(
- width: 30,
- height: 30,
- decoration: BoxDecoration(
- border: Border(
- bottom: BorderSide(color: AppColors.primary, width: 4),
- right: BorderSide(color: AppColors.primary, width: 4),
- ),
- ),
- ),
- ),
+ // Positioned(
+ // top: -2,
+ // left: -2,
+ // child: Container(
+ // width: 30,
+ // height: 30,
+ // decoration: BoxDecoration(
+ // border: Border(
+ // top: BorderSide(color: AppColors.primary, width: 4),
+ // left: BorderSide(color: AppColors.primary, width: 4),
+ // ),
+ // ),
+ // ),
+ // ),
+ // Positioned(
+ // top: -2,
+ // right: -2,
+ // child: Container(
+ // width: 30,
+ // height: 30,
+ // decoration: BoxDecoration(
+ // border: Border(
+ // top: BorderSide(color: AppColors.primary, width: 4),
+ // right: BorderSide(color: AppColors.primary, width: 4),
+ // ),
+ // ),
+ // ),
+ // ),
+ // Positioned(
+ // bottom: -2,
+ // left: -2,
+ // child: Container(
+ // width: 30,
+ // height: 30,
+ // decoration: BoxDecoration(
+ // border: Border(
+ // bottom: BorderSide(color: AppColors.primary, width: 4),
+ // left: BorderSide(color: AppColors.primary, width: 4),
+ // ),
+ // ),
+ // ),
+ // ),
+ // Positioned(
+ // bottom: -2,
+ // right: -2,
+ // child: Container(
+ // width: 30,
+ // height: 30,
+ // decoration: BoxDecoration(
+ // border: Border(
+ // bottom: BorderSide(color: AppColors.primary, width: 4),
+ // right: BorderSide(color: AppColors.primary, width: 4),
+ // ),
+ // ),
+ // ),
+ // ),
],
),
),
@@ -317,10 +314,9 @@ class _QRScannerPageState extends State {
borderRadius: BorderRadius.circular(30),
),
child: IconButton(
- icon: Icon(
- Icons.photo_library,
+ icon: SvgPicture.asset(
+ Assets.icons.gallery.path,
color: AppColors.surface,
- size: 28,
),
onPressed: _pickImageFromGallery,
),
@@ -334,10 +330,9 @@ class _QRScannerPageState extends State {
borderRadius: BorderRadius.circular(30),
),
child: IconButton(
- icon: Icon(
- Icons.keyboard,
+ icon: SvgPicture.asset(
+ Assets.icons.keyboard.path,
color: AppColors.surface,
- size: 28,
),
onPressed: () {
_showManualInputDialog();
@@ -355,32 +350,227 @@ class _QRScannerPageState extends State {
void _showManualInputDialog() {
showDialog(
context: context,
+ barrierDismissible: true,
builder: (context) {
final TextEditingController controller = TextEditingController();
- return AlertDialog(
- title: const Text('Enter Code Manually'),
- content: TextField(
- controller: controller,
- decoration: const InputDecoration(
- hintText: 'Enter QR code or barcode',
- border: OutlineInputBorder(),
+ return Dialog(
+ backgroundColor: Colors.transparent,
+ child: Container(
+ padding: const EdgeInsets.all(24),
+ decoration: BoxDecoration(
+ color: AppColors.surface,
+ borderRadius: BorderRadius.circular(20),
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(0.15),
+ blurRadius: 20,
+ offset: const Offset(0, 10),
+ ),
+ ],
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Header with icon and title
+ Row(
+ children: [
+ Container(
+ padding: const EdgeInsets.all(12),
+ decoration: BoxDecoration(
+ color: AppColors.primary.withOpacity(0.1),
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: SvgPicture.asset(
+ Assets.icons.keyboard.path,
+ color: AppColors.primary,
+ width: 24,
+ height: 24,
+ ),
+ ),
+ const SizedBox(width: 16),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'Manual Entry',
+ style: TextStyle(
+ color: AppColors.textPrimary,
+ fontSize: 20,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ const SizedBox(height: 4),
+ Text(
+ 'Enter your code manually',
+ style: TextStyle(
+ color: AppColors.textSecondary,
+ fontSize: 14,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+
+ const SizedBox(height: 24),
+
+ // Input field with enhanced styling
+ Container(
+ decoration: BoxDecoration(
+ color: AppColors.cardBackground,
+ borderRadius: BorderRadius.circular(12),
+ border: Border.all(
+ color: AppColors.textSecondary.withOpacity(0.2),
+ width: 1,
+ ),
+ ),
+ child: TextField(
+ controller: controller,
+ style: TextStyle(
+ color: AppColors.textPrimary,
+ fontSize: 16,
+ ),
+ maxLines: 3,
+ minLines: 1,
+ decoration: InputDecoration(
+ hintText: 'Enter QR code, barcode, or any text...',
+ hintStyle: TextStyle(
+ color: AppColors.textSecondary.withOpacity(0.7),
+ fontSize: 14,
+ ),
+ border: InputBorder.none,
+ contentPadding: const EdgeInsets.all(16),
+ prefixIcon: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Icon(
+ Icons.qr_code_2,
+ color: AppColors.textSecondary.withOpacity(0.5),
+ size: 20,
+ ),
+ ),
+ ),
+ autofocus: true,
+ textInputAction: TextInputAction.done,
+ onSubmitted: (value) {
+ if (value.trim().isNotEmpty) {
+ Navigator.pop(context);
+ _handleScanResult(value.trim());
+ }
+ },
+ ),
+ ),
+
+ const SizedBox(height: 8),
+
+ // Helper text
+ Text(
+ 'Tip: You can paste codes from clipboard or type them manually',
+ style: TextStyle(
+ color: AppColors.textSecondary.withOpacity(0.8),
+ fontSize: 12,
+ fontStyle: FontStyle.italic,
+ ),
+ ),
+
+ const SizedBox(height: 24),
+
+ // Action buttons with enhanced styling
+ Row(
+ children: [
+ Expanded(
+ child: Container(
+ height: 48,
+ decoration: BoxDecoration(
+ border: Border.all(
+ color: AppColors.textSecondary.withOpacity(0.3),
+ width: 1,
+ ),
+ borderRadius: BorderRadius.circular(12),
+ ),
+ child: TextButton(
+ onPressed: () => Navigator.pop(context),
+ style: TextButton.styleFrom(
+ foregroundColor: AppColors.textSecondary,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(12),
+ ),
+ ),
+ child: const Text(
+ 'Cancel',
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(width: 12),
+ Expanded(
+ child: Container(
+ height: 48,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ colors: [
+ AppColors.primary,
+ AppColors.primary.withOpacity(0.8),
+ ],
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ ),
+ borderRadius: BorderRadius.circular(12),
+ boxShadow: [
+ BoxShadow(
+ color: AppColors.primary.withOpacity(0.3),
+ blurRadius: 8,
+ offset: const Offset(0, 4),
+ ),
+ ],
+ ),
+ child: ElevatedButton(
+ onPressed: () {
+ if (controller.text.trim().isNotEmpty) {
+ Navigator.pop(context);
+ _handleScanResult(controller.text.trim());
+ }
+ },
+ style: ElevatedButton.styleFrom(
+ backgroundColor: Colors.transparent,
+ shadowColor: Colors.transparent,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(12),
+ ),
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Text(
+ 'Submit',
+ style: TextStyle(
+ color: Colors.white,
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ const SizedBox(width: 8),
+ const Icon(
+ Icons.arrow_forward_ios,
+ color: Colors.white,
+ size: 16,
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
),
),
- actions: [
- TextButton(
- onPressed: () => Navigator.pop(context),
- child: const Text('Cancel'),
- ),
- ElevatedButton(
- onPressed: () {
- if (controller.text.isNotEmpty) {
- Navigator.pop(context);
- _handleScanResult(controller.text);
- }
- },
- child: const Text('Submit'),
- ),
- ],
);
},
);
diff --git a/lib/widgets/animated_splash_screen.dart b/lib/widgets/animated_splash_screen.dart
index 909867f..864b6a6 100644
--- a/lib/widgets/animated_splash_screen.dart
+++ b/lib/widgets/animated_splash_screen.dart
@@ -4,21 +4,21 @@ import 'package:lba/res/colors.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../gen/assets.gen.dart';
-class CoolSplashScreen extends StatefulWidget {
+class SplashScreen extends StatefulWidget {
final Widget nextScreen;
final Duration duration;
- const CoolSplashScreen({
+ const SplashScreen({
super.key,
required this.nextScreen,
this.duration = const Duration(seconds: 6),
});
@override
- State createState() => _CoolSplashScreenState();
+ State createState() => _SplashScreenState();
}
-class _CoolSplashScreenState extends State
+class _SplashScreenState extends State
with TickerProviderStateMixin {
late AnimationController _mainController;
late Animation _backgroundAnimation;
diff --git a/lib/widgets/language_selection_dialog.dart b/lib/widgets/language_selection_dialog.dart
index 6b02ffe..327252f 100644
--- a/lib/widgets/language_selection_dialog.dart
+++ b/lib/widgets/language_selection_dialog.dart
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
-import 'package:flutter_svg/svg.dart';
import 'package:lba/res/colors.dart';
class LanguageSelectionOverlay extends StatefulWidget {
@@ -27,19 +26,29 @@ class _LanguageSelectionOverlayState extends State
late String _selectedLanguage;
final List