first commit

This commit is contained in:
Lucas Saburido
2026-05-13 16:26:45 +01:00
commit cabf2025cd
252 changed files with 13524 additions and 0 deletions

View File

@@ -0,0 +1,236 @@
import 'dart:typed_data';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../core/theme/app_colors.dart';
import '../../../../core/widgets/riotz_scaffold.dart';
import '../../../../core/widgets/riotz_button.dart';
import '../providers/music_providers.dart';
import '../widgets/track_card.dart';
class MusicPage extends ConsumerStatefulWidget {
const MusicPage({super.key});
@override
ConsumerState<MusicPage> createState() => _MusicPageState();
}
class _MusicPageState extends ConsumerState<MusicPage> {
final _titleController = TextEditingController();
final _genreController = TextEditingController();
Uint8List? _selectedMp3Bytes;
String? _selectedFileName;
String _selectedExtension = 'mp3';
@override
void dispose() {
_titleController.dispose();
_genreController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final tracksAsync = ref.watch(trackListProvider);
final uploadState = ref.watch(musicControllerProvider);
ref.listen(musicControllerProvider, (_, next) {
next.whenOrNull(
data: (_) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
backgroundColor: AppColors.success,
content: Text('SONIC CHAOS UPLOADED.', style: TextStyle(color: AppColors.black, fontWeight: FontWeight.bold)),
),
);
},
error: (error, _) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: AppColors.bloodRed,
content: Text(error.toString().toUpperCase()),
),
);
},
);
});
return RiotzScaffold(
appBar: AppBar(
title: const Text('RIOTZ // AUDIO'),
),
body: ListView(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 24),
children: [
Text(
'UPLOAD TRANSMISSION',
style: theme.textTheme.labelLarge?.copyWith(color: AppColors.neonRed),
),
const SizedBox(height: 16),
_UploadComposer(
titleController: _titleController,
genreController: _genreController,
selectedFileName: _selectedFileName,
isBusy: uploadState.isLoading,
onPickMp3: uploadState.isLoading ? null : _pickMp3,
onUpload: uploadState.isLoading ? null : _upload,
),
const SizedBox(height: 48),
Text(
'LATEST SOUNDS',
style: theme.textTheme.labelLarge?.copyWith(color: AppColors.neonRed),
),
const SizedBox(height: 16),
tracksAsync.when(
loading: () => const Center(child: CircularProgressIndicator(color: AppColors.neonRed)),
error: (error, _) => Center(child: Text('SIGNAL LOST: $error')),
data: (tracks) {
if (tracks.isEmpty) {
return const Center(
child: Padding(
padding: EdgeInsets.all(32),
child: Text('NO TRACKS FOUND. BROADCAST YOUR SOUND.'),
),
);
}
return Column(
children: tracks.map((track) {
return TrackCard(track: track);
}).toList(),
);
},
),
],
),
);
}
Future<void> _pickMp3() async {
final result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: const ['mp3'],
withData: true,
);
if (result == null || result.files.isEmpty) return;
final file = result.files.first;
if (file.bytes == null) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('UNREADABLE FILE.')),
);
return;
}
setState(() {
_selectedMp3Bytes = file.bytes;
_selectedFileName = file.name.toUpperCase();
_selectedExtension = file.name.split('.').last.toLowerCase();
});
}
Future<void> _upload() async {
final bytes = _selectedMp3Bytes;
if (bytes == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('SELECT AN MP3 FIRST.')),
);
return;
}
await ref.read(musicControllerProvider.notifier).uploadTrack(
bytes: bytes,
title: _titleController.text,
genreTag: _genreController.text,
extension: _selectedExtension,
);
if (!mounted) return;
setState(() {
_selectedMp3Bytes = null;
_selectedFileName = null;
_titleController.clear();
_genreController.clear();
});
}
}
class _UploadComposer extends StatelessWidget {
const _UploadComposer({
required this.titleController,
required this.genreController,
required this.selectedFileName,
required this.isBusy,
required this.onPickMp3,
required this.onUpload,
});
final TextEditingController titleController;
final TextEditingController genreController;
final String? selectedFileName;
final bool isBusy;
final VoidCallback? onPickMp3;
final VoidCallback? onUpload;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: AppColors.surface,
border: Border.all(color: AppColors.border),
),
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: titleController,
decoration: const InputDecoration(
hintText: 'TRACK TITLE',
),
),
const SizedBox(height: 16),
TextField(
controller: genreController,
decoration: const InputDecoration(
hintText: 'GENRE (RAGE, PUNK, PHONK)',
),
),
const SizedBox(height: 24),
if (selectedFileName != null)
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(
'READY: $selectedFileName',
style: const TextStyle(color: AppColors.neonRed, fontWeight: FontWeight.bold, fontSize: 10),
),
),
Row(
children: [
Expanded(
child: RiotzButton(
label: 'SELECT FILE',
style: RiotzButtonStyle.outline,
onPressed: onPickMp3,
),
),
const SizedBox(width: 12),
Expanded(
child: RiotzButton(
label: 'UPLOAD',
isLoading: isBusy,
onPressed: onUpload,
),
),
],
),
],
),
);
}
}