Refactor contacts using flutter_contacts, unify InteractiveScale animations and 16px corner radius across welcome and setup complete screens, and compile final release APK
This commit is contained in:
parent
ac1294a150
commit
3f2a804a31
|
|
@ -11,6 +11,28 @@ rootProject.layout.buildDirectory.value(newBuildDir)
|
|||
subprojects {
|
||||
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
|
||||
project.layout.buildDirectory.value(newSubprojectBuildDir)
|
||||
|
||||
afterEvaluate {
|
||||
if (extensions.findByName("android") != null) {
|
||||
val android = extensions.findByName("android") as com.android.build.gradle.BaseExtension
|
||||
if (android.namespace == null) {
|
||||
var manifestPackage: String? = null
|
||||
val manifestFile = project.projectDir.resolve("src/main/AndroidManifest.xml")
|
||||
if (manifestFile.exists()) {
|
||||
val content = manifestFile.readText()
|
||||
val match = Regex("package=\"([^\"]+)\"").find(content)
|
||||
if (match != null) {
|
||||
manifestPackage = match.groupValues[1]
|
||||
}
|
||||
}
|
||||
if (manifestPackage != null) {
|
||||
android.namespace = manifestPackage
|
||||
} else {
|
||||
android.namespace = "co.gittouch." + project.name.replace("-", "_")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
subprojects {
|
||||
project.evaluationDependsOn(":app")
|
||||
|
|
|
|||
|
|
@ -186,3 +186,119 @@ class _TopAlertWidgetState extends State<_TopAlertWidget> with SingleTickerProvi
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SlideFadeEntrance extends StatefulWidget {
|
||||
final Widget child;
|
||||
final Duration delay;
|
||||
final Duration duration;
|
||||
|
||||
const SlideFadeEntrance({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.delay = Duration.zero,
|
||||
this.duration = const Duration(milliseconds: 500),
|
||||
});
|
||||
|
||||
@override
|
||||
State<SlideFadeEntrance> createState() => _SlideFadeEntranceState();
|
||||
}
|
||||
|
||||
class _SlideFadeEntranceState extends State<SlideFadeEntrance> with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
late Animation<double> _opacityAnimation;
|
||||
late Animation<double> _slideAnimation;
|
||||
Timer? _timer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: widget.duration,
|
||||
);
|
||||
|
||||
_opacityAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
|
||||
CurvedAnimation(parent: _controller, curve: Curves.easeIn),
|
||||
);
|
||||
|
||||
_slideAnimation = Tween<double>(begin: 12.0, end: 0.0).animate(
|
||||
CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic),
|
||||
);
|
||||
|
||||
if (widget.delay == Duration.zero) {
|
||||
_controller.forward();
|
||||
} else {
|
||||
_timer = Timer(widget.delay, () {
|
||||
if (mounted) {
|
||||
_controller.forward();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_timer?.cancel();
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: _controller,
|
||||
builder: (context, child) {
|
||||
return Opacity(
|
||||
opacity: _opacityAnimation.value,
|
||||
child: Transform.translate(
|
||||
offset: Offset(0.0, _slideAnimation.value),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class InteractiveScale extends StatefulWidget {
|
||||
final Widget child;
|
||||
final VoidCallback? onTap;
|
||||
final double hoverScale;
|
||||
final double tapScale;
|
||||
|
||||
const InteractiveScale({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.onTap,
|
||||
this.hoverScale = 1.03,
|
||||
this.tapScale = 0.96,
|
||||
});
|
||||
|
||||
@override
|
||||
State<InteractiveScale> createState() => _InteractiveScaleState();
|
||||
}
|
||||
|
||||
class _InteractiveScaleState extends State<InteractiveScale> {
|
||||
double _scale = 1.0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MouseRegion(
|
||||
onEnter: (_) => setState(() => _scale = widget.hoverScale),
|
||||
onExit: (_) => setState(() => _scale = 1.0),
|
||||
child: AnimatedScale(
|
||||
scale: _scale,
|
||||
duration: const Duration(milliseconds: 120),
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTapDown: (_) => setState(() => _scale = widget.tapScale),
|
||||
onTapUp: (_) => setState(() => _scale = widget.hoverScale),
|
||||
onTapCancel: () => setState(() => _scale = 1.0),
|
||||
onTap: widget.onTap,
|
||||
child: widget.child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,21 +248,23 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
|
|||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: _simulateBarcodeScan,
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size(0, 32),
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: InteractiveScale(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: _simulateBarcodeScan,
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size(0, 32),
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
icon: const Icon(Icons.qr_code_scanner, size: 16),
|
||||
label: const Text(
|
||||
'Scan Item',
|
||||
style: TextStyle(fontSize: 10, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
icon: const Icon(Icons.qr_code_scanner, size: 16),
|
||||
label: const Text(
|
||||
'Scan Item',
|
||||
style: TextStyle(fontSize: 10, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -407,95 +409,103 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
|
|||
final isLowStock = widget.inventoryAlerts && remainingStock <= 3;
|
||||
final isOutOfStock = widget.inventoryAlerts && remainingStock <= 0;
|
||||
|
||||
return InkWell(
|
||||
onTap: isOutOfStock ? null : () => _handleAddItem(prod),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: countInCart > 0
|
||||
? const Color(0xFF1E1B4B) // Deep indigo accent
|
||||
: const Color(0xFF1E293B).withOpacity(0.8),
|
||||
return SlideFadeEntrance(
|
||||
delay: Duration(milliseconds: index * 40),
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: InteractiveScale(
|
||||
tapScale: isOutOfStock ? 1.0 : 0.95,
|
||||
hoverScale: isOutOfStock ? 1.0 : 1.03,
|
||||
child: InkWell(
|
||||
onTap: isOutOfStock ? null : () => _handleAddItem(prod),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: countInCart > 0 ? theme.colorScheme.primary : const Color(0xFF334155),
|
||||
width: countInCart > 0 ? 1.5 : 1.0,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
prod.name,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'₹${prod.price.toStringAsFixed(0)}',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: countInCart > 0
|
||||
? const Color(0xFF1E1B4B) // Deep indigo accent
|
||||
: const Color(0xFF1E293B).withOpacity(0.8),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: countInCart > 0 ? theme.colorScheme.primary : const Color(0xFF334155),
|
||||
width: countInCart > 0 ? 1.5 : 1.0,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
if (widget.inventoryAlerts)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: isLowStock
|
||||
? Colors.red.withOpacity(0.15)
|
||||
: const Color(0xFF334155),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Text(
|
||||
isOutOfStock ? 'Out of stock' : '$remainingStock Stock',
|
||||
style: TextStyle(
|
||||
color: isLowStock ? Colors.redAccent : Colors.grey,
|
||||
fontSize: 8,
|
||||
fontWeight: FontWeight.bold,
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
prod.name,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
const SizedBox(),
|
||||
if (countInCart > 0)
|
||||
Container(
|
||||
width: 20,
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.primary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'$countInCart',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 10,
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'₹${prod.price.toStringAsFixed(0)}',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
if (widget.inventoryAlerts)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: isLowStock
|
||||
? Colors.red.withOpacity(0.15)
|
||||
: const Color(0xFF334155),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Text(
|
||||
isOutOfStock ? 'Out of stock' : '$remainingStock Stock',
|
||||
style: TextStyle(
|
||||
color: isLowStock ? Colors.redAccent : Colors.grey,
|
||||
fontSize: 8,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
const SizedBox(),
|
||||
if (countInCart > 0)
|
||||
Container(
|
||||
width: 20,
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.primary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'$countInCart',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -655,20 +665,27 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
|
|||
const Divider(color: Color(0xFF334155), height: 16),
|
||||
_buildComputationCalculations(theme),
|
||||
const SizedBox(height: 10),
|
||||
ElevatedButton(
|
||||
onPressed: _billingList.isEmpty ? null : _handleReviewAndGenerate,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
disabledBackgroundColor: const Color(0xFF1E293B),
|
||||
minimumSize: const Size.fromHeight(48),
|
||||
),
|
||||
child: const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.receipt, size: 16),
|
||||
SizedBox(width: 8),
|
||||
Text('Generate Invoice'),
|
||||
],
|
||||
InteractiveScale(
|
||||
hoverScale: _billingList.isNotEmpty ? 1.03 : 1.0,
|
||||
tapScale: _billingList.isNotEmpty ? 0.96 : 1.0,
|
||||
child: ElevatedButton(
|
||||
onPressed: _billingList.isEmpty ? null : _handleReviewAndGenerate,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
disabledBackgroundColor: const Color(0xFF1E293B),
|
||||
minimumSize: const Size.fromHeight(48),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
child: const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.receipt, size: 16),
|
||||
SizedBox(width: 8),
|
||||
Text('Generate Invoice'),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
]
|
||||
|
|
@ -751,19 +768,26 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
|
|||
)
|
||||
],
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: _billingList.isEmpty ? null : _handleReviewAndGenerate,
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
minimumSize: const Size(180, 44),
|
||||
),
|
||||
child: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.print, size: 16),
|
||||
SizedBox(width: 8),
|
||||
Text('Review & Invoice', style: TextStyle(fontSize: 12)),
|
||||
],
|
||||
InteractiveScale(
|
||||
hoverScale: _billingList.isNotEmpty ? 1.03 : 1.0,
|
||||
tapScale: _billingList.isNotEmpty ? 0.96 : 1.0,
|
||||
child: ElevatedButton(
|
||||
onPressed: _billingList.isEmpty ? null : _handleReviewAndGenerate,
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
minimumSize: const Size(180, 44),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
child: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.print, size: 16),
|
||||
SizedBox(width: 8),
|
||||
Text('Review & Invoice', style: TextStyle(fontSize: 12)),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
|
|
@ -937,30 +961,40 @@ class _BillingDashboardScreenState extends State<BillingDashboardScreen> {
|
|||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
showTopAlert(
|
||||
context,
|
||||
'Thermal Printed: $_invoiceNumber successfully!',
|
||||
icon: Icons.print,
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.print, size: 16),
|
||||
label: const Text('Simulate Print Receipt', style: TextStyle(fontSize: 12)),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.black,
|
||||
foregroundColor: Colors.white,
|
||||
minimumSize: const Size.fromHeight(42),
|
||||
InteractiveScale(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
showTopAlert(
|
||||
context,
|
||||
'Thermal Printed: $_invoiceNumber successfully!',
|
||||
icon: Icons.print,
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.print, size: 16),
|
||||
label: const Text('Simulate Print Receipt', style: TextStyle(fontSize: 12)),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.black,
|
||||
foregroundColor: Colors.white,
|
||||
minimumSize: const Size.fromHeight(42),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ElevatedButton(
|
||||
onPressed: _closeReceiptAndReset,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
minimumSize: const Size.fromHeight(42),
|
||||
InteractiveScale(
|
||||
child: ElevatedButton(
|
||||
onPressed: _closeReceiptAndReset,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
minimumSize: const Size.fromHeight(42),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
child: const Text('New Ledger Sale', style: TextStyle(fontSize: 12)),
|
||||
),
|
||||
child: const Text('New Ledger Sale', style: TextStyle(fontSize: 12)),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -146,46 +146,48 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
|
|||
Widget _buildDocChip(String label, IconData icon) {
|
||||
final theme = Theme.of(context);
|
||||
final isSelected = _selectedDocs.contains(label);
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
if (isSelected) {
|
||||
_selectedDocs.remove(label);
|
||||
} else {
|
||||
_selectedDocs.add(label);
|
||||
}
|
||||
});
|
||||
},
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(minHeight: 48),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? const Color(0xFFFAF0F0) : Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: isSelected ? const Color(0xFFAF101A) : theme.colorScheme.outlineVariant,
|
||||
width: isSelected ? 1.5 : 1,
|
||||
return InteractiveScale(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
if (isSelected) {
|
||||
_selectedDocs.remove(label);
|
||||
} else {
|
||||
_selectedDocs.add(label);
|
||||
}
|
||||
});
|
||||
},
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(minHeight: 48),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? const Color(0xFFFAF0F0) : Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: isSelected ? const Color(0xFFAF101A) : theme.colorScheme.outlineVariant,
|
||||
width: isSelected ? 1.5 : 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
size: 16,
|
||||
color: isSelected ? const Color(0xFFAF101A) : Colors.grey[600],
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isSelected ? const Color(0xFFAF101A) : Colors.grey[800],
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
size: 16,
|
||||
color: isSelected ? const Color(0xFFAF101A) : Colors.grey[600],
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isSelected ? const Color(0xFFAF101A) : Colors.grey[800],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -337,31 +339,35 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
|
|||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _handleImagePick(docName, ImageSource.camera),
|
||||
icon: const Icon(Icons.camera_alt, size: 14),
|
||||
label: const Text('Take Photo'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size(0, 36),
|
||||
backgroundColor: const Color(0xFFAF101A),
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 1,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8)),
|
||||
InteractiveScale(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () => _handleImagePick(docName, ImageSource.camera),
|
||||
icon: const Icon(Icons.camera_alt, size: 14),
|
||||
label: const Text('Take Photo'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size(0, 36),
|
||||
backgroundColor: const Color(0xFFAF101A),
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 1,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16)),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () => _handleImagePick(docName, ImageSource.gallery),
|
||||
icon: const Icon(Icons.photo_library, size: 14),
|
||||
label: const Text('From Gallery'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: const Color(0xFFAF101A),
|
||||
side: const BorderSide(color: Color(0xFFAF101A)),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8)),
|
||||
InteractiveScale(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () => _handleImagePick(docName, ImageSource.gallery),
|
||||
icon: const Icon(Icons.photo_library, size: 14),
|
||||
label: const Text('From Gallery'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: const Color(0xFFAF101A),
|
||||
side: const BorderSide(color: Color(0xFFAF101A)),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
@ -441,337 +447,407 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Complete verification',
|
||||
style: theme.textTheme.headlineLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
'Verify your business details to start selling.',
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Text(
|
||||
'Business/Shop Name *',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextField(
|
||||
controller: _nameController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'e.g. Sharma General Store',
|
||||
),
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Text(
|
||||
'Business Type *',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 16,
|
||||
mainAxisSpacing: 16,
|
||||
childAspectRatio: 2.2,
|
||||
),
|
||||
itemCount: _categories.length,
|
||||
itemBuilder: (context, index) {
|
||||
final cat = _categories[index];
|
||||
final isSelected = _selectedCategory == cat['id'];
|
||||
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedCategory = cat['id'];
|
||||
});
|
||||
},
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? const Color(0xFFAF101A).withOpacity(0.08)
|
||||
: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: isSelected ? const Color(0xFFAF101A) : const Color(0xFFE2E2E2),
|
||||
width: isSelected ? 2 : 1,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.01),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
)
|
||||
],
|
||||
SlideFadeEntrance(
|
||||
delay: Duration.zero,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Complete verification',
|
||||
style: theme.textTheme.headlineLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Center(
|
||||
child: Icon(
|
||||
cat['icon'],
|
||||
color: isSelected ? const Color(0xFFAF101A) : Colors.grey[600],
|
||||
size: 26,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
'Verify your business details to start selling.',
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 100),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Business/Shop Name *',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextField(
|
||||
controller: _nameController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'e.g. Sharma General Store',
|
||||
),
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 180),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Business Type *',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 16,
|
||||
mainAxisSpacing: 16,
|
||||
childAspectRatio: 2.2,
|
||||
),
|
||||
itemCount: _categories.length,
|
||||
itemBuilder: (context, index) {
|
||||
final cat = _categories[index];
|
||||
final isSelected = _selectedCategory == cat['id'];
|
||||
|
||||
return InteractiveScale(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedCategory = cat['id'];
|
||||
});
|
||||
},
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? const Color(0xFFAF101A).withOpacity(0.08)
|
||||
: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: isSelected ? const Color(0xFFAF101A) : const Color(0xFFE2E2E2),
|
||||
width: isSelected ? 2 : 1,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.01),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Center(
|
||||
child: Icon(
|
||||
cat['icon'],
|
||||
color: isSelected ? const Color(0xFFAF101A) : Colors.grey[600],
|
||||
size: 26,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Center(
|
||||
child: Text(
|
||||
cat['name'],
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isSelected ? const Color(0xFFAF101A) : Colors.grey[800],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Center(
|
||||
child: Text(
|
||||
cat['name'],
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isSelected ? const Color(0xFFAF101A) : Colors.grey[800],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'GSTIN',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 260),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'GSTIN',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'(Optional)',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: Colors.grey[500],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'(Optional)',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: Colors.grey[500],
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _gstinController,
|
||||
textCapitalization: TextCapitalization.characters,
|
||||
decoration: const InputDecoration(
|
||||
hintText: '15-DIGIT GST NUMBER',
|
||||
),
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, letterSpacing: 1.2),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
InteractiveScale(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
showTopAlert(
|
||||
context,
|
||||
'GSTIN Verified Successfully!',
|
||||
icon: Icons.verified_outlined,
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size(0, 48),
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
child: const Text('Verify'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
"Auto-fetches address & legal name on verification.",
|
||||
style: TextStyle(fontSize: 11, color: Colors.grey[500]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _gstinController,
|
||||
textCapitalization: TextCapitalization.characters,
|
||||
decoration: const InputDecoration(
|
||||
hintText: '15-DIGIT GST NUMBER',
|
||||
const SizedBox(height: 16),
|
||||
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 340),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Shop Location *',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, letterSpacing: 1.2),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
showTopAlert(
|
||||
context,
|
||||
'GSTIN Verified Successfully!',
|
||||
icon: Icons.verified_outlined,
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size(0, 48),
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
|
||||
shape: RoundedRectangleBorder(
|
||||
const SizedBox(height: 8),
|
||||
InteractiveScale(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
showTopAlert(
|
||||
context,
|
||||
'Location detected: Sector 62, Noida, UP',
|
||||
icon: Icons.my_location_outlined,
|
||||
);
|
||||
},
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
child: const Text('Verify'),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
"Auto-fetches address & legal name on verification.",
|
||||
style: TextStyle(fontSize: 11, color: Colors.grey[500]),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Text(
|
||||
'Shop Location *',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
showTopAlert(
|
||||
context,
|
||||
'Location detected: Sector 62, Noida, UP',
|
||||
icon: Icons.my_location_outlined,
|
||||
);
|
||||
},
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: theme.colorScheme.outlineVariant,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.primary.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.my_location,
|
||||
color: theme.colorScheme.primary,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'Detect current location',
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 15,
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: theme.colorScheme.outlineVariant,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'Pin shop address automatically',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.primary.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.my_location,
|
||||
color: theme.colorScheme.primary,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'Detect current location',
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'Pin shop address automatically',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.chevron_right,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.chevron_right,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Text(
|
||||
'Identity / License Documents (Select all that apply)',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
_buildDocChip('GSTIN', Icons.description),
|
||||
_buildDocChip('FSSAI', Icons.restaurant),
|
||||
_buildDocChip('Aadhar', Icons.badge_outlined),
|
||||
_buildDocChip('Udhyog Aadhar', Icons.domain),
|
||||
_buildDocChip('PAN', Icons.credit_card),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
..._selectedDocs.map((doc) {
|
||||
IconData docIcon = Icons.description;
|
||||
if (doc == 'GSTIN') docIcon = Icons.description;
|
||||
if (doc == 'FSSAI') docIcon = Icons.restaurant;
|
||||
if (doc == 'Aadhar') docIcon = Icons.badge_outlined;
|
||||
if (doc == 'Udhyog Aadhar') docIcon = Icons.domain;
|
||||
if (doc == 'PAN') docIcon = Icons.credit_card;
|
||||
return _buildUploadSection(doc, docIcon);
|
||||
}),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Text(
|
||||
'Shop Facade Photo *',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
_showFacadePhotoOptions();
|
||||
},
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(vertical: 24),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: _uploadedDocs.containsKey('facade_photo') ? Colors.green : theme.colorScheme.outlineVariant,
|
||||
width: _uploadedDocs.containsKey('facade_photo') ? 2.0 : 1.0,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: _uploadedDocs.containsKey('facade_photo') ? Colors.green.withOpacity(0.1) : theme.colorScheme.primary.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
_uploadedDocs.containsKey('facade_photo') ? Icons.check_circle : Icons.add_a_photo_outlined,
|
||||
color: _uploadedDocs.containsKey('facade_photo') ? Colors.green : theme.colorScheme.primary,
|
||||
size: 24,
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 420),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Identity / License Documents (Select all that apply)',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
_buildDocChip('GSTIN', Icons.description),
|
||||
_buildDocChip('FSSAI', Icons.restaurant),
|
||||
_buildDocChip('Aadhar', Icons.badge_outlined),
|
||||
_buildDocChip('Udhyog Aadhar', Icons.domain),
|
||||
_buildDocChip('PAN', Icons.credit_card),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 500),
|
||||
child: Column(
|
||||
children: _selectedDocs.map((doc) {
|
||||
IconData docIcon = Icons.description;
|
||||
if (doc == 'GSTIN') docIcon = Icons.description;
|
||||
if (doc == 'FSSAI') docIcon = Icons.restaurant;
|
||||
if (doc == 'Aadhar') docIcon = Icons.badge_outlined;
|
||||
if (doc == 'Udhyog Aadhar') docIcon = Icons.domain;
|
||||
if (doc == 'PAN') docIcon = Icons.credit_card;
|
||||
return _buildUploadSection(doc, docIcon);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 580),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Shop Facade Photo *',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
InteractiveScale(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
_showFacadePhotoOptions();
|
||||
},
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(vertical: 24),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: _uploadedDocs.containsKey('facade_photo') ? Colors.green : theme.colorScheme.outlineVariant,
|
||||
width: _uploadedDocs.containsKey('facade_photo') ? 2.0 : 1.0,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: _uploadedDocs.containsKey('facade_photo') ? Colors.green.withOpacity(0.1) : theme.colorScheme.primary.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
_uploadedDocs.containsKey('facade_photo') ? Icons.check_circle : Icons.add_a_photo_outlined,
|
||||
color: _uploadedDocs.containsKey('facade_photo') ? Colors.green : theme.colorScheme.primary,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
_uploadedDocs.containsKey('facade_photo') ? _uploadedDocs['facade_photo']! : 'Upload front view',
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
_uploadedDocs.containsKey('facade_photo') ? 'Facade Photo Attached • Tap to change' : 'Clear photo showing shop board',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Colors.grey[500],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
_uploadedDocs.containsKey('facade_photo') ? _uploadedDocs['facade_photo']! : 'Upload front view',
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
_uploadedDocs.containsKey('facade_photo') ? 'Facade Photo Attached • Tap to change' : 'Clear photo showing shop board',
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Colors.grey[500],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
|
@ -781,30 +857,40 @@ class _BusinessSetupScreenState extends State<BusinessSetupScreen> {
|
|||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
|
||||
child: ElevatedButton(
|
||||
onPressed: _isNameValid
|
||||
? () {
|
||||
Navigator.push(
|
||||
context,
|
||||
FadeSlidePageRoute(
|
||||
child: CustomizeSetupScreen(
|
||||
businessName: _nameController.text.trim(),
|
||||
businessType: _selectedCategory,
|
||||
gstin: _gstinController.text.trim(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(54),
|
||||
backgroundColor: _isNameValid ? theme.colorScheme.primary : Colors.grey[200],
|
||||
foregroundColor: _isNameValid ? Colors.white : Colors.grey[400],
|
||||
elevation: _isNameValid ? 2 : 0,
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 660),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
|
||||
child: InteractiveScale(
|
||||
hoverScale: _isNameValid ? 1.03 : 1.0,
|
||||
tapScale: _isNameValid ? 0.96 : 1.0,
|
||||
child: ElevatedButton(
|
||||
onPressed: _isNameValid
|
||||
? () {
|
||||
Navigator.push(
|
||||
context,
|
||||
FadeSlidePageRoute(
|
||||
child: CustomizeSetupScreen(
|
||||
businessName: _nameController.text.trim(),
|
||||
businessType: _selectedCategory,
|
||||
gstin: _gstinController.text.trim(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(54),
|
||||
backgroundColor: _isNameValid ? theme.colorScheme.primary : Colors.grey[200],
|
||||
foregroundColor: _isNameValid ? Colors.white : Colors.grey[400],
|
||||
elevation: _isNameValid ? 2 : 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
child: const Text('Continue to Verification'),
|
||||
),
|
||||
),
|
||||
child: const Text('Continue to Verification'),
|
||||
),
|
||||
)
|
||||
],
|
||||
|
|
|
|||
|
|
@ -119,49 +119,66 @@ class _CustomizeSetupScreenState extends State<CustomizeSetupScreen> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Customize Your Setup',
|
||||
style: theme.textTheme.headlineLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
SlideFadeEntrance(
|
||||
delay: Duration.zero,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Customize Your Setup',
|
||||
style: theme.textTheme.headlineLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Turn on the features you need for your shop. You can always change these later in settings.',
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Turn on the features you need for your shop. You can always change these later in settings.',
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Toggle 1: Barcode
|
||||
_buildToggleCard(
|
||||
Icons.qr_code_scanner,
|
||||
'Barcode Scanner',
|
||||
'Do you use a Barcode Scanner?',
|
||||
_barcode,
|
||||
(val) => setState(() => _barcode = val),
|
||||
theme,
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 100),
|
||||
child: _buildToggleCard(
|
||||
Icons.qr_code_scanner,
|
||||
'Barcode Scanner',
|
||||
'Do you use a Barcode Scanner?',
|
||||
_barcode,
|
||||
(val) => setState(() => _barcode = val),
|
||||
theme,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Toggle 2: KOT
|
||||
_buildToggleCard(
|
||||
Icons.receipt_long,
|
||||
'KOT Management',
|
||||
'Do you need Kitchen Order Ticket management?',
|
||||
_kot,
|
||||
(val) => setState(() => _kot = val),
|
||||
theme,
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 180),
|
||||
child: _buildToggleCard(
|
||||
Icons.receipt_long,
|
||||
'KOT Management',
|
||||
'Do you need Kitchen Order Ticket management?',
|
||||
_kot,
|
||||
(val) => setState(() => _kot = val),
|
||||
theme,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Toggle 3: Inventory stock tracking
|
||||
_buildToggleCard(
|
||||
Icons.inventory_2,
|
||||
'Inventory Alerts',
|
||||
'Get notified when stock is running low.',
|
||||
_inventory,
|
||||
(val) => setState(() => _inventory = val),
|
||||
theme,
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 260),
|
||||
child: _buildToggleCard(
|
||||
Icons.inventory_2,
|
||||
'Inventory Alerts',
|
||||
'Get notified when stock is running low.',
|
||||
_inventory,
|
||||
(val) => setState(() => _inventory = val),
|
||||
theme,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -170,28 +187,36 @@ class _CustomizeSetupScreenState extends State<CustomizeSetupScreen> {
|
|||
),
|
||||
|
||||
// Pinned action keys
|
||||
Padding(
|
||||
padding: EdgeInsets.all(padding),
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
FadeSlidePageRoute(
|
||||
child: SetupCompleteScreen(
|
||||
businessName: widget.businessName,
|
||||
businessType: widget.businessType,
|
||||
gstin: widget.gstin,
|
||||
barcodeScanner: _barcode,
|
||||
kotManagement: _kot,
|
||||
inventoryAlerts: _inventory,
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 340),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(padding),
|
||||
child: InteractiveScale(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
FadeSlidePageRoute(
|
||||
child: SetupCompleteScreen(
|
||||
businessName: widget.businessName,
|
||||
businessType: widget.businessType,
|
||||
gstin: widget.gstin,
|
||||
barcodeScanner: _barcode,
|
||||
kotManagement: _kot,
|
||||
inventoryAlerts: _inventory,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(54),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(54),
|
||||
child: const Text('Save & Continue'),
|
||||
),
|
||||
),
|
||||
child: const Text('Save & Continue'),
|
||||
),
|
||||
)
|
||||
],
|
||||
|
|
@ -210,64 +235,67 @@ class _CustomizeSetupScreenState extends State<CustomizeSetupScreen> {
|
|||
ValueChanged<bool> onChanged,
|
||||
ThemeData theme,
|
||||
) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.grey[200]!),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.02),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 44,
|
||||
height: 44,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
shape: BoxShape.circle,
|
||||
return InteractiveScale(
|
||||
onTap: () => onChanged(!value),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.grey[200]!),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.02),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 44,
|
||||
height: 44,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(icon, color: Colors.grey[600], size: 22),
|
||||
),
|
||||
child: Icon(icon, color: Colors.grey[600], size: 22),
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
const SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
subtitle,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Colors.grey[500],
|
||||
),
|
||||
)
|
||||
],
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
subtitle,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: Colors.grey[500],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Switch(
|
||||
value: value,
|
||||
onChanged: onChanged,
|
||||
activeColor: Colors.white,
|
||||
activeTrackColor: theme.colorScheme.primary,
|
||||
),
|
||||
],
|
||||
Switch(
|
||||
value: value,
|
||||
onChanged: onChanged,
|
||||
activeColor: Colors.white,
|
||||
activeTrackColor: theme.colorScheme.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:contacts_service/contacts_service.dart';
|
||||
import 'package:flutter_contacts/flutter_contacts.dart';
|
||||
import '../theme.dart';
|
||||
import '../route_transitions.dart';
|
||||
import 'otp_verification_screen.dart';
|
||||
|
|
@ -16,8 +16,8 @@ class MockContact {
|
|||
}
|
||||
|
||||
class MockPhone {
|
||||
final String? value;
|
||||
MockPhone(this.value);
|
||||
final String number;
|
||||
MockPhone(this.number);
|
||||
}
|
||||
|
||||
class MobileLoginScreen extends StatefulWidget {
|
||||
|
|
@ -103,7 +103,7 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
|
|||
];
|
||||
} else {
|
||||
try {
|
||||
final List<Contact> list = await ContactsService.getContacts(withThumbnails: false);
|
||||
final List<Contact> list = await FlutterContacts.getContacts(withProperties: true, withPhoto: false);
|
||||
return list;
|
||||
} catch (e) {
|
||||
return [];
|
||||
|
|
@ -166,7 +166,7 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
|
|||
setDialogState(() {
|
||||
filteredContacts = allContacts.where((contact) {
|
||||
final name = (contact.displayName ?? '').toLowerCase();
|
||||
final phone = (contact.phones.isNotEmpty ? contact.phones.first.value ?? '' : '').toLowerCase();
|
||||
final phone = (contact.phones.isNotEmpty ? contact.phones.first.number : '').toLowerCase();
|
||||
return name.contains(query.toLowerCase()) || phone.contains(query.toLowerCase());
|
||||
}).toList();
|
||||
});
|
||||
|
|
@ -183,7 +183,7 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
|
|||
itemBuilder: (context, index) {
|
||||
final contact = filteredContacts[index];
|
||||
final name = contact.displayName ?? 'Unknown';
|
||||
final phone = contact.phones.isNotEmpty ? contact.phones.first.value : 'No number';
|
||||
final phone = contact.phones.isNotEmpty ? contact.phones.first.number : 'No number';
|
||||
return ListTile(
|
||||
title: Text(name),
|
||||
subtitle: Text(phone),
|
||||
|
|
@ -256,318 +256,335 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Step 2 of 5',
|
||||
style: theme.textTheme.labelMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'Mobile Login',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Plus Jakarta Sans',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFFAF101A),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: List.generate(5, (index) {
|
||||
return Expanded(
|
||||
child: Container(
|
||||
height: 6,
|
||||
margin: EdgeInsets.only(
|
||||
left: index == 0 ? 0 : 3.0,
|
||||
right: index == 4 ? 0 : 3.0,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: index <= 1 ? const Color(0xFFAF101A) : const Color(0xFFE2E2E2),
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Text(
|
||||
'Welcome Back',
|
||||
style: theme.textTheme.headlineLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Enter your mobile number to sign in or create a new account.',
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFAFFD1).withOpacity(0.8),
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
border: Border.all(color: const Color(0xFF016619).withOpacity(0.3)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.lock, size: 14, color: Color(0xFF016619)),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'Secure OTP Verification',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: const Color(0xFF016619),
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF3F3F3),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: const Color(0xFFE4BEBA).withOpacity(0.5)),
|
||||
),
|
||||
SlideFadeEntrance(
|
||||
delay: Duration.zero,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF005FAF),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.verified_user,
|
||||
color: Colors.white,
|
||||
size: 24,
|
||||
Text(
|
||||
'Step 2 of 5',
|
||||
style: theme.textTheme.labelMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Verify instantly with',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'Truecaller',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Plus Jakarta Sans',
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
const Text(
|
||||
'Mobile Login',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Plus Jakarta Sans',
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFFAF101A),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const Text(
|
||||
'+91 98765 43210',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Plus Jakarta Sans',
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
_controller.text = '9876543210';
|
||||
setState(() {
|
||||
_isValid = true;
|
||||
});
|
||||
showTopAlert(
|
||||
context,
|
||||
'Phone verified automatically with Truecaller! Joining PozoApp...',
|
||||
icon: Icons.verified_user_outlined,
|
||||
);
|
||||
Navigator.push(
|
||||
context,
|
||||
FadeSlidePageRoute(
|
||||
child: const OtpVerificationScreen(
|
||||
mobileNumber: '9876543210',
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: List.generate(5, (index) {
|
||||
return Expanded(
|
||||
child: Container(
|
||||
height: 6,
|
||||
margin: EdgeInsets.only(
|
||||
left: index == 0 ? 0 : 3.0,
|
||||
right: index == 4 ? 0 : 3.0,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: index <= 1 ? const Color(0xFFAF101A) : const Color(0xFFE2E2E2),
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.flash_on, size: 18, color: Colors.white),
|
||||
label: const Text(
|
||||
'1-Tap Verification',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 16,
|
||||
}),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Welcome Back',
|
||||
style: theme.textTheme.headlineLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Enter your mobile number to sign in or create a new account.',
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 100),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFAFFD1).withOpacity(0.8),
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
border: Border.all(color: const Color(0xFF016619).withOpacity(0.3)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.lock, size: 14, color: Color(0xFF016619)),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'Secure OTP Verification',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: const Color(0xFF016619),
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF005FAF),
|
||||
foregroundColor: Colors.white,
|
||||
minimumSize: const Size.fromHeight(56),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
elevation: 1,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'No OTP required. Your phone will be verified automatically.',
|
||||
textAlign: TextAlign.center,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: Colors.grey[600],
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: gap),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
const Expanded(child: Divider(color: Color(0xFFE4BEBA))),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
'Or enter mobile number manually',
|
||||
style: theme.textTheme.labelMedium?.copyWith(
|
||||
color: Colors.grey[600],
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Expanded(child: Divider(color: Color(0xFFE4BEBA))),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 200),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF3F3F3),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: const Color(0xFFE4BEBA).withOpacity(0.5)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF005FAF),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.verified_user,
|
||||
color: Colors.white,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Verify instantly with',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'Truecaller',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Plus Jakarta Sans',
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const Text(
|
||||
'+91 98765 43210',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Plus Jakarta Sans',
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
_controller.text = '9876543210';
|
||||
setState(() {
|
||||
_isValid = true;
|
||||
});
|
||||
showTopAlert(
|
||||
context,
|
||||
'Phone verified automatically with Truecaller! Joining PozoApp...',
|
||||
icon: Icons.verified_user_outlined,
|
||||
);
|
||||
Navigator.push(
|
||||
context,
|
||||
FadeSlidePageRoute(
|
||||
child: const OtpVerificationScreen(
|
||||
mobileNumber: '9876543210',
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.flash_on, size: 18, color: Colors.white),
|
||||
label: const Text(
|
||||
'1-Tap Verification',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF005FAF),
|
||||
foregroundColor: Colors.white,
|
||||
minimumSize: const Size.fromHeight(56),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
elevation: 1,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'No OTP required. Your phone will be verified automatically.',
|
||||
textAlign: TextAlign.center,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: Colors.grey[600],
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: gap),
|
||||
|
||||
Text(
|
||||
'Mobile Number',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
Container(
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: theme.colorScheme.outlineVariant),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.02),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 300),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
right: BorderSide(color: theme.colorScheme.outlineVariant),
|
||||
Row(
|
||||
children: [
|
||||
const Expanded(child: Divider(color: Color(0xFFE4BEBA))),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
'Or enter mobile number manually',
|
||||
style: theme.textTheme.labelMedium?.copyWith(
|
||||
color: Colors.grey[600],
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Expanded(child: Divider(color: Color(0xFFE4BEBA))),
|
||||
],
|
||||
),
|
||||
SizedBox(height: gap),
|
||||
Text(
|
||||
'Mobile Number',
|
||||
style: theme.textTheme.labelSmall?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: theme.colorScheme.outlineVariant),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.02),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 22,
|
||||
height: 14,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey[300]!, width: 0.5),
|
||||
border: Border(
|
||||
right: BorderSide(color: theme.colorScheme.outlineVariant),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
child: Row(
|
||||
children: [
|
||||
Container(height: 4, color: const Color(0xFFFF9933)),
|
||||
Container(
|
||||
height: 4,
|
||||
color: Colors.white,
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 2,
|
||||
height: 2,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF000080),
|
||||
shape: BoxShape.circle,
|
||||
width: 22,
|
||||
height: 14,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey[300]!, width: 0.5),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(height: 4, color: const Color(0xFFFF9933)),
|
||||
Container(
|
||||
height: 4,
|
||||
color: Colors.white,
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 2,
|
||||
height: 2,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF000080),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(height: 4, color: const Color(0xFF128807)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(height: 4, color: const Color(0xFF128807)),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'+91',
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'+91',
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: theme.colorScheme.onSurface,
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
keyboardType: TextInputType.phone,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
LengthLimitingTextInputFormatter(10),
|
||||
],
|
||||
decoration: const InputDecoration(
|
||||
hintText: '00000 00000',
|
||||
border: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
filled: false,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 16),
|
||||
),
|
||||
style: theme.textTheme.headlineMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.contact_phone, color: Color(0xFFAF101A)),
|
||||
onPressed: _pickContact,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
keyboardType: TextInputType.phone,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
LengthLimitingTextInputFormatter(10),
|
||||
],
|
||||
decoration: const InputDecoration(
|
||||
hintText: '00000 00000',
|
||||
border: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
filled: false,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 16),
|
||||
),
|
||||
style: theme.textTheme.headlineMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.contact_phone, color: Color(0xFFAF101A)),
|
||||
onPressed: _pickContact,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -577,66 +594,75 @@ class _MobileLoginScreenState extends State<MobileLoginScreen> {
|
|||
),
|
||||
),
|
||||
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: _isValid
|
||||
? () {
|
||||
Navigator.push(
|
||||
context,
|
||||
FadeSlidePageRoute(
|
||||
child: OtpVerificationScreen(
|
||||
mobileNumber: _controller.text,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(56),
|
||||
backgroundColor: _isValid ? const Color(0xFFAF101A) : Colors.grey[200],
|
||||
foregroundColor: _isValid ? Colors.white : Colors.grey[400],
|
||||
elevation: _isValid ? 2 : 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: const Text('Send OTP'),
|
||||
),
|
||||
SizedBox(height: gap / 2),
|
||||
OutlinedButton(
|
||||
onPressed: () {
|
||||
showTopAlert(
|
||||
context,
|
||||
'Connecting with Pozo Support... Please wait.',
|
||||
icon: Icons.support_agent_outlined,
|
||||
);
|
||||
},
|
||||
style: OutlinedButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(56),
|
||||
side: const BorderSide(color: Color(0xFFAF101A), width: 2),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.forum, color: Color(0xFFAF101A), size: 18),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'Help / Talk to Support',
|
||||
style: TextStyle(
|
||||
color: Color(0xFFAF101A),
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 400),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InteractiveScale(
|
||||
hoverScale: _isValid ? 1.03 : 1.0,
|
||||
tapScale: _isValid ? 0.96 : 1.0,
|
||||
child: ElevatedButton(
|
||||
onPressed: _isValid
|
||||
? () {
|
||||
Navigator.push(
|
||||
context,
|
||||
FadeSlidePageRoute(
|
||||
child: OtpVerificationScreen(
|
||||
mobileNumber: _controller.text,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
: null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(56),
|
||||
backgroundColor: _isValid ? const Color(0xFFAF101A) : Colors.grey[200],
|
||||
foregroundColor: _isValid ? Colors.white : Colors.grey[400],
|
||||
elevation: _isValid ? 2 : 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: const Text('Send OTP'),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
SizedBox(height: gap / 2),
|
||||
InteractiveScale(
|
||||
child: OutlinedButton(
|
||||
onPressed: () {
|
||||
showTopAlert(
|
||||
context,
|
||||
'Connecting with Pozo Support... Please wait.',
|
||||
icon: Icons.support_agent_outlined,
|
||||
);
|
||||
},
|
||||
style: OutlinedButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(56),
|
||||
side: const BorderSide(color: Color(0xFFAF101A), width: 2),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.forum, color: Color(0xFFAF101A), size: 18),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'Help / Talk to Support',
|
||||
style: TextStyle(
|
||||
color: Color(0xFFAF101A),
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -123,184 +123,212 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// Alert envelope pulse symbol
|
||||
Center(
|
||||
child: Container(
|
||||
width: 96,
|
||||
height: 96,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerHigh,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 72,
|
||||
height: 72,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.06),
|
||||
blurRadius: 10,
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Icon(
|
||||
Icons.sms,
|
||||
color: theme.colorScheme.primary,
|
||||
size: 38,
|
||||
),
|
||||
// Alert envelope pulse symbol with SlideFadeEntrance
|
||||
SlideFadeEntrance(
|
||||
delay: Duration.zero,
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 96,
|
||||
height: 96,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerHigh,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Verifying your number',
|
||||
textAlign: TextAlign.center,
|
||||
style: theme.textTheme.headlineMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"Sent code to $displayMobile",
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.pop(context),
|
||||
child: Text(
|
||||
'Edit',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 6 Custom Inputs Fields
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: List.generate(11, (index) {
|
||||
if (index.isOdd) {
|
||||
return const SizedBox(width: 8);
|
||||
}
|
||||
final fieldIndex = index ~/ 2;
|
||||
return SizedBox(
|
||||
width: 44,
|
||||
child: TextField(
|
||||
controller: _controllers[fieldIndex],
|
||||
focusNode: _focusNodes[fieldIndex],
|
||||
keyboardType: TextInputType.number,
|
||||
textAlign: TextAlign.center,
|
||||
maxLength: 1,
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
counterText: '',
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 12),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: theme.colorScheme.outlineVariant),
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 72,
|
||||
height: 72,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.06),
|
||||
blurRadius: 10,
|
||||
)
|
||||
],
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: theme.colorScheme.primary, width: 2),
|
||||
child: Icon(
|
||||
Icons.sms,
|
||||
color: theme.colorScheme.primary,
|
||||
size: 38,
|
||||
),
|
||||
),
|
||||
onChanged: (value) {
|
||||
if (value.isNotEmpty && fieldIndex < 5) {
|
||||
_focusNodes[fieldIndex + 1].requestFocus();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Auto detect loader
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerLow,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 100),
|
||||
child: Text(
|
||||
'Verifying your number',
|
||||
textAlign: TextAlign.center,
|
||||
style: theme.textTheme.headlineMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 150),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 14,
|
||||
height: 14,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation(theme.colorScheme.primary),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'Auto-detecting OTP...',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
"Sent code to $displayMobile",
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
InteractiveScale(
|
||||
onTap: () => Navigator.pop(context),
|
||||
child: Text(
|
||||
'Edit',
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Resending timer action column
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.schedule, size: 16, color: Colors.grey),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
_timeLeft > 0
|
||||
? 'Resend code in ${_getFormattedTime()}'
|
||||
: 'Resend Code via SMS',
|
||||
style: theme.textTheme.bodyMedium,
|
||||
// 6 Custom Inputs Fields with SlideFadeEntrance
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 200),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: List.generate(11, (index) {
|
||||
if (index.isOdd) {
|
||||
return const SizedBox(width: 8);
|
||||
}
|
||||
final fieldIndex = index ~/ 2;
|
||||
return SizedBox(
|
||||
width: 44,
|
||||
child: TextField(
|
||||
controller: _controllers[fieldIndex],
|
||||
focusNode: _focusNodes[fieldIndex],
|
||||
keyboardType: TextInputType.number,
|
||||
textAlign: TextAlign.center,
|
||||
maxLength: 1,
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
counterText: '',
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 12),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: theme.colorScheme.outlineVariant),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: theme.colorScheme.primary, width: 2),
|
||||
),
|
||||
),
|
||||
onChanged: (value) {
|
||||
if (value.isNotEmpty && fieldIndex < 5) {
|
||||
_focusNodes[fieldIndex + 1].requestFocus();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Auto detect loader card with SlideFadeEntrance
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 250),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerLow,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
],
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 14,
|
||||
height: 14,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation(theme.colorScheme.primary),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'Auto-detecting OTP...',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Resending timer action row
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 300),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.schedule, size: 16, color: Colors.grey),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
_timeLeft > 0
|
||||
? 'Resend code in ${_getFormattedTime()}'
|
||||
: 'Resend Code via SMS',
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// WhatsApp custom CTA button
|
||||
ElevatedButton(
|
||||
onPressed: () {},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black87,
|
||||
side: const BorderSide(color: Color(0xFFE2E2E2)),
|
||||
elevation: 1,
|
||||
minimumSize: const Size(200, 48),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
Text(
|
||||
// WhatsApp custom CTA button with InteractiveScale
|
||||
SlideFadeEntrance(
|
||||
delay: const Duration(milliseconds: 350),
|
||||
child: InteractiveScale(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
showTopAlert(
|
||||
context,
|
||||
'WhatsApp verification request sent!',
|
||||
icon: Icons.chat_bubble_outline,
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.forum, color: Colors.green, size: 18),
|
||||
label: const Text(
|
||||
'Resend via WhatsApp',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
],
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
side: const BorderSide(color: Color(0xFFE2E2E2)),
|
||||
elevation: 1,
|
||||
minimumSize: const Size(220, 48),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
|
|
@ -326,28 +354,42 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
_buildFooterAction(Icons.help_outline, 'Help'),
|
||||
_buildFooterAction(Icons.save, 'Save Draft'),
|
||||
TextButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
FadeSlidePageRoute(
|
||||
child: const BusinessSetupScreen(),
|
||||
_buildFooterAction(Icons.help_outline, 'Help', () {
|
||||
showTopAlert(
|
||||
context,
|
||||
'Support agent contacted. We will call you shortly.',
|
||||
icon: Icons.support_agent_outlined,
|
||||
);
|
||||
}),
|
||||
_buildFooterAction(Icons.save, 'Save Draft', () {
|
||||
showTopAlert(
|
||||
context,
|
||||
'Onboarding draft saved successfully!',
|
||||
icon: Icons.save_outlined,
|
||||
);
|
||||
}),
|
||||
InteractiveScale(
|
||||
child: TextButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
FadeSlidePageRoute(
|
||||
child: const BusinessSetupScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: const Text(
|
||||
'Verify Manually',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
label: const Icon(Icons.arrow_forward, size: 16),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: const Text(
|
||||
'Verify Manually',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
label: const Icon(Icons.arrow_forward, size: 16),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -363,21 +405,24 @@ class _OtpVerificationScreenState extends State<OtpVerificationScreen> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildFooterAction(IconData icon, String label) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(icon, color: Colors.grey[600], size: 22),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
)
|
||||
],
|
||||
Widget _buildFooterAction(IconData icon, String label, VoidCallback onTap) {
|
||||
return InteractiveScale(
|
||||
onTap: onTap,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(icon, color: Colors.grey[600], size: 22),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ class _SetupCompleteScreenState extends State<SetupCompleteScreen> with SingleTi
|
|||
late Animation<double> _cardOpacityAnimation;
|
||||
late Animation<double> _btnSlideAnimation;
|
||||
late Animation<double> _btnOpacityAnimation;
|
||||
double _btnScale = 1.0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -382,53 +381,42 @@ class _SetupCompleteScreenState extends State<SetupCompleteScreen> with SingleTi
|
|||
),
|
||||
);
|
||||
},
|
||||
child: MouseRegion(
|
||||
onEnter: (_) => setState(() => _btnScale = 1.03),
|
||||
onExit: (_) => setState(() => _btnScale = 1.0),
|
||||
child: AnimatedScale(
|
||||
scale: _btnScale,
|
||||
duration: const Duration(milliseconds: 150),
|
||||
child: GestureDetector(
|
||||
onTapDown: (_) => setState(() => _btnScale = 0.96),
|
||||
onTapUp: (_) => setState(() => _btnScale = 1.03),
|
||||
onTapCancel: () => setState(() => _btnScale = 1.0),
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
showTopAlert(
|
||||
context,
|
||||
'Launching Register Dashboard... Enjoy billing!',
|
||||
icon: Icons.rocket_launch_outlined,
|
||||
);
|
||||
Navigator.push(
|
||||
context,
|
||||
FadeSlidePageRoute(
|
||||
child: BillingDashboardScreen(
|
||||
businessName: widget.businessName,
|
||||
businessType: widget.businessType,
|
||||
gstin: widget.gstin,
|
||||
barcodeScanner: widget.barcodeScanner,
|
||||
kotManagement: widget.kotManagement,
|
||||
inventoryAlerts: widget.inventoryAlerts,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(56),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: InteractiveScale(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
showTopAlert(
|
||||
context,
|
||||
'Launching Register Dashboard... Enjoy billing!',
|
||||
icon: Icons.rocket_launch_outlined,
|
||||
);
|
||||
Navigator.push(
|
||||
context,
|
||||
FadeSlidePageRoute(
|
||||
child: BillingDashboardScreen(
|
||||
businessName: widget.businessName,
|
||||
businessType: widget.businessType,
|
||||
gstin: widget.gstin,
|
||||
barcodeScanner: widget.barcodeScanner,
|
||||
kotManagement: widget.kotManagement,
|
||||
inventoryAlerts: widget.inventoryAlerts,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const [
|
||||
Icon(Icons.receipt, size: 20),
|
||||
SizedBox(width: 8),
|
||||
Text('Create Your First Bill'),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(56),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: const [
|
||||
Icon(Icons.receipt, size: 20),
|
||||
SizedBox(width: 8),
|
||||
Text('Create Your First Bill'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ class _WelcomeScreenState extends State<WelcomeScreen> with SingleTickerProvider
|
|||
late Animation<double> _splashContainerOpacityAnimation;
|
||||
late Animation<double> _contentFadeAnimation;
|
||||
late Animation<Offset> _contentSlideAnimation;
|
||||
double _btnScale = 1.0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -491,37 +490,25 @@ class _WelcomeScreenState extends State<WelcomeScreen> with SingleTickerProvider
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: context.isMobile ? CrossAxisAlignment.center : CrossAxisAlignment.start,
|
||||
children: [
|
||||
MouseRegion(
|
||||
onEnter: (_) => setState(() => _btnScale = 1.03),
|
||||
onExit: (_) => setState(() => _btnScale = 1.0),
|
||||
child: GestureDetector(
|
||||
onTapDown: (_) => setState(() => _btnScale = 0.95),
|
||||
onTapUp: (_) => setState(() => _btnScale = 1.03),
|
||||
onTapCancel: () => setState(() => _btnScale = 1.0),
|
||||
child: AnimatedScale(
|
||||
scale: _btnScale,
|
||||
duration: const Duration(milliseconds: 150),
|
||||
curve: Curves.easeOut,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
FadeSlidePageRoute(child: const MobileLoginScreen()),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size(260, 54),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
Text('Get Started'),
|
||||
SizedBox(width: 8),
|
||||
Icon(Icons.arrow_forward, size: 18),
|
||||
],
|
||||
),
|
||||
),
|
||||
InteractiveScale(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
FadeSlidePageRoute(child: const MobileLoginScreen()),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size(260, 54),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
Text('Get Started'),
|
||||
SizedBox(width: 8),
|
||||
Icon(Icons.arrow_forward, size: 18),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ class PozoTheme {
|
|||
minimumSize: const Size.fromHeight(54),
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
textStyle: GoogleFonts.plusJakartaSans(
|
||||
fontSize: 16,
|
||||
|
|
|
|||
|
|
@ -41,14 +41,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
contacts_service:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: contacts_service
|
||||
sha256: f6d5ea33b31dfcdcd2e65d8abdc836502e04ddb0f66a96aa726fa9891ea9671e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.3"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -126,6 +118,14 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_contacts:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_contacts
|
||||
sha256: "388d32cd33f16640ee169570128c933b45f3259bddbfae7a100bb49e5ffea9ae"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.9+2"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
|
@ -424,14 +424,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: quiver
|
||||
sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.2"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ dependencies:
|
|||
google_fonts: ^5.1.0
|
||||
image_picker: ^1.0.4
|
||||
permission_handler: ^11.0.1
|
||||
contacts_service: ^0.6.3
|
||||
flutter_contacts: ^1.1.9
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||
Loading…
Reference in New Issue