Files
tripz-llc/apps/rider/lib/features/auth/view/phone_screen.dart
T

177 lines
7.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../core/l10n/app_localizations.dart';
import '../../../core/build_config.dart';
import '../cubit/auth_cubit.dart';
import '../cubit/auth_state.dart';
import '../../../core/design/tokens.dart';
import '../../../core/utils/phone_normalizer.dart';
import '../../../core/ui/phone_intel/intl_phone_field.dart';
import '../../../core/ui/phone_intel/phone_number.dart';
import '../../../core/ui/tripz_scaffold.dart';
import '../../../core/ui/tripz_button.dart';
/// شاشة إدخال الهاتف — تطابق stitch/phone_login.html:
/// خلفية فاتحة + شعار بالمنتصف بالأعلى + sheet أبيض سفلي بحواف مدورة
/// فيه حقل الهاتف (علم + كود دولة + رقم) وزر إرسال الكود.
class PhoneScreen extends StatefulWidget {
const PhoneScreen({super.key});
@override
State<PhoneScreen> createState() => _PhoneScreenState();
}
class _PhoneScreenState extends State<PhoneScreen> {
PhoneNumber? _phoneNumber;
bool _valid = false;
void _sendOtp(BuildContext context) {
if (_phoneNumber == null) return;
final normalized = PhoneNormalizer.normalize(
_phoneNumber!.number,
isoCode: _phoneNumber!.countryISOCode,
);
context.read<AuthCubit>().sendOtp(normalized);
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final theme = Theme.of(context);
final cs = theme.colorScheme;
return BlocConsumer<AuthCubit, AuthState>(
listener: (context, state) {
if (state.status == AuthStatus.error && state.error != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.error!)),
);
}
},
builder: (context, state) {
final isLoading = state.status == AuthStatus.authenticating;
return TripzScaffold(
backgroundColor: cs.surfaceContainerLow,
padding: false,
safeAreaBottom: false,
bottomAction: TripzButton(
onPressed: (_valid && !isLoading) ? () => _sendOtp(context) : null,
label: l10n.sendCode,
loading: isLoading,
),
child: Column(
children: [
// ── منطقة الشعار (النصف العلوي) ──
Expanded(
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: T.s32),
child: Image.asset(
'assets/images/login_hero.png',
width: 200,
fit: BoxFit.contain,
errorBuilder: (_, __, ___) => Icon(
Icons.directions_car_filled_rounded,
size: 96,
color: cs.primary,
),
),
),
),
),
// ── بطاقة الإدخال السفلية (منطقة الإبهام) ──
Container(
width: double.infinity,
decoration: BoxDecoration(
color: cs.surfaceContainerLowest,
borderRadius: const BorderRadius.vertical(top: Radius.circular(T.r24)),
boxShadow: const [T.shadowLg],
),
padding: const EdgeInsets.fromLTRB(T.pagePadding, T.s16, T.pagePadding, T.s32),
child: SafeArea(
top: false,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// مقبض السحب الزخرفي
Center(
child: Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: cs.outlineVariant,
borderRadius: BorderRadius.circular(T.rCircle),
),
),
),
const SizedBox(height: T.s24),
// العنوان
Text(
l10n.loginTitle,
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w600,
color: cs.onSurface,
),
),
const SizedBox(height: T.s8),
Text(
l10n.phoneLoginSubtitle,
style: theme.textTheme.bodyMedium?.copyWith(
color: cs.onSurfaceVariant,
),
),
const SizedBox(height: T.s24),
// حقل الهاتف — علم + كود الدولة (افتراضي BuildConfig.defaultCountry) + الرقم
IntlPhoneField(
initialCountryCode: BuildConfig.defaultCountry,
languageCode: Localizations.localeOf(context).languageCode,
textAlign: TextAlign.left,
style: theme.textTheme.bodyLarge,
dropdownTextStyle: theme.textTheme.bodyLarge,
flagsButtonPadding: const EdgeInsets.symmetric(horizontal: T.s12),
dropdownDecoration: BoxDecoration(
border: BorderDirectional(
end: BorderSide(color: cs.outlineVariant),
),
),
decoration: InputDecoration(
hintText: l10n.phoneHint,
filled: true,
fillColor: cs.surfaceContainerLowest,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(T.r8),
borderSide: BorderSide(color: cs.outlineVariant),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(T.r8),
borderSide: BorderSide(color: cs.outlineVariant),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(T.r8),
borderSide: BorderSide(color: cs.primary, width: 1.5),
),
contentPadding: const EdgeInsets.symmetric(horizontal: T.s16, vertical: T.s16),
),
onChanged: (phone) {
_phoneNumber = phone;
final num = phone.number;
_valid = num.isNotEmpty && num.length >= 7;
setState(() {});
},
),
],
),
),
),
],
),
);
},
);
}
}