Files
fitness/mobile/lib/screens/home_screen.dart
T

388 lines
15 KiB
Dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/workout_controller.dart';
import '../models/workout.dart';
import '../screens/history_screen.dart';
import '../services/sync_service.dart';
import '../services/auth_service.dart';
import 'training_screens.dart';
import '../utils/formatters.dart';
import 'phone_login_screen.dart';
const _ink = Color(0xFF17251E);
const _green = Color(0xFF456C52);
const _paper = Color(0xFFF5F7F2);
const _muted = Color(0xFF778078);
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
final workoutCtrl = Get.find<WorkoutController>();
final syncService = Get.find<SyncService>();
return Scaffold(
backgroundColor: _paper,
body: SafeArea(
child: CustomScrollView(
slivers: [
SliverPadding(
padding: const EdgeInsets.fromLTRB(22, 14, 22, 28),
sliver: SliverList.list(children: [
Row(children: [
Container(
width: 38,
height: 38,
decoration: BoxDecoration(
color: _green,
borderRadius: BorderRadius.circular(13),
),
child: const Center(
child: Text('S',
style: TextStyle(
color: Colors.white,
fontSize: 23,
fontFamily: 'Georgia',
fontStyle: FontStyle.italic)),
),
),
const SizedBox(width: 10),
const Text('SportPath',
style: TextStyle(
color: _ink,
fontSize: 17,
fontWeight: FontWeight.w700,
letterSpacing: -.4)),
const Spacer(),
Obx(() => Container(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 7),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20)),
child: Row(children: [
Icon(
syncService.isOnline
? Icons.cloud_done_outlined
: Icons.cloud_off_outlined,
size: 16,
color: syncService.isOnline
? _green
: Colors.orange.shade800),
const SizedBox(width: 5),
Text(syncService.isOnline ? 'متصل' : 'دون اتصال',
style:
const TextStyle(color: _ink, fontSize: 11)),
]),
)),
PopupMenuButton<String>(
tooltip: 'الحساب',
icon: const Icon(Icons.account_circle_outlined,
color: _green, size: 25),
onSelected: (value) async {
if (value == 'logout') {
await Get.find<AuthService>().logout();
Get.offAll(() => const PhoneLoginScreen());
}
},
itemBuilder: (context) => const [
PopupMenuItem(
value: 'logout',
child: Text('تسجيل الخروج'),
),
],
),
]),
const SizedBox(height: 34),
Text(_arabicToday(),
style: const TextStyle(
color: _green,
fontSize: 12,
fontWeight: FontWeight.w600,
letterSpacing: .2)),
const SizedBox(height: 8),
const Text('خطوة صغيرة،\nفرق كبير.',
style: TextStyle(
color: _ink,
fontSize: 35,
height: 1.18,
fontWeight: FontWeight.w600,
letterSpacing: -1.2)),
const SizedBox(height: 12),
const Text('خلّينا نبدأ حركة اليوم بإيقاع يناسبك.',
style:
TextStyle(color: _muted, fontSize: 14, height: 1.65)),
const SizedBox(height: 22),
const TrainingPlanSpotlight(),
const SizedBox(height: 26),
Row(children: [
const Text('ابدأ الآن',
style: TextStyle(
color: _ink,
fontSize: 18,
fontWeight: FontWeight.w600)),
const Spacer(),
TextButton.icon(
onPressed: () => Get.to(() => const HistoryScreen()),
icon: const Icon(Icons.arrow_back, size: 15),
label: const Text('سجل نشاطك'),
style: TextButton.styleFrom(
foregroundColor: _green,
textStyle: const TextStyle(fontSize: 12)),
),
]),
const SizedBox(height: 11),
Row(children: [
Expanded(
child: _ActionCard(
title: 'مشي بالخارج',
subtitle: 'سجّل المسافة والوقت',
icon: Icons.directions_walk_rounded,
tint: const Color(0xFFE8EFE5),
onTap: () =>
workoutCtrl.startWorkout(WorkoutType.walking))),
const SizedBox(width: 12),
Expanded(
child: _ActionCard(
title: 'جري خفيف',
subtitle: 'على قدر طاقتك',
icon: Icons.directions_run_rounded,
tint: const Color(0xFFF0EAE0),
onTap: () =>
workoutCtrl.startWorkout(WorkoutType.running))),
]),
Obx(() => workoutCtrl.currentWorkout.value != null
? Padding(
padding: const EdgeInsets.only(top: 16),
child: _ActiveWorkoutCard(controller: workoutCtrl))
: const SizedBox.shrink()),
const SizedBox(height: 28),
const Text('عادات تدعم يومك',
style: TextStyle(
color: _ink,
fontSize: 18,
fontWeight: FontWeight.w600)),
const SizedBox(height: 12),
const _HabitCard(
icon: Icons.restaurant_outlined,
title: 'وجبتك القادمة',
detail: 'سجّلها يدويًا الآن؛ تحليل الصور قيد الإعداد.',
tag: 'تغذية'),
const SizedBox(height: 9),
const _HabitCard(
icon: Icons.water_drop_outlined,
title: 'اشرب كوب ماء',
detail: 'تذكير بسيط للعناية بنفسك.',
tag: 'عافية'),
const SizedBox(height: 25),
Row(children: [
const Text('برامج التمرين',
style: TextStyle(
color: _ink,
fontSize: 18,
fontWeight: FontWeight.w600)),
const Spacer(),
TextButton(
onPressed: () =>
Get.to(() => const TrainingLibraryScreen()),
child: const Text('عرض المكتبة'),
),
]),
const SizedBox(height: 6),
const Text(
'الخطط والجلسات تُحمّل من الخادم وتُحفظ للعرض دون اتصال.',
style: TextStyle(color: _muted, fontSize: 11, height: 1.5)),
const SizedBox(height: 20),
Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: const Color(0xFFE8EEE5),
borderRadius: BorderRadius.circular(18)),
child: const Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(Icons.favorite_border, color: _green, size: 20),
SizedBox(width: 11),
Expanded(
child: Text(
'ابدأ بهدوء، وتوقف إذا شعرت بألم أو دوخة. الخطة العامة لا تغني عن نصيحة مختص عند وجود حالة صحية.',
style: TextStyle(
color: _green, fontSize: 11, height: 1.7))),
]),
),
]),
),
],
),
),
);
}
}
String _arabicToday() {
const weekdays = [
'الاثنين',
'الثلاثاء',
'الأربعاء',
'الخميس',
'الجمعة',
'السبت',
'الأحد'
];
const months = [
'يناير',
'فبراير',
'مارس',
'أبريل',
'مايو',
'يونيو',
'يوليو',
'أغسطس',
'سبتمبر',
'أكتوبر',
'نوفمبر',
'ديسمبر'
];
final now = DateTime.now();
final day = now.day.toString().replaceAllMapped(
RegExp(r'\d'), (match) => '٠١٢٣٤٥٦٧٨٩'[int.parse(match.group(0)!)]);
return '${weekdays[now.weekday - 1]}، $day ${months[now.month - 1]}';
}
class _ActionCard extends StatelessWidget {
const _ActionCard(
{required this.title,
required this.subtitle,
required this.icon,
required this.tint,
required this.onTap});
final String title, subtitle;
final IconData icon;
final Color tint;
final VoidCallback onTap;
@override
Widget build(BuildContext context) => Material(
color: Colors.white,
borderRadius: BorderRadius.circular(17),
child: InkWell(
borderRadius: BorderRadius.circular(17),
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 38,
height: 38,
decoration: BoxDecoration(
color: tint,
borderRadius: BorderRadius.circular(12)),
child: Icon(icon, color: _green, size: 21)),
const SizedBox(height: 13),
Text(title,
style: const TextStyle(
color: _ink,
fontSize: 13,
fontWeight: FontWeight.w600)),
const SizedBox(height: 3),
Text(subtitle,
style: const TextStyle(color: _muted, fontSize: 10)),
]))),
);
}
class _ActiveWorkoutCard extends StatelessWidget {
const _ActiveWorkoutCard({required this.controller});
final WorkoutController controller;
@override
Widget build(BuildContext context) => Container(
padding: const EdgeInsets.all(17),
decoration: BoxDecoration(
color: const Color(0xFF293A30),
borderRadius: BorderRadius.circular(18)),
child: Column(children: [
const Row(children: [
Icon(Icons.fiber_manual_record, size: 9, color: Color(0xFFB6D29D)),
SizedBox(width: 8),
Text('النشاط جارٍ',
style:
TextStyle(color: Colors.white, fontWeight: FontWeight.w600))
]),
const SizedBox(height: 15),
Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
_LiveStat('المسافة',
'${Formatters.distanceKm(controller.liveDistance.value)} كم'),
_LiveStat(
'الوقت', Formatters.time(controller.elapsedSeconds.value)),
_LiveStat('السرعة',
'${Formatters.speedKmh(controller.liveSpeed.value)} كم/س'),
]),
const SizedBox(height: 13),
Align(
alignment: Alignment.centerLeft,
child: TextButton(
onPressed: controller.stopWorkout,
style: TextButton.styleFrom(foregroundColor: Colors.white),
child: const Text('إنهاء وحفظ'))),
]),
);
}
class _LiveStat extends StatelessWidget {
const _LiveStat(this.label, this.value);
final String label, value;
@override
Widget build(BuildContext context) => Column(children: [
Text(label,
style: const TextStyle(color: Colors.white60, fontSize: 10)),
const SizedBox(height: 3),
Text(value,
style: const TextStyle(
color: Colors.white, fontSize: 14, fontWeight: FontWeight.w600))
]);
}
class _HabitCard extends StatelessWidget {
const _HabitCard(
{required this.icon,
required this.title,
required this.detail,
required this.tag});
final IconData icon;
final String title, detail, tag;
@override
Widget build(BuildContext context) => Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(15)),
child: Row(children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: const Color(0xFFE8EFE5),
borderRadius: BorderRadius.circular(13)),
child: Icon(icon, color: _green, size: 20)),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,
style: const TextStyle(
color: _ink,
fontSize: 13,
fontWeight: FontWeight.w600)),
const SizedBox(height: 2),
Text(detail,
style: const TextStyle(color: _muted, fontSize: 10))
])),
Text(tag,
style: const TextStyle(
color: _green, fontSize: 10, fontWeight: FontWeight.w600)),
]),
);
}