You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
481 lines
16 KiB
481 lines
16 KiB
import 'package:common_utils/common_utils.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
import 'package:get/get.dart';
|
|
|
|
/// 构建天气内容卡片
|
|
Widget hefengWeatherContent(BuildContext context, String city,
|
|
Map<String, dynamic> now, List<Map<String, dynamic>> days) {
|
|
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'weatherQuery'.tr, // 翻译键代表的中文文本内容:查询天气情况:
|
|
style: TextStyle(
|
|
color:
|
|
isDarkMode ? Colors.white.withValues(alpha: 0.9) : Colors.black87,
|
|
fontSize: 14.sp,
|
|
height: 1.4,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
),
|
|
SizedBox(height: 12.h),
|
|
Container(
|
|
padding: EdgeInsets.all(16.w),
|
|
decoration: BoxDecoration(
|
|
gradient: _getWeatherGradient(now['textDay'] ?? '', isDarkMode),
|
|
borderRadius: BorderRadius.circular(12.r),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withValues(alpha: isDarkMode ? 0.3 : 0.1),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"${city} ${now["fxDate"]}",
|
|
style: TextStyle(
|
|
fontSize: 16.sp,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
SizedBox(height: 4.w),
|
|
Text(
|
|
_getWeatherDescriptionTranslation(now["textDay"]),
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
color: Colors.white.withValues(alpha: 0.9),
|
|
),
|
|
),
|
|
SizedBox(height: 8.h),
|
|
Row(
|
|
children: [
|
|
Text(
|
|
"${now["tempMax"]}°",
|
|
style: TextStyle(
|
|
fontSize: 36.sp,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
SizedBox(width: 8.w),
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'${'highest'.tr}: ${now["tempMax"]}°', // 翻译键代表的中文文本内容:最高
|
|
style: TextStyle(
|
|
fontSize: 12.sp,
|
|
color: Colors.white.withValues(alpha: 0.9),
|
|
),
|
|
),
|
|
Text(
|
|
'${'lowest'.tr}: ${now["tempMin"]}°', // 翻译键代表的中文文本内容:最低
|
|
style: TextStyle(
|
|
fontSize: 12.sp,
|
|
color: Colors.white.withValues(alpha: 0.9),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
getWeatherSvgIcon(
|
|
now['iconDay'],
|
|
size: 48.sp,
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 16.h),
|
|
Container(
|
|
height: 1,
|
|
color: Colors.white.withValues(alpha: isDarkMode ? 0.15 : 0.2),
|
|
),
|
|
SizedBox(height: 16.h),
|
|
Text(
|
|
'futureForecast'.tr, // 翻译键代表的中文文本内容:未来预报
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
SizedBox(height: 8.h),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: List.generate(
|
|
days.length,
|
|
(index) {
|
|
String dateStr = days[index]['fxDate'];
|
|
DateTime? date = DateUtil.getDateTime(dateStr);
|
|
Widget weatherIcon =
|
|
getWeatherSvgIcon(days[index]['iconDay'], size: 20.sp);
|
|
|
|
// 获取星期几的翻译键
|
|
String weekdayKey = _getWeekdayTranslationKey(date);
|
|
String weekday = weekdayKey.tr; // 翻译键代表的中文文本内容:对应的星期几
|
|
|
|
String temp = "${days[index]['tempMax']}°";
|
|
return _buildForecastDay(
|
|
weekday,
|
|
weatherIcon,
|
|
temp,
|
|
);
|
|
},
|
|
),
|
|
),
|
|
SizedBox(height: 16.h),
|
|
_hefengWeatherTip(context, now, isDarkMode),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
/// 获取天气描述的翻译
|
|
String _getWeatherDescriptionTranslation(String weatherDescription) {
|
|
final description = weatherDescription.trim();
|
|
|
|
// 天气描述映射表
|
|
final Map<String, String> weatherMap = {
|
|
// 晴天类
|
|
'晴': 'sunny'.tr, // 翻译键代表的中文文本内容:晴
|
|
'少云': 'partlyCloudy'.tr, // 翻译键代表的中文文本内容:少云
|
|
'晴间多云': 'partlySunny'.tr, // 翻译键代表的中文文本内容:晴间多云
|
|
|
|
// 多云类
|
|
'多云': 'cloudy'.tr, // 翻译键代表的中文文本内容:多云
|
|
'阴': 'overcast'.tr, // 翻译键代表的中文文本内容:阴
|
|
|
|
// 雨天类
|
|
'阵雨': 'shower'.tr, // 翻译键代表的中文文本内容:阵雨
|
|
'雷阵雨': 'thundershower'.tr, // 翻译键代表的中文文本内容:雷阵雨
|
|
'小雨': 'lightRain'.tr, // 翻译键代表的中文文本内容:小雨
|
|
'中雨': 'moderateRain'.tr, // 翻译键代表的中文文本内容:中雨
|
|
'大雨': 'heavyRain'.tr, // 翻译键代表的中文文本内容:大雨
|
|
'暴雨': 'rainstorm'.tr, // 翻译键代表的中文文本内容:暴雨
|
|
|
|
// 雪天类
|
|
'阵雪': 'snowShower'.tr, // 翻译键代表的中文文本内容:阵雪
|
|
'小雪': 'lightSnow'.tr, // 翻译键代表的中文文本内容:小雪
|
|
'中雪': 'moderateSnow'.tr, // 翻译键代表的中文文本内容:中雪
|
|
'大雪': 'heavySnow'.tr, // 翻译键代表的中文文本内容:大雪
|
|
'暴雪': 'blizzard'.tr, // 翻译键代表的中文文本内容:暴雪
|
|
|
|
// 雾霾类
|
|
'雾': 'fog'.tr, // 翻译键代表的中文文本内容:雾
|
|
'霾': 'haze'.tr, // 翻译键代表的中文文本内容:霾
|
|
|
|
// 风类
|
|
'大风': 'strongWind'.tr, // 翻译键代表的中文文本内容:大风
|
|
};
|
|
|
|
// 返回翻译后的描述,如果没有找到对应翻译则返回原文
|
|
return weatherMap[description] ?? description;
|
|
}
|
|
|
|
/// 获取星期几的翻译键
|
|
String _getWeekdayTranslationKey(DateTime? date) {
|
|
if (date == null) return 'unknown';
|
|
|
|
switch (date.weekday) {
|
|
case 1:
|
|
return 'monday'; // 翻译键代表的中文文本内容:周一
|
|
case 2:
|
|
return 'tuesday'; // 翻译键代表的中文文本内容:周二
|
|
case 3:
|
|
return 'wednesday'; // 翻译键代表的中文文本内容:周三
|
|
case 4:
|
|
return 'thursday'; // 翻译键代表的中文文本内容:周四
|
|
case 5:
|
|
return 'friday'; // 翻译键代表的中文文本内容:周五
|
|
case 6:
|
|
return 'saturday'; // 翻译键代表的中文文本内容:周六
|
|
case 7:
|
|
return 'sunday'; // 翻译键代表的中文文本内容:周日
|
|
default:
|
|
return 'unknown';
|
|
}
|
|
}
|
|
|
|
/// 构建智能天气提示
|
|
Widget _hefengWeatherTip(
|
|
BuildContext context, Map<String, dynamic> now, bool isDarkMode) {
|
|
final weatherTip = _generateWeatherTip(now);
|
|
|
|
return Container(
|
|
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 12.w),
|
|
decoration: BoxDecoration(
|
|
color: isDarkMode
|
|
? Colors.white.withValues(alpha: 0.15)
|
|
: Colors.white.withValues(alpha: 0.2),
|
|
borderRadius: BorderRadius.circular(8.r),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
weatherTip.icon,
|
|
size: 16.sp,
|
|
color: Colors.white,
|
|
),
|
|
SizedBox(width: 8.w),
|
|
Flexible(
|
|
child: Text(
|
|
weatherTip.message,
|
|
style: TextStyle(
|
|
fontSize: 12.sp,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 天气提示数据类
|
|
class WeatherTip {
|
|
final IconData icon;
|
|
final String message;
|
|
|
|
const WeatherTip({required this.icon, required this.message});
|
|
}
|
|
|
|
/// 根据天气数据生成智能提示
|
|
WeatherTip _generateWeatherTip(Map<String, dynamic> now) {
|
|
final dayWeather = (now['textDay'] as String? ?? '').trim();
|
|
final dayTemp = int.tryParse(now['tempMax']?.toString() ?? '0') ?? 0;
|
|
final nightTemp = int.tryParse(now['tempMin']?.toString() ?? '0') ?? 0;
|
|
|
|
// 高温提示 (>= 35°C)
|
|
if (dayTemp >= 35) {
|
|
return WeatherTip(
|
|
icon: Icons.local_fire_department,
|
|
message: 'hotWeatherTip'.tr, // 翻译键代表的中文文本内容:今日高温炎热,请注意防暑降温,多喝水避免中暑
|
|
);
|
|
}
|
|
|
|
// 低温提示 (<= 5°C)
|
|
if (dayTemp <= 5) {
|
|
return WeatherTip(
|
|
icon: Icons.ac_unit,
|
|
message: 'coldWeatherTip'.tr, // 翻译键代表的中文文本内容:今日气温较低,外出请注意保暖,小心感冒
|
|
);
|
|
}
|
|
|
|
// 雨天提示
|
|
if (dayWeather.contains('雨') ||
|
|
['阵雨', '雷阵雨', '小雨', '中雨', '大雨', '暴雨']
|
|
.any((rain) => dayWeather.contains(rain))) {
|
|
return WeatherTip(
|
|
icon: Icons.umbrella,
|
|
message: 'rainyWeatherTip'.tr, // 翻译键代表的中文文本内容:今日有雨,外出请携带雨具,注意出行安全
|
|
);
|
|
}
|
|
|
|
// 雪天提示
|
|
if (dayWeather.contains('雪') ||
|
|
['阵雪', '小雪', '中雪', '大雪', '暴雪'].any((snow) => dayWeather.contains(snow))) {
|
|
return WeatherTip(
|
|
icon: Icons.ac_unit,
|
|
message: 'snowyWeatherTip'.tr, // 翻译键代表的中文文本内容:今日有雪,路面湿滑请注意安全,做好保暖措施
|
|
);
|
|
}
|
|
|
|
// 雾霾提示
|
|
if (dayWeather.contains('雾') ||
|
|
dayWeather.contains('霾') ||
|
|
['雾', '浓雾', '霾', '中度霾', '重度霾'].any((fog) => dayWeather.contains(fog))) {
|
|
return WeatherTip(
|
|
icon: Icons.blur_on,
|
|
message: 'foggyWeatherTip'.tr, // 翻译键代表的中文文本内容:今日有雾霾,能见度较低,外出请佩戴口罩
|
|
);
|
|
}
|
|
|
|
// 大风提示
|
|
if (dayWeather.contains('风') ||
|
|
['大风', '强风', '疾风', '烈风'].any((wind) => dayWeather.contains(wind))) {
|
|
return WeatherTip(
|
|
icon: Icons.air,
|
|
message: 'windyWeatherTip'.tr, // 翻译键代表的中文文本内容:今日风力较大,外出请注意安全,固定好物品
|
|
);
|
|
}
|
|
|
|
// 晴天紫外线提示 (温度 >= 25°C)
|
|
if (['晴', '少云', '晴间多云'].any((sunny) => dayWeather.contains(sunny)) &&
|
|
dayTemp >= 25) {
|
|
return WeatherTip(
|
|
icon: Icons.wb_sunny,
|
|
message: 'sunnyWeatherTip'.tr, // 翻译键代表的中文文本内容:今日阳光充足紫外线较强,外出请做好防晒
|
|
);
|
|
}
|
|
|
|
// 温差大提示 (>= 10°C)
|
|
if ((dayTemp - nightTemp).abs() >= 10) {
|
|
return WeatherTip(
|
|
icon: Icons.thermostat,
|
|
message: 'temperatureDifferenceTip'.tr, // 翻译键代表的中文文本内容:今日昼夜温差较大,请适时增减衣物
|
|
);
|
|
}
|
|
|
|
// 默认舒适天气提示
|
|
return WeatherTip(
|
|
icon: Icons.wb_sunny_outlined,
|
|
message: 'comfortableWeatherTip'.tr, // 翻译键代表的中文文本内容:今日天气适宜,是外出活动的好时光
|
|
);
|
|
}
|
|
|
|
/// 构建单日天气预报
|
|
Widget _buildForecastDay(String day, Widget icon, String temp) {
|
|
return Column(
|
|
children: [
|
|
Text(
|
|
day,
|
|
style: TextStyle(
|
|
fontSize: 12.sp,
|
|
color: Colors.white.withValues(alpha: 0.9),
|
|
),
|
|
),
|
|
SizedBox(height: 4.h),
|
|
icon,
|
|
SizedBox(height: 4.h),
|
|
Text(
|
|
temp,
|
|
style: TextStyle(
|
|
fontSize: 12.sp,
|
|
fontWeight: FontWeight.w500,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
/// 获取天气 SVG 图标 Widget
|
|
Widget getWeatherSvgIcon(String mappedCode, {double size = 24.0}) {
|
|
return _getWeatherSvgByCode(mappedCode, size: size);
|
|
}
|
|
|
|
/// 根据和风天气代码获取 SVG 图标
|
|
Widget _getWeatherSvgByCode(String code, {double size = 24.0}) {
|
|
// 检查 SVG 文件是否存在,如果不存在则使用默认图标
|
|
final svgPath = 'assets/weather/$code.svg';
|
|
|
|
return SvgPicture.asset(
|
|
svgPath,
|
|
width: size,
|
|
height: size,
|
|
fit: BoxFit.contain,
|
|
colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn),
|
|
// 如果 SVG 加载失败,显示默认图标
|
|
placeholderBuilder: (context) => Icon(
|
|
Icons.help_outline,
|
|
size: size,
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 根据天气类型获取背景渐变颜色
|
|
LinearGradient _getWeatherGradient(String weather, bool isDarkMode) {
|
|
final weatherType = weather.trim();
|
|
|
|
// 晴天 - 温暖的橙黄色
|
|
if (['晴', '少云', '晴间多云'].any((sunny) => weatherType.contains(sunny))) {
|
|
return LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: isDarkMode
|
|
? [Color(0xFFFF8A00), Color(0xFFE65100)] // 深色模式:深橙色
|
|
: [Color(0xFFFFB74D), Color(0xFFFF9800)], // 浅色模式:亮橙色
|
|
);
|
|
}
|
|
|
|
// 多云/阴天 - 灰蓝色
|
|
if (['多云', '阴'].any((cloudy) => weatherType.contains(cloudy))) {
|
|
return LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: isDarkMode
|
|
? [Color(0xFF455A64), Color(0xFF37474F)] // 深色模式:深灰蓝
|
|
: [Color(0xFF78909C), Color(0xFF607D8B)], // 浅色模式:灰蓝色
|
|
);
|
|
}
|
|
|
|
// 雨天 - 深蓝色
|
|
if (weatherType.contains('雨') ||
|
|
['阵雨', '雷阵雨', '小雨', '中雨', '大雨', '暴雨']
|
|
.any((rain) => weatherType.contains(rain))) {
|
|
return LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: isDarkMode
|
|
? [Color(0xFF1565C0), Color(0xFF0D47A1)] // 深色模式:深蓝色
|
|
: [Color(0xFF42A5F5), Color(0xFF1976D2)], // 浅色模式:亮蓝色
|
|
);
|
|
}
|
|
|
|
// 雪天 - 冷色调蓝白色
|
|
if (weatherType.contains('雪') ||
|
|
['阵雪', '小雪', '中雪', '大雪', '暴雪']
|
|
.any((snow) => weatherType.contains(snow))) {
|
|
return LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: isDarkMode
|
|
? [Color(0xFF0277BD), Color(0xFF01579B)] // 深色模式:深冷蓝
|
|
: [Color(0xFF81D4FA), Color(0xFF29B6F6)], // 浅色模式:浅蓝色
|
|
);
|
|
}
|
|
|
|
// 雾霾天 - 暗黄灰色
|
|
if (weatherType.contains('雾') ||
|
|
weatherType.contains('霾') ||
|
|
['雾', '浓雾', '霾', '中度霾', '重度霾'].any((fog) => weatherType.contains(fog))) {
|
|
return LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: isDarkMode
|
|
? [Color(0xFF5D4037), Color(0xFF3E2723)] // 深色模式:深棕色
|
|
: [Color(0xFF8D6E63), Color(0xFF6D4C41)], // 浅色模式:棕灰色
|
|
);
|
|
}
|
|
|
|
// 大风天 - 青绿色
|
|
if (weatherType.contains('风') ||
|
|
['大风', '强风', '疾风', '烈风'].any((wind) => weatherType.contains(wind))) {
|
|
return LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: isDarkMode
|
|
? [Color(0xFF00695C), Color(0xFF004D40)] // 深色模式:深青色
|
|
: [Color(0xFF26A69A), Color(0xFF00796B)], // 浅色模式:青绿色
|
|
);
|
|
}
|
|
|
|
// 默认 - 原来的蓝色
|
|
return LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: isDarkMode
|
|
? [Color(0xFF1E40AF), Color(0xFF1E3A8A)]
|
|
: [Color(0xFF60A5FA), Color(0xFF3B82F6)],
|
|
);
|
|
}
|
|
|