import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../constants.dart'; import '../models/city_model.dart'; import '../providers/city_provider.dart'; import '../providers.dart'; class CityManagementScreen extends ConsumerStatefulWidget { const CityManagementScreen({super.key}); @override ConsumerState createState() => _CityManagementScreenState(); } class _CityManagementScreenState extends ConsumerState { final TextEditingController _searchController = TextEditingController(); bool _isSearching = false; @override void dispose() { _searchController.dispose(); super.dispose(); } Future _addCity(String cityQuery) async { if (cityQuery.trim().isEmpty) { _showSnackBar('Please enter a city name'); return; } try { // 先尝试获取天气数据以验证城市是否存在 final repository = ref.read(weatherRepositoryProvider); final weather = await repository.fetchWeather(cityQuery.trim()); // 创建城市模型 final city = CityModel( name: weather.location.name, region: weather.location.region, country: weather.location.country, query: cityQuery.trim(), ); // 添加到列表 final success = await ref.read(cityListProvider.notifier).addCity(city); if (success) { _showSnackBar('City added successfully'); _searchController.clear(); setState(() { _isSearching = false; }); } else { _showSnackBar('City already exists'); } } catch (e) { _showSnackBar('Failed to add city: ${e.toString()}'); } } Future _removeCity(String query) async { final cities = ref.read(cityListProvider); if (cities.length <= 1) { _showSnackBar('At least one city must remain'); return; } final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Delete City'), content: const Text('Are you sure you want to remove this city?'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Cancel'), ), TextButton( onPressed: () => Navigator.pop(context, true), child: const Text('Delete', style: TextStyle(color: Colors.red)), ), ], ), ); if (confirmed == true) { await ref.read(cityListProvider.notifier).removeCity(query); _showSnackBar('City removed'); } } Future _switchCity(String query) async { await ref.read(weatherProvider.notifier).switchCity(query); if (mounted) { Navigator.pop(context); _showSnackBar('City switched'); } } void _showSnackBar(String message) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(message), duration: const Duration(seconds: 2)), ); } } @override Widget build(BuildContext context) { final cities = ref.watch(cityListProvider); final currentCityAsync = ref.watch(currentCityProvider); return Scaffold( appBar: AppBar( title: const Text('Manage Cities'), actions: [ IconButton( icon: Icon(_isSearching ? Icons.close : Icons.add), onPressed: () { setState(() { _isSearching = !_isSearching; if (!_isSearching) { _searchController.clear(); } }); }, ), ], ), body: Column( children: [ if (_isSearching) Container( padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( color: kCardBackground, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Row( children: [ Expanded( child: TextField( controller: _searchController, decoration: InputDecoration( hintText: 'Enter city name (e.g., London, Beijing)', hintStyle: TextStyle( color: Colors.white.withOpacity(0.7), ), filled: true, fillColor: Colors.black.withOpacity(0.3), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 12, ), ), style: const TextStyle(color: Colors.white), onSubmitted: _addCity, ), ), const SizedBox(width: 8), IconButton( icon: const Icon(Icons.search), onPressed: () => _addCity(_searchController.text), ), ], ), ), Expanded( child: cities.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.location_city, size: 64, color: Colors.white.withOpacity(0.5), ), const SizedBox(height: 16), Text( 'No cities added', style: TextStyle( color: Colors.white.withOpacity(0.7), fontSize: 18, ), ), const SizedBox(height: 8), Text( 'Tap + to add a city', style: TextStyle( color: Colors.white.withOpacity(0.5), fontSize: 14, ), ), ], ), ) : ListView.builder( padding: const EdgeInsets.all(16.0), itemCount: cities.length, itemBuilder: (context, index) { final city = cities[index]; return currentCityAsync.when( data: (currentQuery) => _CityCard( city: city, isCurrent: city.query == currentQuery, onTap: () => _switchCity(city.query), onDelete: cities.length > 1 ? () => _removeCity(city.query) : null, ), loading: () => _CityCard( city: city, isCurrent: city.isDefault, onTap: () => _switchCity(city.query), onDelete: cities.length > 1 ? () => _removeCity(city.query) : null, ), error: (_, __) => _CityCard( city: city, isCurrent: city.isDefault, onTap: () => _switchCity(city.query), onDelete: cities.length > 1 ? () => _removeCity(city.query) : null, ), ); }, ), ), ], ), ); } } class _CityCard extends StatelessWidget { final CityModel city; final bool isCurrent; final VoidCallback onTap; final VoidCallback? onDelete; const _CityCard({ required this.city, required this.isCurrent, required this.onTap, this.onDelete, }); @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.only(bottom: 12.0), decoration: BoxDecoration( color: isCurrent ? Colors.blue.withOpacity(0.3) : kCardBackground, borderRadius: kCardBorderRadius, border: isCurrent ? Border.all(color: Colors.blueAccent, width: 2) : null, ), child: ListTile( leading: CircleAvatar( backgroundColor: isCurrent ? Colors.blueAccent : Colors.white.withOpacity(0.2), child: Icon( isCurrent ? Icons.location_on : Icons.location_city, color: Colors.white, ), ), title: Text( city.name, style: TextStyle( fontWeight: isCurrent ? FontWeight.bold : FontWeight.normal, color: Colors.white, ), ), subtitle: Text( city.displayName, style: TextStyle(color: Colors.white.withOpacity(0.7), fontSize: 12), ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ if (isCurrent) Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Colors.blueAccent, borderRadius: BorderRadius.circular(12), ), child: const Text( 'Current', style: TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold, ), ), ), if (onDelete != null) ...[ const SizedBox(width: 8), IconButton( icon: const Icon(Icons.delete_outline, color: Colors.red), onPressed: onDelete, tooltip: 'Delete city', ), ], ], ), onTap: onTap, ), ); } }