proxibuy/lib/presentation/pages/otp_page.dart

290 lines
9.9 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// lib/presentation/pages/otp_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:proxibuy/presentation/auth/bloc/auth_bloc.dart';
import 'package:proxibuy/presentation/pages/user_info_page.dart';
import '../../core/config/app_colors.dart';
import '../../core/gen/assets.gen.dart';
import '../utils/otp_timer_helper.dart';
class OtpPage extends StatefulWidget {
final String phoneNumber;
const OtpPage({super.key, required this.phoneNumber});
@override
State<OtpPage> createState() => _OtpPageState();
}
class _OtpPageState extends State<OtpPage> {
final List<FocusNode> _focusNodes = List.generate(5, (_) => FocusNode());
final List<TextEditingController> _controllers = List.generate(
5,
(_) => TextEditingController(),
);
late final OtpTimerHelper _otpTimer;
bool _hasError = false;
String? _errorMessage;
// ۱. افزودن متغیر وضعیت برای کنترل دکمه
bool _isOtpComplete = false;
@override
void initState() {
super.initState();
_otpTimer = OtpTimerHelper()..startTimer();
}
@override
void dispose() {
for (final node in _focusNodes) {
node.dispose();
}
for (final controller in _controllers) {
controller.dispose();
}
_otpTimer.dispose();
super.dispose();
}
Widget _buildOtpFields() {
return Directionality(
textDirection: TextDirection.ltr,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(5, (index) {
return SizedBox(
width: 60,
height: 60,
child: TextField(
controller: _controllers[index],
focusNode: _focusNodes[index],
keyboardType: TextInputType.number,
textAlign: TextAlign.center,
maxLength: 1,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: _hasError ? Colors.red : Colors.black,
),
decoration: InputDecoration(
counterText: '',
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color:
_hasError
? Colors.red
: (Theme.of(context)
.inputDecorationTheme
.enabledBorder
?.borderSide
.color ??
AppColors.grey),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: _hasError ? Colors.red : AppColors.primary,
width: 2,
),
),
),
onChanged: (value) {
if (_hasError) {
setState(() {
_hasError = false;
_errorMessage = null;
});
}
if (value.length == 1 && index < 4) {
FocusScope.of(context).requestFocus(_focusNodes[index + 1]);
}
if (value.isEmpty && index > 0) {
FocusScope.of(context).requestFocus(_focusNodes[index - 1]);
}
// ۲. به‌روزرسانی وضعیت کامل بودن کد
final otpCode = _controllers.map((c) => c.text).join();
setState(() {
_isOtpComplete = otpCode.length == 5;
});
},
),
);
}),
),
);
}
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
appBar: AppBar(backgroundColor: Colors.transparent, elevation: 0),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(child: Assets.icons.logo.svg(height: 160)),
const SizedBox(height: 40),
Text(
"کد یکبار مصرف",
style: textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
// --- تغییر اصلی: افزودن دکمه ویرایش شماره ---
Text(
"کد تایید به شماره ${widget.phoneNumber} ارسال شد.",
style: textTheme.titleMedium?.copyWith(
color: Colors.grey,
height: 1.5,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(Assets.icons.vector.path),
Center(
child: TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text("ویرایش شماره همراه"),
),
),
],
),
const SizedBox(height: 15),
_buildOtpFields(),
const SizedBox(height: 15),
if (_errorMessage != null)
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: Center(
child: Text(
_errorMessage!,
style: const TextStyle(
color: Colors.red,
fontWeight: FontWeight.bold,
),
),
),
)
else
const SizedBox(height: 40),
BlocConsumer<AuthBloc, AuthState>(
listener: (context, state) {
if (state is AuthLoading) {
setState(() {
_hasError = false;
_errorMessage = null;
});
} else if (state is AuthFailure) {
setState(() {
_hasError = true;
_errorMessage = state.message;
});
} else if (state is AuthVerified) {
if (state is AuthVerified) {
// این بخش را از کامنت خارج کنید
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (_) => const UserInfoPage(),
),
(route) => false,
);
}
}
},
builder: (context, state) {
if (state is AuthLoading) {
return const Center(child: CircularProgressIndicator());
}
return SizedBox(
width: double.infinity,
child: ElevatedButton(
// ۳. شرطی کردن دکمه "ورود"
onPressed: _isOtpComplete ? _verifyOtp : null,
child: const Text("ورود"),
),
);
},
),
const SizedBox(height: 32),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.icons.clock.path,
colorFilter: const ColorFilter.mode(
Colors.grey,
BlendMode.srcIn,
),
),
const SizedBox(width: 8),
ValueListenableBuilder<bool>(
valueListenable: _otpTimer.canResend,
builder: (context, canResend, child) {
return canResend
? TextButton(
onPressed: _resendOtp,
child: const Text("ارسال مجدد کد"),
)
: ValueListenableBuilder<int>(
valueListenable: _otpTimer.remainingSeconds,
builder:
(context, seconds, child) => Text(
"${_otpTimer.formatTime()} تا دریافت مجدد",
style: const TextStyle(
color: Colors.grey,
),
),
);
},
),
],
),
],
),
),
),
),
);
}
void _verifyOtp() {
final otpCode = _controllers.map((c) => c.text).join();
// یک بررسی اضافه می‌کنیم که اگر دکمه فعال بود حتما کد کامل باشد
if (otpCode.length == 5) {
context.read<AuthBloc>().add(VerifyOtpEvent(otpCode));
}
}
void _resendOtp() {
setState(() {
_hasError = false;
_errorMessage = null;
});
context.read<AuthBloc>().add(SendOtpEvent(widget.phoneNumber));
_otpTimer.resetTimer();
}
}