## الثيم مقرَّب من سيرو (قرار المالك 2026-08-05) - البذرة صارت كحليّ سيرو #1A2340 بدل الأزرق العام، والثانوي #8C9CF8 - fromSeed وحدها تنتج من بذرة داكنة أسطحاً باهتة، فثُبّت الأساسي والثانوي والأسطح صراحةً وتُرك الباقي للمشتقّ — شكل سيرو مع تباين M3 - الأدوار الدلالية بأرقام سيرو الميدانية: نجاح #22C55E · تحذير #F59E0B · خطأ #EF4444، وبطاقة الليلي #2C2C2E ## مفتاح الدولة عاد core/ui/phone_field.dart — النمط المجرَّب في سيرو، لكن بلا intl_phone_field: تلك الحزمة تحمل 240 دولة ونحتاج أربعاً. CountryCode صريح ومقصور على أسواقنا. - الاختيار من ورقة سفلية لا قائمة منسدلة: أهداف لمس أكبر وأربع دول لا مئتان - الترتيب يبدأ بحزمة دولة المستأجر — الأردنيّ يرى الأردن أولاً بلا بحث - الرقم يُرسل كاملاً بالمفتاح بلا + (962790000001) كما يقبله الخادم - التحقّق صار بطول الدولة لا برقم ثابت: jo/sy تسع خانات، eg/iq عشر ## العراق - CountryCode.iraq (+964) في قائمة الهاتف - قنوات دفعه: زين كاش · كي كارد · نقد ⚠️ الباك إند لا يعرف حزمة `iq`: default-tariffs.ts فيه jo/sy/eg فقط، فمستأجر عراقي يُزوَّد بلا أي تعرفة. موثَّق في الكود — وهو شغل خادم يسبق أي إطلاق عراقي. flutter analyze نظيف في التطبيقين. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
71 lines
2.3 KiB
Dart
71 lines
2.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
|
|
import '../../../core/di.dart';
|
|
import '../../../core/session/session_cubit.dart';
|
|
import '../cubit/login_cubit.dart';
|
|
import '../cubit/login_state.dart';
|
|
import 'agreement_view.dart';
|
|
import 'otp_view.dart';
|
|
import 'permission_view.dart';
|
|
import 'phone_view.dart';
|
|
|
|
/// مضيف تدفّق الدخول: خطوة واحدة معروضة في كل لحظة، والانتقال بينها من
|
|
/// الـCubit لا من التنقّل — فلا يستطيع المستخدم الرجوع لخطوة تخطّاها.
|
|
class LoginPage extends StatefulWidget {
|
|
const LoginPage({super.key});
|
|
|
|
@override
|
|
State<LoginPage> createState() => _LoginPageState();
|
|
}
|
|
|
|
class _LoginPageState extends State<LoginPage> with WidgetsBindingObserver {
|
|
late final LoginCubit _cubit = sl<LoginCubit>();
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addObserver(this);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
WidgetsBinding.instance.removeObserver(this);
|
|
_cubit.close();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
void didChangeAppLifecycleState(AppLifecycleState lifecycle) {
|
|
// العودة من إعدادات النظام بعد منح الإذن يدوياً — بلا هذا الفحص تبقى
|
|
// شاشة الإذن عالقة (حالة حافة مثبتة ميدانياً، docs/39 §2).
|
|
if (lifecycle == AppLifecycleState.resumed &&
|
|
_cubit.state.step == LoginStep.permission) {
|
|
_cubit.checkLocationPermission();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return BlocProvider.value(
|
|
value: _cubit,
|
|
child: BlocListener<LoginCubit, LoginState>(
|
|
listenWhen: (prev, next) => next.step == LoginStep.done,
|
|
listener: (context, state) {
|
|
final user = state.user;
|
|
if (user != null) context.read<SessionCubit>().onLoggedIn(user);
|
|
},
|
|
child: BlocBuilder<LoginCubit, LoginState>(
|
|
buildWhen: (prev, next) => prev.step != next.step,
|
|
builder: (context, state) => switch (state.step) {
|
|
LoginStep.agreement => const AgreementView(),
|
|
LoginStep.permission => const PermissionView(),
|
|
LoginStep.phone => const PhoneView(),
|
|
LoginStep.otp || LoginStep.done => const OtpView(),
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|