import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; import 'package:flutter_animate/flutter_animate.dart'; import '../providers/event_provider.dart'; import '../theme.dart'; import '../l10n/app_localizations.dart'; class EventPublishPage extends ConsumerStatefulWidget { const EventPublishPage({super.key}); @override ConsumerState createState() => _EventPublishPageState(); } class _EventPublishPageState extends ConsumerState { final _entityController = TextEditingController(); final _descController = TextEditingController(); final _formKey = GlobalKey(); final List _selectedImages = []; DateTime? _publishTime; DateTime? _effectiveTime; DateTime? _endTime; bool _popupReminder = false; @override void dispose() { _entityController.dispose(); _descController.dispose(); super.dispose(); } void _setDefaultTimes() { final now = DateTime.now(); setState(() { _publishTime = now; _effectiveTime = now.add(const Duration(hours: 1)); _endTime = now.add(const Duration(days: 7)); }); } Future _pickImage() async { final picker = ImagePicker(); final picked = await picker.pickImage(source: ImageSource.gallery); if (picked != null) { setState(() { _selectedImages.add(File(picked.path)); }); } } Future _selectDateTime(BuildContext context, bool isPublish) async { final date = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2024), lastDate: DateTime(2030), builder: (context, child) { return Theme( data: Theme.of(context).copyWith( colorScheme: const ColorScheme.light(primary: AppColors.primary), ), child: child!, ); }, ); if (date == null) return; if (!context.mounted) return; final time = await showTimePicker( context: context, initialTime: TimeOfDay.now(), builder: (context, child) { return Theme( data: Theme.of(context).copyWith( colorScheme: const ColorScheme.light(primary: AppColors.primary), ), child: child!, ); }, ); if (time == null) return; setState(() { final dt = DateTime(date.year, date.month, date.day, time.hour, time.minute); if (isPublish) { _publishTime = dt; } else { _endTime = dt; } }); } Future _selectEffectiveTime(BuildContext context) async { final date = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(2024), lastDate: DateTime(2030), builder: (context, child) { return Theme( data: Theme.of(context).copyWith( colorScheme: const ColorScheme.light(primary: AppColors.primary), ), child: child!, ); }, ); if (date == null) return; if (!context.mounted) return; final time = await showTimePicker( context: context, initialTime: TimeOfDay.now(), builder: (context, child) { return Theme( data: Theme.of(context).copyWith( colorScheme: const ColorScheme.light(primary: AppColors.primary), ), child: child!, ); }, ); if (time == null) return; setState(() { _effectiveTime = DateTime(date.year, date.month, date.day, time.hour, time.minute); }); } void _publish() { if (!_formKey.currentState!.validate()) return; ref.read(eventProvider.notifier).publishEvent({ 'entityName': _entityController.text.trim(), 'description': _descController.text.trim(), 'images': _selectedImages.map((f) => f.path).toList(), 'publishTime': _publishTime?.toIso8601String(), 'effectiveTime': _effectiveTime?.toIso8601String(), 'endTime': _endTime?.toIso8601String(), 'popupReminder': _popupReminder, }).then((_) { final state = ref.read(eventProvider); if (state.publishSuccess) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(AppLocalizations.of(context)!.publishSuccess), backgroundColor: AppColors.success, behavior: SnackBarBehavior.floating, ), ); ref.read(eventProvider.notifier).clearPublishSuccess(); context.pop(); } }); } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final eventState = ref.watch(eventProvider); return Scaffold( backgroundColor: AppColors.background, appBar: AppBar( title: Text(l10n.publishEvent), actions: [ TextButton( onPressed: () => context.push('/event/list'), child: Text(l10n.eventRecords), ), ], ), body: Form( key: _formKey, child: ListView( padding: const EdgeInsets.all(20), children: [ _buildSectionTitle(l10n.basicInfo), const SizedBox(height: 12), _buildTextField( controller: _entityController, label: l10n.entityName, hint: l10n.entityNameHint, icon: Icons.business_outlined, validator: (v) => v == null || v.isEmpty ? l10n.entityName : null, ), const SizedBox(height: 16), _buildTextField( controller: _descController, label: l10n.eventDesc, hint: l10n.eventDescHint, maxLines: 4, validator: (v) => v == null || v.isEmpty ? l10n.eventDesc : null, ), const SizedBox(height: 24), _buildSectionTitle(l10n.eventImages), const SizedBox(height: 12), _buildImagePicker(l10n), const SizedBox(height: 24), _buildSectionTitle(l10n.timeSettings), const SizedBox(height: 12), _buildTimeSelector( label: l10n.publishTime, value: _publishTime, icon: Icons.access_time, onTap: () => _selectDateTime(context, true), l10n: l10n, ), const SizedBox(height: 12), _buildTimeSelector( label: l10n.effectiveTime, value: _effectiveTime, icon: Icons.play_circle_outline, onTap: () => _selectEffectiveTime(context), l10n: l10n, ), const SizedBox(height: 12), _buildTimeSelector( label: l10n.endTime, value: _endTime, icon: Icons.stop_circle_outlined, onTap: () => _selectDateTime(context, false), l10n: l10n, ), const SizedBox(height: 12), Align( alignment: Alignment.centerRight, child: TextButton.icon( onPressed: _setDefaultTimes, icon: const Icon(Icons.auto_fix_high, size: 18), label: Text(l10n.setDefaultTime), ), ), const SizedBox(height: 16), _buildSwitchTile(l10n), const SizedBox(height: 32), SizedBox( width: double.infinity, height: 52, child: ElevatedButton( onPressed: eventState.isLoading ? null : _publish, style: ElevatedButton.styleFrom( backgroundColor: AppColors.primary, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), ), ), child: eventState.isLoading ? const SizedBox( width: 24, height: 24, child: CircularProgressIndicator( strokeWidth: 2.5, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : Text( l10n.publishEvent, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600), ), ), ), ], ), ), ); } Widget _buildSectionTitle(String title) { return Text( title, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.textPrimary, ), ); } Widget _buildTextField({ required TextEditingController controller, required String label, required String hint, IconData? icon, int maxLines = 1, String? Function(String?)? validator, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: AppColors.textPrimary, ), ), const SizedBox(height: 8), TextFormField( controller: controller, maxLines: maxLines, validator: validator, decoration: InputDecoration( hintText: hint, prefixIcon: icon != null ? Icon(icon, color: AppColors.textTertiary, size: 20) : null, filled: true, fillColor: AppColors.surface, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: AppColors.primary, width: 1.5), ), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), ), ), ], ); } Widget _buildImagePicker(AppLocalizations l10n) { return Wrap( spacing: 12, runSpacing: 12, children: [ ..._selectedImages.asMap().entries.map((entry) { return Stack( children: [ ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.file( entry.value, width: 100, height: 100, fit: BoxFit.cover, ), ), Positioned( top: 4, right: 4, child: GestureDetector( onTap: () { setState(() { _selectedImages.removeAt(entry.key); }); }, child: Container( padding: const EdgeInsets.all(4), decoration: const BoxDecoration( color: Colors.black54, shape: BoxShape.circle, ), child: const Icon(Icons.close, size: 14, color: Colors.white), ), ), ), ], ); }), GestureDetector( onTap: _pickImage, child: Container( width: 100, height: 100, decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(12), border: Border.all(color: AppColors.divider, width: 1.5), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.add_photo_alternate_outlined, color: AppColors.textTertiary, size: 28), const SizedBox(height: 4), Text(l10n.addImage, style: TextStyle(fontSize: 12, color: AppColors.textTertiary)), ], ), ), ), ], ); } Widget _buildTimeSelector({ required String label, required DateTime? value, required IconData icon, required VoidCallback onTap, required AppLocalizations l10n, }) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Icon(icon, color: AppColors.primary, size: 20), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: TextStyle(fontSize: 12, color: AppColors.textSecondary), ), const SizedBox(height: 2), Text( value != null ? '${value.year}-${value.month.toString().padLeft(2, '0')}-${value.day.toString().padLeft(2, '0')} ${value.hour.toString().padLeft(2, '0')}:${value.minute.toString().padLeft(2, '0')}' : '${l10n.selectTime}$label', style: TextStyle( fontSize: 15, color: value != null ? AppColors.textPrimary : AppColors.textTertiary, fontWeight: FontWeight.w500, ), ), ], ), ), Icon(Icons.chevron_right, color: AppColors.textTertiary), ], ), ), ); } Widget _buildSwitchTile(AppLocalizations l10n) { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), decoration: BoxDecoration( color: AppColors.surface, borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: AppColors.warning.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: const Icon(Icons.notifications_active_outlined, color: AppColors.warning, size: 20), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( l10n.popupReminder, style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500, color: AppColors.textPrimary), ), Text( l10n.popupReminderDesc, style: TextStyle(fontSize: 12, color: AppColors.textSecondary), ), ], ), ), Switch( value: _popupReminder, onChanged: (v) => setState(() => _popupReminder = v), activeColor: AppColors.primary, ), ], ), ); } }