Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

212 wiersze
7.9 KiB

  1. // ignore_for_file: public_member_api_docs, sort_constructors_first
  2. import 'package:flutter/material.dart';
  3. import 'package:go_router/go_router.dart';
  4. import 'package:provider/provider.dart';
  5. import 'package:qadirneyriz/config/config.dart';
  6. import 'package:qadirneyriz/screens/auth/state/state.dart';
  7. import 'package:qadirneyriz/utils/enums/status.dart';
  8. import 'package:qadirneyriz/utils/timer/apt_simple_timer_with_controller.dart';
  9. import 'package:qadirneyriz/utils/tools/tools.dart';
  10. import 'package:qadirneyriz/widgets/otp_textfield.dart';
  11. import 'package:flutter_gen/gen_l10n/app_localizations.dart';
  12. class OtpScreen extends StatefulWidget {
  13. final String phoneNumber;
  14. const OtpScreen({
  15. super.key,
  16. required this.phoneNumber,
  17. });
  18. @override
  19. State<OtpScreen> createState() => _OtpScreenState();
  20. }
  21. class _OtpScreenState extends State<OtpScreen> {
  22. bool timerEnd = false;
  23. @override
  24. Widget build(BuildContext context) {
  25. return Scaffold(
  26. body: Consumer<AuthState>(
  27. builder: (context, value, child) {
  28. return CustomScrollView(
  29. slivers: <Widget>[
  30. SliverToBoxAdapter(
  31. child: Container(
  32. height: 30,
  33. color: config.ui.mainGreen,
  34. ),
  35. ),
  36. // Header Image with cut-out shape at the bottom
  37. SliverToBoxAdapter(
  38. child: ClipPath(
  39. child: Image.asset(
  40. 'assets/images/template.png',
  41. width: double.infinity,
  42. fit: BoxFit.cover,
  43. ),
  44. ),
  45. ),
  46. SliverPadding(
  47. padding: const EdgeInsets.symmetric(horizontal: 20),
  48. sliver: SliverToBoxAdapter(
  49. child: Container(
  50. padding: const EdgeInsets.all(16),
  51. decoration: BoxDecoration(
  52. color: Colors.white,
  53. borderRadius: BorderRadius.circular(16),
  54. boxShadow: [
  55. BoxShadow(
  56. color:
  57. Colors.black.withOpacity(0.1), // light shadow
  58. blurRadius: 10,
  59. spreadRadius: 5,
  60. ),
  61. ],
  62. ),
  63. child: Column(
  64. children: [
  65. Text(
  66. AppLocalizations.of(context)!.enterotp,
  67. style: TextStyle(
  68. color: config.ui.mainGray,
  69. fontSize: 18,
  70. fontWeight: FontWeight.bold),
  71. ),
  72. const SizedBox(
  73. height: 5,
  74. ),
  75. Text(
  76. AppLocalizations.of(context)!.an4digitotp,
  77. style: TextStyle(
  78. color: config.ui.mainGray.withOpacity(.5),
  79. fontSize: 14,
  80. ),
  81. ),
  82. const SizedBox(
  83. height: 5,
  84. ),
  85. Row(
  86. mainAxisAlignment: MainAxisAlignment.center,
  87. children: [
  88. Text(
  89. widget.phoneNumber,
  90. style: TextStyle(
  91. color: config.ui.mainGray.withOpacity(.5),
  92. fontSize: 16,
  93. fontWeight: FontWeight.bold),
  94. ),
  95. Icon(
  96. Icons.phone_callback_outlined,
  97. color: config.ui.mainGreen,
  98. )
  99. ],
  100. ),
  101. const SizedBox(
  102. height: 10,
  103. ),
  104. OTPTextField(
  105. length: 4,
  106. onSubmitted: (onSubmitted) {
  107. otpCheckCode(onSubmitted, value);
  108. }),
  109. const SizedBox(
  110. height: 20,
  111. ),
  112. Row(
  113. mainAxisAlignment: MainAxisAlignment.center,
  114. children: [
  115. resendOtpbutton(value),
  116. if (!timerEnd)
  117. Directionality(
  118. textDirection: TextDirection.ltr,
  119. child: AptSimpleTimerWithController(
  120. seconds: 60,
  121. fontSize: 16,
  122. timerColor: const Color(0xff8FA5B6)
  123. .withOpacity(0.6),
  124. onTimerEnd: () {
  125. setState(() {
  126. timerEnd = true;
  127. });
  128. },
  129. ),
  130. ),
  131. ],
  132. ),
  133. ],
  134. )),
  135. ),
  136. ),
  137. ],
  138. );
  139. },
  140. ),
  141. );
  142. }
  143. Widget resendOtpbutton(AuthState value) {
  144. return timerEnd
  145. ? GestureDetector(
  146. onTap: value.statusSendotp != Status.loading
  147. ? () async {
  148. final status = await value.sendOtp(
  149. mobile: widget.phoneNumber,
  150. );
  151. if (status == Status.ready) {
  152. setState(() {
  153. timerEnd = false;
  154. });
  155. } else {
  156. Tools.showCustomSnackBar(
  157. text: value.errorsSendOtp == null
  158. ? value.messageSendOtp ?? 'An error has occurred'
  159. : Tools.combineErrorMessages(
  160. value.errorsSendOtp ?? {}),
  161. isError: true,
  162. context,
  163. );
  164. }
  165. }
  166. : null,
  167. child: Text(
  168. value.statusSendotp == Status.loading
  169. ? AppLocalizations.of(context)!.loading
  170. : AppLocalizations.of(context)!.resend,
  171. style: TextStyle(
  172. color: config.ui.secendGreen,
  173. fontSize: 16,
  174. ),
  175. ),
  176. )
  177. : Container();
  178. }
  179. void otpCheckCode(onSubmitted, AuthState value) async {
  180. if (onSubmitted.length == 4) {
  181. final status =
  182. await value.login(mobile: widget.phoneNumber, otp: onSubmitted);
  183. if (status == Status.ready) {
  184. context.goNamed('navigate', pathParameters: {'tab': '0'});
  185. } else if (status == Status.error) {
  186. Tools.showCustomSnackBar(
  187. text: value.errorsLogin == null
  188. ? value.messageLogin ?? AppLocalizations.of(context)!.haserror
  189. : Tools.combineErrorMessages(value.errorsLogin ?? {}),
  190. isError: true,
  191. context,
  192. );
  193. setState(() {});
  194. } else if (status == Status.empty) {
  195. Tools.showCustomSnackBar(
  196. text: value.errorsLogin == null
  197. ? value.messageLogin ?? AppLocalizations.of(context)!.haserror
  198. : Tools.combineErrorMessages(value.errorsLogin ?? {}),
  199. isError: true,
  200. context,
  201. );
  202. onSubmitted = '';
  203. }
  204. }
  205. }
  206. }