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.

406 lines
12 KiB

import 'package:common_utils/common_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
/// 构建天气内容卡片
Widget buildWeatherContent(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(
'查询天气情况:',
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: isDarkMode
? const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFF1E40AF), Color(0xFF1E3A8A)],
)
: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFF60A5FA), Color(0xFF3B82F6)],
),
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["date"]}",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
SizedBox(height: 4),
Text(
now["dayweather"],
style: TextStyle(
fontSize: 14.sp,
color: Colors.white.withValues(alpha: 0.9),
),
),
SizedBox(height: 8.h),
Row(
children: [
Text(
"${now["daytemp"]}°",
style: TextStyle(
fontSize: 36.sp,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
SizedBox(width: 8.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'最高: ${now["daytemp"]}°',
style: TextStyle(
fontSize: 12.sp,
color: Colors.white.withValues(alpha: 0.9),
),
),
Text(
'最低: ${now["nighttemp"]}°',
style: TextStyle(
fontSize: 12.sp,
color: Colors.white.withValues(alpha: 0.9),
),
),
],
),
],
),
],
),
Icon(
getWeatherIcon(now['dayweather']),
size: 48.sp,
color: Colors.white,
),
],
),
SizedBox(height: 16.h),
Container(
height: 1,
color: Colors.white.withValues(alpha: isDarkMode ? 0.15 : 0.2),
),
SizedBox(height: 16.h),
Text(
'未来预报',
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]['date'];
DateTime? date = DateUtil.getDateTime(dateStr);
IconData weatherIcon =
getWeatherIcon(days[index]['dayweather']);
String weekday = DateUtil.getWeekday(date,
languageCode: "zh", short: true);
String temp = "${days[index]['daytemp']}°";
return _buildForecastDay(
weekday,
weatherIcon,
temp,
);
},
),
),
SizedBox(height: 16.h),
_buildWeatherTip(context, now, isDarkMode),
],
),
),
],
);
}
/// 构建智能天气提示
Widget _buildWeatherTip(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['dayweather'] as String? ?? '').trim();
final dayTemp = int.tryParse(now['daytemp']?.toString() ?? '0') ?? 0;
final nightTemp = int.tryParse(now['nighttemp']?.toString() ?? '0') ?? 0;
// 高温提示 (>= 35°C)
if (dayTemp >= 35) {
return const WeatherTip(
icon: Icons.local_fire_department,
message: '今日高温炎热,请注意防暑降温,多喝水避免中暑',
);
}
// 低温提示 (<= 5°C)
if (dayTemp <= 5) {
return const WeatherTip(
icon: Icons.ac_unit,
message: '今日气温较低,外出请注意保暖,小心感冒',
);
}
// 雨天提示
if (dayWeather.contains('雨') || ['阵雨', '雷阵雨', '小雨', '中雨', '大雨', '暴雨'].any((rain) => dayWeather.contains(rain))) {
return const WeatherTip(
icon: Icons.umbrella,
message: '今日有雨,外出请携带雨具,注意出行安全',
);
}
// 雪天提示
if (dayWeather.contains('雪') || ['阵雪', '小雪', '中雪', '大雪', '暴雪'].any((snow) => dayWeather.contains(snow))) {
return const WeatherTip(
icon: Icons.ac_unit,
message: '今日有雪,路面湿滑请注意安全,做好保暖措施',
);
}
// 雾霾提示
if (dayWeather.contains('雾') || dayWeather.contains('霾') || ['雾', '浓雾', '霾', '中度霾', '重度霾'].any((fog) => dayWeather.contains(fog))) {
return const WeatherTip(
icon: Icons.blur_on,
message: '今日有雾霾,能见度较低,外出请佩戴口罩',
);
}
// 大风提示
if (dayWeather.contains('风') || ['大风', '强风', '疾风', '烈风'].any((wind) => dayWeather.contains(wind))) {
return const WeatherTip(
icon: Icons.air,
message: '今日风力较大,外出请注意安全,固定好物品',
);
}
// 晴天紫外线提示 (温度 >= 25°C)
if (['晴', '少云', '晴间多云'].any((sunny) => dayWeather.contains(sunny)) && dayTemp >= 25) {
return const WeatherTip(
icon: Icons.wb_sunny,
message: '今日阳光充足紫外线较强,外出请做好防晒',
);
}
// 温差大提示 (>= 10°C)
if ((dayTemp - nightTemp).abs() >= 10) {
return const WeatherTip(
icon: Icons.thermostat,
message: '今日昼夜温差较大,请适时增减衣物',
);
}
// 默认舒适天气提示
return const WeatherTip(
icon: Icons.wb_sunny_outlined,
message: '今日天气适宜,是外出活动的好时光',
);
}
/// 构建单日天气预报
Widget _buildForecastDay(String day, IconData 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(
icon,
size: 20.sp,
color: Colors.white,
),
SizedBox(height: 4.h),
Text(
temp,
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
],
);
}
/// 获取天气图标
IconData getWeatherIcon(String description) {
description = description.trim();
if (["晴", "少云", "晴间多云"].contains(description)) {
return Icons.wb_sunny;
}
if (["多云", "阴"].contains(description)) {
return Icons.cloud;
}
if (description.contains("雾") ||
["雾", "浓雾", "强浓雾", "轻雾", "大雾", "特强浓雾", "霾", "中度霾", "重度霾", "严重霾"]
.contains(description)) {
return Icons.blur_on;
}
if (description.contains("风") ||
[
"有风",
"平静",
"微风",
"和风",
"清风",
"强风/劲风",
"疾风",
"大风",
"烈风",
"风暴",
"狂爆风",
"飓风",
"热带风暴"
].contains(description)) {
return Icons.air;
}
if (description.contains("雨") ||
[
"阵雨",
"雷阵雨",
"雷阵雨并伴有冰雹",
"小雨",
"中雨",
"大雨",
"暴雨",
"大暴雨",
"特大暴雨",
"强阵雨",
"强雷阵雨",
"极端降雨",
"毛毛雨/细雨",
"雨",
"小雨-中雨",
"中雨-大雨",
"大雨-暴雨",
"暴雨-大暴雨",
"大暴雨-特大暴雨"
].contains(description)) {
return Icons.grain; // 或使用 community icon 包:weather_icons 中的 rain
}
if (description.contains("雪") ||
[
"雨雪天气",
"雨夹雪",
"阵雨夹雪",
"冻雨",
"雪",
"阵雪",
"小雪",
"中雪",
"大雪",
"暴雪",
"小雪-中雪",
"中雪-大雪",
"大雪-暴雪"
].contains(description)) {
return Icons.ac_unit;
}
if (["浮尘", "扬沙", "沙尘暴", "强沙尘暴"].contains(description)) {
return Icons.filter_drama;
}
if (["龙卷风"].contains(description)) {
return Icons.waves;
}
if (["热"].contains(description)) {
return Icons.local_fire_department;
}
if (["冷"].contains(description)) {
return Icons.ac_unit;
}
// 默认或未知天气
return Icons.help_outline;
}