commit
1e65a67f0e
@ -0,0 +1,194 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_export.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/weather/weather_view_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/weather/weather_details_page.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class WeatherWidget extends StatelessWidget {
|
||||||
|
final bool showButton;
|
||||||
|
|
||||||
|
const WeatherWidget({super.key, this.showButton = true});
|
||||||
|
|
||||||
|
String _getWeatherIcon(String? categoryValue) {
|
||||||
|
if (categoryValue == null) return "assets/images/svg/cloudyweather.svg";
|
||||||
|
|
||||||
|
final lowerValue = categoryValue.toLowerCase();
|
||||||
|
if (lowerValue.contains('sunny') || lowerValue.contains('clear')) {
|
||||||
|
return "assets/images/svg/morning.svg";
|
||||||
|
} else if (lowerValue.contains('cloudy') || lowerValue.contains('cloud')) {
|
||||||
|
return "assets/images/svg/cloudyweather.svg";
|
||||||
|
} else if (lowerValue.contains('rain')) {
|
||||||
|
return "assets/images/svg/cloudyweather.svg";
|
||||||
|
}
|
||||||
|
return "assets/images/svg/cloudyweather.svg";
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatDateTime(DateTime dateTime) {
|
||||||
|
final formatter = DateFormat('MMMM dd, yyyy - EEEE, HH:mm');
|
||||||
|
return formatter.format(dateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Consumer<WeatherMonitorViewModel>(
|
||||||
|
builder: (context, weatherVM, child) {
|
||||||
|
final currentCity = weatherVM.cityInfoList.isNotEmpty
|
||||||
|
? weatherVM.cityInfoList.first
|
||||||
|
: null;
|
||||||
|
|
||||||
|
final now = DateTime.now();
|
||||||
|
final temperature = currentCity?.temperature;
|
||||||
|
final healthTip = LocaleKeys.healthTipsBasedOnCurrentWeather.tr();
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(16.w),
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||||
|
color: AppColors.whiteColor,
|
||||||
|
borderRadius: 24.r,
|
||||||
|
hasShadow: false,
|
||||||
|
),
|
||||||
|
child: weatherVM.isLoading
|
||||||
|
? _buildLoadingState()
|
||||||
|
: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Utils.buildSvgWithAssets(
|
||||||
|
icon: _getWeatherIcon(currentCity?.categoryValue),
|
||||||
|
width: 64.w,
|
||||||
|
height: 64.w,
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_formatDateTime(now).toText12(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: AppColors.greyTextColor,
|
||||||
|
),
|
||||||
|
SizedBox(height: 4.w),
|
||||||
|
"$temperature°C"
|
||||||
|
.toText16(
|
||||||
|
isBold: true,
|
||||||
|
weight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
SizedBox(height: 4.w),
|
||||||
|
healthTip.toText12(
|
||||||
|
color: AppColors.greyTextColor,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
maxLine: 2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (showButton) ...[
|
||||||
|
SizedBox(height: 16.w),
|
||||||
|
CustomButton(
|
||||||
|
text: LocaleKeys.viewDetails.tr(context: context),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
CustomPageRoute(
|
||||||
|
page: ChangeNotifierProvider.value(
|
||||||
|
value: weatherVM,
|
||||||
|
child: const WeatherDetailsPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
backgroundColor: AppColors.secondaryLightRedColor,
|
||||||
|
borderColor: AppColors.secondaryLightRedColor,
|
||||||
|
textColor: AppColors.primaryRedColor,
|
||||||
|
fontSize: 14.f,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
borderRadius: 10.r,
|
||||||
|
height: 40.h,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetrical(24.w, 0.w);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildLoadingState() {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 64.w,
|
||||||
|
height: 64.w,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.lightGrayBGColor,
|
||||||
|
borderRadius: BorderRadius.circular(12.r),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 12.h,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.lightGrayBGColor,
|
||||||
|
borderRadius: BorderRadius.circular(6.r),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 8.h),
|
||||||
|
Container(
|
||||||
|
width: 100.w,
|
||||||
|
height: 16.h,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.lightGrayBGColor,
|
||||||
|
borderRadius: BorderRadius.circular(8.r),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 8.h),
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
height: 12.h,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.lightGrayBGColor,
|
||||||
|
borderRadius: BorderRadius.circular(6.r),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 16.w),
|
||||||
|
Container(
|
||||||
|
height: 56.h,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.lightGrayBGColor,
|
||||||
|
borderRadius: BorderRadius.circular(10.r),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,185 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_export.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/app_state.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/dependencies.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/weather/models/waether_cities_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/weather/weather_view_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||||
|
import 'package:hmg_patient_app_new/presentation/hmg_services/widgets/weather_widget.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class WeatherDetailsPage extends StatelessWidget {
|
||||||
|
const WeatherDetailsPage({super.key});
|
||||||
|
|
||||||
|
Color _getColorFromColorName(ColorName? colorName) {
|
||||||
|
if (colorName == null) return AppColors.successColor;
|
||||||
|
|
||||||
|
switch (colorName) {
|
||||||
|
case ColorName.GREEN:
|
||||||
|
return AppColors.successColor;
|
||||||
|
case ColorName.ORANGE:
|
||||||
|
return AppColors.warningColor;
|
||||||
|
case ColorName.RED:
|
||||||
|
return AppColors.errorColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Color _getBackgroundColorFromColorName(ColorName? colorName) {
|
||||||
|
if (colorName == null) return AppColors.successLightColor.withValues(alpha: 0.1);
|
||||||
|
|
||||||
|
switch (colorName) {
|
||||||
|
case ColorName.GREEN:
|
||||||
|
return AppColors.successLightColor.withValues(alpha: 0.1);
|
||||||
|
case ColorName.ORANGE:
|
||||||
|
return AppColors.warningLightColor.withValues(alpha: 0.1);
|
||||||
|
case ColorName.RED:
|
||||||
|
return AppColors.errorLightColor.withValues(alpha: 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IconData _getIconFromCategory(String? category) {
|
||||||
|
if (category == null) return Icons.wb_sunny_outlined;
|
||||||
|
|
||||||
|
final lowerCategory = category.toLowerCase();
|
||||||
|
if (lowerCategory.contains('walking') || lowerCategory.contains('activity')) {
|
||||||
|
return Icons.directions_walk;
|
||||||
|
} else if (lowerCategory.contains('weather') || lowerCategory.contains('temperature')) {
|
||||||
|
return Icons.thermostat_outlined;
|
||||||
|
} else if (lowerCategory.contains('elderly') || lowerCategory.contains('hiking')) {
|
||||||
|
return Icons.elderly;
|
||||||
|
}
|
||||||
|
return Icons.wb_sunny_outlined;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Consumer<WeatherMonitorViewModel>(
|
||||||
|
builder: (context, weatherVM, child) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: AppColors.bgScaffoldColor,
|
||||||
|
body: CollapsingListView(
|
||||||
|
title: LocaleKeys.healthWeatherIndicators.tr(),
|
||||||
|
isLeading: true,
|
||||||
|
child: weatherVM.isLoading
|
||||||
|
? Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: AppColors.primaryRedColor,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Reuse WeatherWidget at the top (without the button)
|
||||||
|
const WeatherWidget(showButton: false),
|
||||||
|
|
||||||
|
SizedBox(height: 16.h),
|
||||||
|
|
||||||
|
// Categories List (Sliders)
|
||||||
|
...weatherVM.cityInfoList.map((cityInfo) {
|
||||||
|
return _buildCategoryCard(
|
||||||
|
description: cityInfo.name ?? '',
|
||||||
|
categoryLabel: cityInfo.category ?? '',
|
||||||
|
color: _getColorFromColorName(cityInfo.colorName),
|
||||||
|
backgroundColor: _getBackgroundColorFromColorName(cityInfo.colorName),
|
||||||
|
icon: _getIconFromCategory(cityInfo.category),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
|
||||||
|
SizedBox(height: 24.h),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCategoryCard({
|
||||||
|
required String description,
|
||||||
|
required String categoryLabel,
|
||||||
|
required Color color,
|
||||||
|
required Color backgroundColor,
|
||||||
|
required IconData icon,
|
||||||
|
}) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(bottom: 16.h),
|
||||||
|
padding: EdgeInsets.all(16.w),
|
||||||
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
||||||
|
color: AppColors.whiteColor,
|
||||||
|
borderRadius: 16.r,
|
||||||
|
hasShadow: false,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Icon
|
||||||
|
Container(
|
||||||
|
width: 40.w,
|
||||||
|
height: 40.w,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: backgroundColor,
|
||||||
|
borderRadius: BorderRadius.circular(8.r),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
icon,
|
||||||
|
color: color,
|
||||||
|
size: 24.w,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
// Content
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
description.toText14(
|
||||||
|
isBold: true,
|
||||||
|
color: AppColors.textColor,
|
||||||
|
),
|
||||||
|
SizedBox(height: 8.h),
|
||||||
|
// Progress bar
|
||||||
|
Container(
|
||||||
|
height: 4.h,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.lightGrayBGColor,
|
||||||
|
borderRadius: BorderRadius.circular(2.r),
|
||||||
|
),
|
||||||
|
child: FractionallySizedBox(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
widthFactor: 0.7,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color,
|
||||||
|
borderRadius: BorderRadius.circular(2.r),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 12.w),
|
||||||
|
// Category Label
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: backgroundColor,
|
||||||
|
borderRadius: BorderRadius.circular(8.r),
|
||||||
|
),
|
||||||
|
child: categoryLabel.toText12(
|
||||||
|
color: color,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
maxLine: 1
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
).paddingSymmetrical(24.w, 0.w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue