Commit 54232159 by Rafa Castillo Passols

Merge de actualizacionLista

parents a1d6afbf 38e38526
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';
import 'archivo.dart'; import 'archivo.dart';
import 'convertible.dart'; import 'convertible.dart';
import 'elemento_seleccionable.dart'; import 'elemento_seleccionable.dart';
...@@ -7,41 +8,85 @@ import 'formato.dart'; ...@@ -7,41 +8,85 @@ import 'formato.dart';
class Carpeta extends ElementoSeleccionable{ class Carpeta extends ElementoSeleccionable{
final Directory _directory; final Directory _directory;
final bool _incluirSubcarpetas = false; bool _incluirSubcarpetas = false;
final List<InfoFormato> _formatos; bool _open = false;
late final List<InfoFormato> _formatos;
Directory get directory => _directory; Directory get directory => _directory;
List<InfoFormato> get formatos => List.unmodifiable(_formatos); List<InfoFormato> get formatos => List.unmodifiable(_formatos);
bool get isOpen => _open;
bool get incluyeSubcarpetas => _incluirSubcarpetas;
Carpeta({required super.id, required Directory directory}): Carpeta({required super.id, required Directory directory}):
_directory = directory, _formatos = [], _directory = directory,
super(nombre: directory.path.split('/').last, icon: const Icon(Icons.folder_outlined))
{
_formatos = [];
final archivos = directory.listSync(recursive: true, followLinks: false);
for(var a in archivos) {
Formato? f = Formato.fromExtension(a.path.split(".").last);
if(f != null){
_formatos.add(InfoFormato(
formato: f,
nombreCarpeta: directory.path.split('/').last,
subCarpeta: false));
}
}
}
Carpeta.fromList({required super.id, required Directory directory, required List<InfoFormato> formatos}):
_directory = directory, _formatos = formatos,
super(nombre: directory.path.split('/').last, icon: const Icon(Icons.folder_outlined)); super(nombre: directory.path.split('/').last, icon: const Icon(Icons.folder_outlined));
InfoFormato? getInfoFormato({required Formato formato}){
for(InfoFormato i in _formatos){
if(i.formatoOriginal == formato){
return i;
}
}
return null;
}
void setFormatoDestino(Formato original, Formato? destino){
final formatosAlt = formatos.toList();
for(InfoFormato i in formatosAlt){
if(i.formatoOriginal == original){
i.formatoDestino = destino;
}
}
}
void pressOpenClose(){
_open = !_open;
}
void pressIncluirSubcarpetas(){
_incluirSubcarpetas = !_incluirSubcarpetas;
}
void pressAltSeleccionado(int index){
_formatos[index].seleccionado = !_formatos[index].seleccionado;
}
} }
class InfoFormato { class InfoFormato {
final bool _subcarpeta; final bool _subcarpeta;
bool _seleccionado; bool seleccionado;
final Convertible _conversion; final Convertible _conversion;
final List<File> _archivos;
bool get subcarpeta => _subcarpeta; bool get subcarpeta => _subcarpeta;
bool get seleccionado => _seleccionado;
Convertible get conversion => _conversion; Convertible get conversion => _conversion;
Formato get formatoOriginal => _conversion.formatoOriginal; Formato get formatoOriginal => _conversion.formatoOriginal;
Formato? get formatoDestino => _conversion.formatoDestino; Formato? get formatoDestino => _conversion.formatoDestino;
List<Archivo> get archivos => List.unmodifiable(_archivos);
set seleccionado(bool value) => _seleccionado = value; set formatoDestino(Formato? destino) => _conversion.formatoDestino = destino;
set formato(Formato? destino) => _conversion.formatoDestino = destino;
InfoFormato({required Formato formato, InfoFormato({required Formato formato,
required String nombreCarpeta, required String nombreCarpeta,
required bool subCarpeta, required bool subCarpeta,
bool? seleccionado, bool? seleccionado}):
required List<File> archivos}): _subcarpeta = subCarpeta, seleccionado = seleccionado ?? !subCarpeta,
_subcarpeta = subCarpeta, _seleccionado = seleccionado ?? !subCarpeta, _archivos = archivos, _conversion = Convertible(id: const Uuid().v1(), nombre: '$nombreCarpeta > ${formato.name}',
_conversion = Convertible(id: "null", nombre: '$nombreCarpeta > ${formato.name}',
icon: Icon(Icons.find_in_page_outlined), formatoOriginal: formato); icon: Icon(Icons.find_in_page_outlined), formatoOriginal: formato);
void convertir(){ void convertir(){
...@@ -49,4 +94,10 @@ class InfoFormato { ...@@ -49,4 +94,10 @@ class InfoFormato {
// TODO: <implement> // TODO: <implement>
} }
} }
@override
bool operator ==(Object other) {
if(other is InfoFormato) return this.formatoOriginal == other.formatoOriginal;
return super == other;
}
} }
\ No newline at end of file
...@@ -88,12 +88,13 @@ enum Formato { ...@@ -88,12 +88,13 @@ enum Formato {
} }
enum TipoMultimedia { enum TipoMultimedia {
video(Icon(Icons.movie_creation_outlined)), video("Vídeo", Icon(Icons.movie_creation_outlined)),
audio(Icon(Icons.music_note_outlined)), audio("Audio", Icon(Icons.music_note_outlined)),
imagen(Icon(Icons.image_outlined)); imagen("Imagen", Icon(Icons.image_outlined));
final String nombre;
final Icon icono; final Icon icono;
const TipoMultimedia(this.icono); const TipoMultimedia(this.nombre, this.icono);
} }
enum Clasificacion { enum Clasificacion {
......
...@@ -4,6 +4,7 @@ import 'package:uuid/uuid.dart'; ...@@ -4,6 +4,7 @@ import 'package:uuid/uuid.dart';
import 'carpeta.dart'; import 'carpeta.dart';
import 'elemento_seleccionable.dart'; import 'elemento_seleccionable.dart';
import 'archivo.dart'; import 'archivo.dart';
import 'formato.dart';
class ListaSeleccionables extends ChangeNotifier { class ListaSeleccionables extends ChangeNotifier {
final _seleccionables = <ElementoSeleccionable>[]; final _seleccionables = <ElementoSeleccionable>[];
...@@ -14,15 +15,33 @@ class ListaSeleccionables extends ChangeNotifier { ...@@ -14,15 +15,33 @@ class ListaSeleccionables extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void actualizaSeleccionable(int indice, ElementoSeleccionable elemento){
_seleccionables[indice] = elemento;
notifyListeners();
}
void addArchivo(File file) { void addArchivo(File file) {
_seleccionables.add(Archivo(id: "Archivo", _seleccionables.add(Archivo(id: const Uuid().v1(),
file: file)); file: file));
notifyListeners(); notifyListeners();
} }
void addCarpeta(Directory directory){ void addCarpeta(Directory directory){
_seleccionables.add(Carpeta(id: const Uuid().v1(), final newCarpeta = Carpeta(
directory: directory)); id: const Uuid().v1(),
directory: directory
);
if(newCarpeta.formatos.isNotEmpty){
_seleccionables.add(Carpeta(
id: const Uuid().v1(),
directory: directory
));
notifyListeners();
}
}
void reinsertar(int index, ElementoSeleccionable element){
_seleccionables.insert(index, element);
notifyListeners(); notifyListeners();
} }
} }
\ No newline at end of file
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:prueba_multimedia/modelo/modelo.dart'; import 'package:prueba_multimedia/modelo/modelo.dart';
import 'package:prueba_multimedia/paginas/paginas.dart'; import 'package:prueba_multimedia/paginas/paginas.dart';
class PaginaConfiguracion extends StatefulWidget { class PaginaConfiguracion extends StatefulWidget {
final ListaSeleccionables _lista;
final int _indice;
final ElementoSeleccionable _elementoAsociado; final ElementoSeleccionable _elementoAsociado;
final Carpeta? _carpeta;
const PaginaConfiguracion({ const PaginaConfiguracion({
super.key, super.key,
required elementoAsociado required ListaSeleccionables lista,
}): _elementoAsociado = elementoAsociado; required int indice,
required ElementoSeleccionable elemento,
Carpeta? carpeta
}): _lista = lista, _indice = indice, _elementoAsociado = elemento,
_carpeta = carpeta;
@override @override
State<PaginaConfiguracion> createState() => _PaginaConfiguracionState(); State<PaginaConfiguracion> createState() => _PaginaConfiguracionState();
...@@ -23,10 +31,12 @@ class _PaginaConfiguracionState extends State<PaginaConfiguracion> { ...@@ -23,10 +31,12 @@ class _PaginaConfiguracionState extends State<PaginaConfiguracion> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: RichText( title: RichText(
maxLines: 1,
overflow: TextOverflow.ellipsis,
text: TextSpan( text: TextSpan(
children: [ children: [
WidgetSpan( WidgetSpan(
child: Icon(_getIcon()) child: _getIcon()
), ),
TextSpan( TextSpan(
text: _getShownName(), text: _getShownName(),
...@@ -36,19 +46,22 @@ class _PaginaConfiguracionState extends State<PaginaConfiguracion> { ...@@ -36,19 +46,22 @@ class _PaginaConfiguracionState extends State<PaginaConfiguracion> {
) )
), ),
), ),
body: _construirCuerpo(), body: _construirCuerpo(widget._lista),
bottomNavigationBar: _construirBarraNavegacion() bottomNavigationBar: _construirBarraNavegacion()
); );
} }
IconData _getIcon(){ Icon _getIcon(){
if(widget._elementoAsociado is Carpeta){ if(widget._elementoAsociado is Carpeta){
return Icons.folder; return Icon(Icons.folder);
} }
if(widget._elementoAsociado is Enlace){ if(widget._elementoAsociado is Enlace){
return Icons.link; return Icon(Icons.link);
} }
return Icons.insert_drive_file; if(widget._elementoAsociado is Convertible){
return (widget._elementoAsociado as Convertible).formatoOriginal.tipoMultimedia.icono;
}
return Icon(Icons.insert_drive_file);
} }
String _getShownName() { String _getShownName() {
...@@ -58,44 +71,51 @@ class _PaginaConfiguracionState extends State<PaginaConfiguracion> { ...@@ -58,44 +71,51 @@ class _PaginaConfiguracionState extends State<PaginaConfiguracion> {
return sb.toString(); return sb.toString();
} }
Widget _construirCuerpo() { Widget _construirCuerpo(ListaSeleccionables manager) {
if(widget._elementoAsociado is Convertible){ if(widget._elementoAsociado is Archivo){
final arch = widget._elementoAsociado as Convertible; final arch = widget._elementoAsociado as Archivo;
if(_categoriaActiva == 0){ if(_categoriaActiva == 0){
return PaginaConversion(formatoOriginal: arch.formatoOriginal); if(widget._carpeta != null){
return PaginaConversion.carpeta(
lista: manager,
indiceArchivo: widget._indice,
carpeta: widget._carpeta!,
infoFormato: widget._carpeta!.getInfoFormato(formato: arch.formatoOriginal)!,
formatoOriginal: arch.formatoOriginal
);
}
return PaginaConversion.convertible(
lista: manager,
indiceArchivo: widget._indice,
elemento: arch,
formatoOriginal: arch.formatoOriginal
);
} }
else if(_categoriaActiva == 2){ else if(_categoriaActiva == 2 || arch.formatoOriginal.tipoMultimedia != TipoMultimedia.video){
return PaginaMetadatos(formato: arch.formatoOriginal); return FutureBuilder(
future: arch.metadatos,
builder: (context, snapshot) {
return snapshot.hasData
? PaginaMetadatos(
metadatos: snapshot.data!,
formato: arch.formatoOriginal
)
: CircularProgressIndicator();
}
);
} }
else{ else{
if(arch.formatoOriginal.tipoMultimedia == TipoMultimedia.video){ return PaginaFotograma();
return PaginaFotograma();
}
// Página de metadatos
return PaginaMetadatos(formato: arch.formatoOriginal);
} }
} }
// Páginas de carpetas y formato de archivo para carpeta // Páginas de carpetas y formato de archivo para carpeta
// TODO: Carpeta de ejemplo, cargar carpetas de verdad // TODO: Carpeta de ejemplo, cargar carpetas de verdad
return PaginaConfiguracionCarpeta(formatosCarpeta: [ return PaginaConfiguracionCarpeta(
InfoFormato(formato: Formato.jpg, lista: manager,
nombreCarpeta: widget._elementoAsociado.nombre, carpeta: widget._elementoAsociado as Carpeta,
subCarpeta: false, indice: widget._indice
archivos: []), );
InfoFormato(formato: Formato.mp4,
nombreCarpeta: widget._elementoAsociado.nombre,
subCarpeta: false,
archivos: []),
InfoFormato(formato: Formato.tif,
nombreCarpeta: widget._elementoAsociado.nombre,
subCarpeta: true,
archivos: []),
InfoFormato(formato: Formato.png,
nombreCarpeta: widget._elementoAsociado.nombre,
subCarpeta: true,
archivos: [])
]);
} }
BottomNavigationBar? _construirBarraNavegacion(){ BottomNavigationBar? _construirBarraNavegacion(){
......
...@@ -3,10 +3,21 @@ import 'package:prueba_multimedia/modelo/modelo.dart'; ...@@ -3,10 +3,21 @@ import 'package:prueba_multimedia/modelo/modelo.dart';
import 'package:prueba_multimedia/paginas/paginas.dart'; import 'package:prueba_multimedia/paginas/paginas.dart';
class PaginaConfiguracionCarpeta extends StatefulWidget { class PaginaConfiguracionCarpeta extends StatefulWidget {
final ListaSeleccionables _lista;
final List<InfoFormato> _formatosCarpeta; final List<InfoFormato> _formatosCarpeta;
final int _indice;
const PaginaConfiguracionCarpeta({super.key, required List<InfoFormato> formatosCarpeta}): final Carpeta _carpeta;
_formatosCarpeta = formatosCarpeta;
PaginaConfiguracionCarpeta({
super.key,
required ListaSeleccionables lista,
required int indice,
required Carpeta carpeta
}):
_lista = lista,
_indice = indice,
_carpeta = carpeta,
_formatosCarpeta = carpeta.formatos;
@override @override
State<PaginaConfiguracionCarpeta> createState() => _PaginaConfiguracionCarpetaState(); State<PaginaConfiguracionCarpeta> createState() => _PaginaConfiguracionCarpetaState();
...@@ -21,7 +32,7 @@ class _PaginaConfiguracionCarpetaState extends State<PaginaConfiguracionCarpeta> ...@@ -21,7 +32,7 @@ class _PaginaConfiguracionCarpetaState extends State<PaginaConfiguracionCarpeta>
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_incluirSubcarpetas = false; _incluirSubcarpetas = widget._carpeta.incluyeSubcarpetas;
for(TipoMultimedia t in TipoMultimedia.values){ for(TipoMultimedia t in TipoMultimedia.values){
_formatos[t] = []; _formatos[t] = [];
...@@ -83,6 +94,8 @@ class _PaginaConfiguracionCarpetaState extends State<PaginaConfiguracionCarpeta> ...@@ -83,6 +94,8 @@ class _PaginaConfiguracionCarpetaState extends State<PaginaConfiguracionCarpeta>
onChanged: (bool? value) { onChanged: (bool? value) {
setState(() { setState(() {
_incluirSubcarpetas = value!; _incluirSubcarpetas = value!;
widget._carpeta.pressIncluirSubcarpetas();
widget._lista.actualizaSeleccionable(widget._indice, widget._carpeta);
}); });
}) })
], ],
...@@ -136,8 +149,10 @@ class _PaginaConfiguracionCarpetaState extends State<PaginaConfiguracionCarpeta> ...@@ -136,8 +149,10 @@ class _PaginaConfiguracionCarpetaState extends State<PaginaConfiguracionCarpeta>
for(InfoFormato i in widget._formatosCarpeta){ for(InfoFormato i in widget._formatosCarpeta){
if(_formatos[t]!.contains(i.formatoOriginal) && !(!_incluirSubcarpetas && i.subcarpeta)){ if(_formatos[t]!.contains(i.formatoOriginal) && !(!_incluirSubcarpetas && i.subcarpeta)){
_seleccionados[i.formatoOriginal] = _allOfType[t]!; _seleccionados[i.formatoOriginal] = _allOfType[t]!;
widget._carpeta.pressAltSeleccionado(widget._carpeta.formatos.indexOf(i));
} }
} }
widget._lista.actualizaSeleccionable(widget._indice, widget._carpeta);
}); });
}) })
], ],
...@@ -186,6 +201,8 @@ class _PaginaConfiguracionCarpetaState extends State<PaginaConfiguracionCarpeta> ...@@ -186,6 +201,8 @@ class _PaginaConfiguracionCarpetaState extends State<PaginaConfiguracionCarpeta>
_seleccionados[i.formatoOriginal] = value!; _seleccionados[i.formatoOriginal] = value!;
_allOfType[i.formatoOriginal.tipoMultimedia] = _allOfType[i.formatoOriginal.tipoMultimedia] =
_actualizarCheckboxTipo(i.formatoOriginal.tipoMultimedia); _actualizarCheckboxTipo(i.formatoOriginal.tipoMultimedia);
widget._carpeta.pressAltSeleccionado(widget._carpeta.formatos.indexOf(i));
widget._lista.actualizaSeleccionable(widget._indice, widget._carpeta);
}); });
}), }),
IconButton( IconButton(
...@@ -194,7 +211,12 @@ class _PaginaConfiguracionCarpetaState extends State<PaginaConfiguracionCarpeta> ...@@ -194,7 +211,12 @@ class _PaginaConfiguracionCarpetaState extends State<PaginaConfiguracionCarpeta>
() { () {
Navigator.push(context, Navigator.push(context,
MaterialPageRoute(builder: (context) { MaterialPageRoute(builder: (context) {
return PaginaConfiguracion(elementoAsociado: i.conversion); return PaginaConfiguracion(
indice: widget._indice,
elemento: i.conversion,
lista: widget._lista,
carpeta: widget._carpeta
);
} }
)); ));
}, },
......
...@@ -2,12 +2,39 @@ import 'package:flutter/material.dart'; ...@@ -2,12 +2,39 @@ import 'package:flutter/material.dart';
import 'package:prueba_multimedia/modelo/modelo.dart'; import 'package:prueba_multimedia/modelo/modelo.dart';
class PaginaConversion extends StatefulWidget { class PaginaConversion extends StatefulWidget {
final ListaSeleccionables _lista;
final int _indiceArchivo;
final Carpeta? _carpeta;
final InfoFormato? _infoFormato;
final Convertible? _archivo;
final Formato _formatoOriginal; final Formato _formatoOriginal;
const PaginaConversion({ const PaginaConversion.convertible({
super.key, super.key,
required formatoOriginal required Formato formatoOriginal,
}): _formatoOriginal = formatoOriginal; required int indiceArchivo,
required Convertible elemento,
required ListaSeleccionables lista
}): _formatoOriginal = formatoOriginal,
_indiceArchivo = indiceArchivo,
_archivo = elemento,
_carpeta = null,
_infoFormato = null,
_lista = lista;
const PaginaConversion.carpeta({
super.key,
required Formato formatoOriginal,
required int indiceArchivo,
required Carpeta carpeta,
required InfoFormato infoFormato,
required ListaSeleccionables lista
}): _formatoOriginal = formatoOriginal,
_indiceArchivo = indiceArchivo,
_archivo = null,
_carpeta = carpeta,
_infoFormato = infoFormato,
_lista = lista;
@override @override
State<PaginaConversion> createState() => _PaginaConversionState(); State<PaginaConversion> createState() => _PaginaConversionState();
...@@ -15,10 +42,8 @@ class PaginaConversion extends StatefulWidget { ...@@ -15,10 +42,8 @@ class PaginaConversion extends StatefulWidget {
class _PaginaConversionState extends State<PaginaConversion> class _PaginaConversionState extends State<PaginaConversion>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
// Podríamos hacer que en vez de poder haber nulos sea el mismo formato
// que el de origen de manera predeterminada ?
Formato? _formatoConvertido; Formato? _formatoConvertido;
Calidad calidadActual = Calidad.media; Calidad? _calidadActual = null;
bool _showProfiles = true; bool _showProfiles = true;
late final TabController _tabController; late final TabController _tabController;
...@@ -27,6 +52,12 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -27,6 +52,12 @@ class _PaginaConversionState extends State<PaginaConversion>
void initState() { void initState() {
super.initState(); super.initState();
_tabController = TabController(length: 3, vsync: this); _tabController = TabController(length: 3, vsync: this);
if(widget._carpeta != null){
_formatoConvertido = widget._infoFormato!.formatoDestino;
}
else{
_formatoConvertido = widget._archivo!.formatoDestino;
}
} }
@override @override
...@@ -43,11 +74,11 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -43,11 +74,11 @@ class _PaginaConversionState extends State<PaginaConversion>
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
_createOriginalFormatBox(), _createFormatBox(true),
const SizedBox(width: 10.0), const SizedBox(width: 10.0),
Icon(Icons.chevron_right), Icon(Icons.chevron_right),
const SizedBox(width: 10.0), const SizedBox(width: 10.0),
_createConversionFormatBox() _createFormatBox(false)
], ],
), ),
SizedBox(height: 20.0), SizedBox(height: 20.0),
...@@ -60,34 +91,21 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -60,34 +91,21 @@ class _PaginaConversionState extends State<PaginaConversion>
); );
} }
Widget _createOriginalFormatBox(){ Widget _createFormatBox(bool original){
final String nombreFormato = widget._formatoOriginal.name.toUpperCase();
return DecoratedBox(
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
),
borderRadius: BorderRadius.circular(15),
color: Colors.black26
),
child: Padding(
padding: const EdgeInsets.all(13.0),
child: Text(
nombreFormato,
style: Theme.of(context).textTheme.titleLarge,
textScaler: TextScaler.linear(1.3)
),
)
);
}
_createConversionFormatBox() {
final String nombreFormato; final String nombreFormato;
if(_formatoConvertido == null){ final Color fondo;
nombreFormato = ' '; if(original){
} else { nombreFormato = widget._formatoOriginal.name.toUpperCase();
nombreFormato = _formatoConvertido!.name.toUpperCase(); fondo = Colors.black26;
}
else{
if(_formatoConvertido == null){
nombreFormato = ' ';
fondo = Colors.white;
} else {
nombreFormato = _formatoConvertido!.name.toUpperCase();
fondo = Theme.of(context).colorScheme.inversePrimary;
}
} }
return DecoratedBox( return DecoratedBox(
...@@ -96,7 +114,7 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -96,7 +114,7 @@ class _PaginaConversionState extends State<PaginaConversion>
color: Colors.black, color: Colors.black,
), ),
borderRadius: BorderRadius.circular(15), borderRadius: BorderRadius.circular(15),
color: _formatoConvertido == null ? Colors.white : Colors.black12 color: fondo
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(13.0), padding: const EdgeInsets.all(13.0),
...@@ -109,22 +127,16 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -109,22 +127,16 @@ class _PaginaConversionState extends State<PaginaConversion>
); );
} }
// TODO: A QUÉ SE REFIERE LA CALIDAD ?
/*
Un formato puede tener una calidad predeterminada?
De que sirve cambiar la calidad de un formato?
Hay formatos lossless para los que no hay ajustes de calidad/compresión
*/
// TODO: IMPLEMENTAR SISTEMA CALIDAD FORMATOS
/// Comportamiento temporal: Mostrar todas las calidades /// Comportamiento temporal: Mostrar todas las calidades
Widget _createCalidadSelectionPanel() { Widget _createCalidadSelectionPanel() {
final listaChips = Calidad.values.map((elemento) { final listaChips = Calidad.values.map((elemento) {
return ChoiceChip( return ChoiceChip(
selected: elemento == calidadActual, selected: elemento == _calidadActual,
shape: StadiumBorder(), shape: StadiumBorder(),
label: Text(elemento.texto), label: Text(elemento.texto),
// TODO: CAMBIAR CALIDAD FORMATO // TODO: CAMBIAR CALIDAD FORMATO
onSelected: (selected) { setState((){calidadActual = elemento;}); }, onSelected: (_formatoConvertido == null)? null:
(selected) { setState((){_calidadActual = elemento;}); },
); );
}).toList(); }).toList();
...@@ -153,18 +165,37 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -153,18 +165,37 @@ class _PaginaConversionState extends State<PaginaConversion>
} }
} }
Widget _createDefaultConversionPanel() { Widget _createTabVideos(){
final tabs = <Widget>[];
final grids = <Widget>[];
final tipos = TipoMultimedia.values;
for (var element in tipos) {
if(widget._carpeta != null && element == TipoMultimedia.imagen) continue;
tabs.add(Tab(
icon: element.icono,
text: element.nombre,
));
grids.add(GridView.count(
crossAxisCount: 3,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
children: _createGridBotonesConversion(tipo: element),
));
}
return Material( return Material(
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
shape: BeveledRectangleBorder( shape: BeveledRectangleBorder(
side: BorderSide( side: BorderSide(
color: Colors.black, color: Colors.black,
width: 1 width: 1
), ),
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0), topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0) topRight: Radius.circular(20.0)
) )
), ),
child: Container( child: Container(
height: MediaQuery.of(context).size.height - 380, height: MediaQuery.of(context).size.height - 380,
...@@ -176,15 +207,19 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -176,15 +207,19 @@ class _PaginaConversionState extends State<PaginaConversion>
children: [ children: [
_createBotonesPerfilFormato(), _createBotonesPerfilFormato(),
SizedBox( SizedBox(
height: MediaQuery.of(context).size.height - 460, height: 68,
child: TabBar(
controller: _tabController,
tabs: tabs
),
),
SizedBox(
height: MediaQuery.of(context).size.height - 528,
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 16.0), padding: const EdgeInsets.only(top: 16.0),
child: GridView.count( child: TabBarView(
crossAxisCount: _showProfiles? 1 : 3, controller: _tabController,
childAspectRatio: _showProfiles ? 6.0 : 1.0, children: grids
crossAxisSpacing: 12,
mainAxisSpacing: 12,
children: _showProfiles? _createListPerfiles() : _createGridBotonesConversion()
), ),
), ),
), ),
...@@ -195,18 +230,18 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -195,18 +230,18 @@ class _PaginaConversionState extends State<PaginaConversion>
); );
} }
Widget _createTabVideos(){ Widget _createDefaultConversionPanel() {
return Material( return Material(
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
shape: BeveledRectangleBorder( shape: BeveledRectangleBorder(
side: BorderSide( side: BorderSide(
color: Colors.black, color: Colors.black,
width: 1 width: 1
), ),
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0), topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0) topRight: Radius.circular(20.0)
) )
), ),
child: Container( child: Container(
height: MediaQuery.of(context).size.height - 380, height: MediaQuery.of(context).size.height - 380,
...@@ -218,39 +253,15 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -218,39 +253,15 @@ class _PaginaConversionState extends State<PaginaConversion>
children: [ children: [
_createBotonesPerfilFormato(), _createBotonesPerfilFormato(),
SizedBox( SizedBox(
height: 68, height: MediaQuery.of(context).size.height - 460,
child: TabBar(
controller: _tabController,
tabs: <Widget>[
Tab(
icon: TipoMultimedia.video.icono,
text: 'Vídeo',
),
Tab(
icon: TipoMultimedia.audio.icono,
text: 'Audio',
),
Tab(
icon: TipoMultimedia.imagen.icono,
text: 'Imagen',
)
]
),
),
SizedBox(
height: MediaQuery.of(context).size.height - 528,
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 16.0), padding: const EdgeInsets.only(top: 16.0),
child: TabBarView( child: GridView.count(
controller: _tabController, crossAxisCount: _showProfiles? 1 : 3,
children: TipoMultimedia.values.map((tipo) { childAspectRatio: _showProfiles ? 6.0 : 1.0,
return GridView.count( crossAxisSpacing: 12,
crossAxisCount: 3, mainAxisSpacing: 12,
crossAxisSpacing: 12, children: _showProfiles? _createListPerfiles() : _createGridBotonesConversion()
mainAxisSpacing: 12,
children: _createGridBotonesConversion(tipo: tipo)
);
}).toList()
), ),
), ),
), ),
...@@ -269,7 +280,7 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -269,7 +280,7 @@ class _PaginaConversionState extends State<PaginaConversion>
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0), borderRadius: BorderRadius.circular(20.0),
), ),
color: (_showProfiles)? Theme.of(context).disabledColor : Theme.of(context).cardColor, color: (_showProfiles)? Theme.of(context).colorScheme.inversePrimary : Theme.of(context).cardColor,
child: InkWell( child: InkWell(
onTap: () { onTap: () {
setState(() { setState(() {
...@@ -290,7 +301,7 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -290,7 +301,7 @@ class _PaginaConversionState extends State<PaginaConversion>
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0), borderRadius: BorderRadius.circular(20.0),
), ),
color: (!_showProfiles)? Theme.of(context).disabledColor : Theme.of(context).cardColor, color: (!_showProfiles)? Theme.of(context).colorScheme.inversePrimary : Theme.of(context).cardColor,
child: InkWell( child: InkWell(
onTap: () { onTap: () {
setState(() { setState(() {
...@@ -310,7 +321,6 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -310,7 +321,6 @@ class _PaginaConversionState extends State<PaginaConversion>
); );
} }
// TODO: HABLAR SOBRE CÓMO HACER LOS PERFILES
List<Widget> _createListPerfiles(){ List<Widget> _createListPerfiles(){
final toRet = <Widget>[]; final toRet = <Widget>[];
...@@ -323,43 +333,30 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -323,43 +333,30 @@ class _PaginaConversionState extends State<PaginaConversion>
if(widget._formatoOriginal.tipoMultimedia == TipoMultimedia.audio){ if(widget._formatoOriginal.tipoMultimedia == TipoMultimedia.audio){
formatoPerfil = perfil.extensionMusica; formatoPerfil = perfil.extensionMusica;
} }
//
if(formatoPerfil != null && formatoPerfil != widget._formatoOriginal){ if(formatoPerfil != null){
/** Color color = Theme.of(context).cardColor;
* Entiendo que si el archivo ya se encuentra en un formato que if(formatoPerfil == widget._formatoOriginal){
* es el mismo que se usa para un cierto perfil entonces ese perfil color = Theme.of(context).disabledColor;
* esté desabilitado, pero podría llegar a ser confuso ya que no }
* se especifica por ningún sitio al usuario el por qué esa opción if(formatoPerfil == _formatoConvertido && perfil.calidad == _calidadActual){
* no está disponible. color = Theme.of(context).colorScheme.inversePrimary;
* }
* Además, no se como compaginar esto con que cada formato pueda
* tener un nivel de calidad diferente, porque podríamos tener
* hipotéticamente un formato como jpg (que es de baja calidad) pero con
* un valor de calidad alto
*
* En aplicaciones como gimp por ejemplo se puede especificar el nivel
* de compresión (inverso a la calidad) de formatos como jpg
bool desabilitado = (_formatoConvertido == formatoPerfil) &&
(_formatoConvertido?.calidad == perfil.calidad);
**/
toRet.add(Material( toRet.add(Material(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0), borderRadius: BorderRadius.circular(20.0),
), ),
// TODO: ANALIZAR SI DESABILITAR PERFILES O NO color: color,
// color: desabilitado ? Theme.of(context).disabledColor : Theme.of(context).cardColor,
color: Theme.of(context).cardColor,
child: InkWell( child: InkWell(
onTap: () { onTap: (formatoPerfil == widget._formatoOriginal)? () {
if (_formatoConvertido != formatoPerfil) { ScaffoldMessenger.of(context).showSnackBar(
setState(() { SnackBar(
// TODO: IMPLEMENTAR SISTEMA CALIDAD FORMATOS content: Text('El archivo ya coincide con este perfil'),
// _formatoConvertido = Formato(formato: formatoPerfil!, calidad: perfil.calidad); ),
_formatoConvertido = formatoPerfil; );
}); } : () {
if (_formatoConvertido != formatoPerfil && _calidadActual != perfil.calidad) {
_cambiarFormatoCalidad(formatoPerfil!, perfil.calidad);
} }
}, },
child: Container( child: Container(
...@@ -396,16 +393,16 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -396,16 +393,16 @@ class _PaginaConversionState extends State<PaginaConversion>
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0), borderRadius: BorderRadius.circular(20.0),
), ),
color: _formatoConvertido == elemento ? Theme.of(context).disabledColor color: _formatoConvertido == elemento ? Theme.of(context).colorScheme.inversePrimary
: Theme.of(context).cardColor, : Theme.of(context).cardColor,
child: InkWell( child: InkWell(
/* borderRadius: BorderRadius.circular(20.0),
TODO: COMPORTAMIENTO DE LOS BOTONES
onTap: _formatoConvertido == elemento ? () { onTap: _formatoConvertido == elemento ? () {
setState(() { _formatoConvertido = null; }); setState(() {
} : _formatoConvertido = _calidadActual = null;
*/ _cambiarFormatoCalidad(null, null);
onTap: () { setState((){_formatoConvertido = elemento;}); }, });
} : () => _cambiarFormatoCalidad(elemento, Calidad.media),
onLongPress: () { onLongPress: () {
showDialog<void>( showDialog<void>(
context: context, context: context,
...@@ -450,4 +447,25 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -450,4 +447,25 @@ class _PaginaConversionState extends State<PaginaConversion>
); );
}).toList(); }).toList();
} }
}
void _cambiarFormatoCalidad(Formato? destino, Calidad? calidad){
setState((){
_formatoConvertido = destino;
_calidadActual = calidad;
ElementoSeleccionable resultado;
if(widget._carpeta != null){
widget._carpeta!.setFormatoDestino(widget._formatoOriginal, _formatoConvertido);
resultado = widget._carpeta!;
}
else{
widget._archivo!.formatoDestino = _formatoConvertido;
resultado = widget._archivo!;
}
// TODO: ACTUALIZAR CALIDAD
widget._lista.actualizaSeleccionable(widget._indiceArchivo, resultado);
});
}
}
\ No newline at end of file
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:prueba_multimedia/modelo/modelo.dart'; import 'package:prueba_multimedia/modelo/modelo.dart';
import 'package:prueba_multimedia/widgets/carpeta_widget.dart';
import 'package:prueba_multimedia/widgets/widgets.dart'; import 'package:prueba_multimedia/widgets/widgets.dart';
class PaginaPrincipalLlena extends StatelessWidget { class PaginaPrincipalLlena extends StatelessWidget {
...@@ -19,7 +20,48 @@ class PaginaPrincipalLlena extends StatelessWidget { ...@@ -19,7 +20,48 @@ class PaginaPrincipalLlena extends StatelessWidget {
return const SizedBox(height: 8.0); return const SizedBox(height: 8.0);
}, },
itemBuilder: (context, index) { itemBuilder: (context, index) {
return SeleccionableWidget(seleccionable: seleccionables[index]); return Dismissible(
key: Key(seleccionables[index].id),
background: Container(
color: Colors.red,
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Icon(Icons.delete,
color: Colors.white,
size: 31.0
),
),
),
secondaryBackground: Container(
color: Colors.red,
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Icon(Icons.delete,
color: Colors.white,
size: 31.0
),
),
),
onDismissed: (direction) {
listaSeleccionables.borraSeleccionable(index);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('${seleccionables[index].nombre} borrado'),
action: SnackBarAction(
label: 'Deshacer',
onPressed: () {
listaSeleccionables.reinsertar(index, seleccionables[index]);
}
),
)
);
},
child: (seleccionables[index] is Carpeta)?
CarpetaWidget(indice: index, carpeta: seleccionables[index] as Carpeta, lista: listaSeleccionables)
: SeleccionableWidget(indice: index, seleccionable: seleccionables[index], lista: listaSeleccionables)
);
}, },
), ),
); );
......
import 'package:flutter/material.dart';
import 'package:prueba_multimedia/paginas/paginas.dart';
import 'package:prueba_multimedia/modelo/modelo.dart';
import 'dart:math';
class CarpetaWidget extends StatefulWidget {
final int indice;
final Carpeta carpeta;
final ListaSeleccionables lista;
const CarpetaWidget({super.key, required this.indice, required this.carpeta, required this.lista});
@override
State<CarpetaWidget> createState() => _CarpetaWidgetState();
}
class _CarpetaWidgetState extends State<CarpetaWidget>
with SingleTickerProviderStateMixin{
static double MAX_HEIGHT = 50;
late final AnimationController _controller;
List<InfoFormato> seleccionados = [];
bool open = false;
double rotAngle = 0;
double height = 0;
@override
void initState() {
_controller = AnimationController(
duration: Duration(milliseconds: 200),
vsync: this
);
_controller.addListener(() {
setState(() {
rotAngle = pi*_controller.value;
height = MAX_HEIGHT*_controller.value;
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
seleccionados = [];
for(var i in widget.carpeta.formatos){
if(i.seleccionado && (widget.carpeta.incluyeSubcarpetas || !i.subcarpeta)){
seleccionados.add(i);
}
}
if(seleccionados.length <= 0){
_controller.reset();
}
return SizedBox(
height: (height <= 0)? 60 : 60 + (seleccionados.length*(height+10)),
width: MediaQuery.of(context).size.width,
child: Stack(
children: [
Positioned(
top: 0,
child: buildMainContainer(),
),
...buildSecondaryContainers()
]
),
);
}
Widget buildMainContainer(){
String s = "";
if(seleccionados.length > 1) s = "s";
return SizedBox(
height: 60,
width: MediaQuery.of(context).size.width-10,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Flexible(flex:1, child: widget.carpeta.icono),
Flexible(flex: 1, child: const SizedBox(width: 20)),
Flexible(
flex: 15,
child: SizedBox.expand(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
flex: 1,
child: Text(
widget.carpeta.nombre,
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.bold
),
overflow: TextOverflow.ellipsis,
),
),
Flexible(
flex: 1,
child: Text(
(seleccionados.length > 0)?
'${seleccionados.length} formato$s seleccionado$s'
: 'Ningún formato seleccionado',
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: (seleccionados.length > 0)? null : Colors.red
)
),
),
],
)
),
),
Flexible(
flex: 3,
fit: FlexFit.loose,
child: Transform.rotate(
angle: rotAngle,
child: IconButton(
icon: Icon(
Icons.arrow_drop_down,
color: (seleccionados.length > 0)? null : Theme.of(context).disabledColor,
),
onPressed: (seleccionados.length <= 0)? null : () {
setState(() {
open = !open;
});
widget.carpeta.pressOpenClose();
_controller.toggle();
widget.lista.actualizaSeleccionable(widget.indice, widget.carpeta);
}
),
),
),
Flexible(
flex: 3,
child: IconButton(
icon: const Icon(Icons.edit),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return PaginaConfiguracion(
indice: widget.indice,
elemento: widget.carpeta,
lista: widget.lista
);
}));
},
),
)
]
),
);
}
List<Widget> buildSecondaryContainers(){
int index = 0;
return seleccionados.map((elemento) {
index++;
String texto = elemento.formatoOriginal.name.toUpperCase();
if(elemento.formatoDestino != null) texto += " > ${elemento.formatoDestino!.name.toUpperCase()}";
return Positioned(
top: (height+10)*index,
child: ClipRect(
clipper: _MyRectClipper(_controller),
child: Dismissible(
key: Key(elemento.conversion.id),
background: Container(
color: Colors.red,
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 4.0),
child: Icon(Icons.check_box_outline_blank,
color: Colors.white,
size: 31.0
),
),
),
secondaryBackground: Container(
color: Colors.red,
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.only(right: 14.0),
child: Icon(Icons.check_box_outline_blank,
color: Colors.white,
size: 31.0
),
),
),
onDismissed: (direction) {
seleccionados.remove(elemento);
widget.carpeta.pressAltSeleccionado(widget.carpeta.formatos.indexOf(elemento));
widget.lista.actualizaSeleccionable(widget.indice, widget.carpeta);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('${elemento.formatoOriginal.name.toUpperCase()} excluido de ${widget.carpeta.nombre}'),
action: SnackBarAction(
label: 'Deshacer',
onPressed: () {
seleccionados.add(elemento);
widget.carpeta.pressAltSeleccionado(widget.carpeta.formatos.indexOf(elemento));
widget.lista.actualizaSeleccionable(widget.indice, widget.carpeta);
}
),
)
);
},
child: SizedBox(
height: 50,
width: MediaQuery.of(context).size.width-10,
child: Padding(
padding: const EdgeInsets.only(left: 24.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Flexible(flex: 1, child: Icon(Icons.find_in_page_outlined)),
Flexible(flex: 1, child: const SizedBox(width: 10)),
Flexible(
flex: 13,
child: SizedBox.expand(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
flex: 1,
child: Text(
widget.carpeta.nombre,
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.bold
),
overflow: TextOverflow.ellipsis
),
),
Flexible(
flex: 1,
child: Text(
texto,
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.bold
)
),
),
],
)
),
),
Flexible(
flex: 2,
child: IconButton(
icon: const Icon(Icons.edit),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return PaginaConfiguracion(
indice: widget.indice,
elemento: elemento.conversion,
lista: widget.lista,
carpeta: widget.carpeta
);
}
));
},
),
)
]
),
),
),
),
),
);
}).toList();
}
}
class _MyRectClipper extends CustomClipper<Rect> {
final AnimationController _controller;
_MyRectClipper(this._controller);
@override
Rect getClip(Size size) {
double value = (1-_controller.value)*_CarpetaWidgetState.MAX_HEIGHT;
return Rect.fromLTWH(0, value, 1000, _CarpetaWidgetState.MAX_HEIGHT);
}
@override
bool shouldReclip(covariant CustomClipper<Rect> oldClipper) {
return !(_controller.isDismissed || _controller.isCompleted);
}
}
\ No newline at end of file
...@@ -3,40 +3,75 @@ import 'package:prueba_multimedia/paginas/paginas.dart'; ...@@ -3,40 +3,75 @@ import 'package:prueba_multimedia/paginas/paginas.dart';
import 'package:prueba_multimedia/modelo/modelo.dart'; import 'package:prueba_multimedia/modelo/modelo.dart';
class SeleccionableWidget extends StatelessWidget { class SeleccionableWidget extends StatelessWidget {
final int indice;
final ElementoSeleccionable seleccionable; final ElementoSeleccionable seleccionable;
final ListaSeleccionables lista;
const SeleccionableWidget({super.key, required this.seleccionable}); const SeleccionableWidget({super.key, required this.indice, required this.seleccionable, required this.lista});
// TODO: ACTUALIZAR PARA MOSTRAR FORMATOS Y CAMBIOS // TODO: ACTUALIZAR PARA MOSTRAR FORMATOS Y CAMBIOS
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( final String nombre = seleccionable.nombre;
children: <Widget>[ int index = nombre.lastIndexOf('.');
seleccionable.icono, final String visible;
const SizedBox(width: 10,), if(index > -1){
Expanded( visible = nombre.substring(0, index);
child: Column( }
crossAxisAlignment: CrossAxisAlignment.start, else {
children: [ visible = nombre;
Text( }
seleccionable.nombre,
style: Theme.of(context).textTheme.bodyLarge
), Widget? formatoOrig;
], if(seleccionable is Convertible){
Convertible arch = (seleccionable as Convertible);
String texto = arch.formatoOriginal.name.toUpperCase();
if(arch.formatoDestino != null) texto += " > ${arch.formatoDestino!.name.toUpperCase()}";
formatoOrig = Flexible(flex:1, child: Text(texto));
}
return SizedBox(
height: 60,
width: MediaQuery.of(context).size.width,
child: Row(
children: <Widget>[
Flexible(flex: 1, child: seleccionable.icono),
Flexible(flex: 1, child: const SizedBox(width: 15)),
Flexible(
flex: 16,
child: SizedBox.expand(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
flex: 1,
child: Text(
visible,
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.bold
)
),
),
if(formatoOrig != null) formatoOrig
],
)
),
),
Flexible(
flex: 2,
child: IconButton(
icon: const Icon(Icons.edit),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return PaginaConfiguracion(indice: indice, elemento: seleccionable, lista: lista);
}));
},
),
) )
), ]
if (seleccionable is Carpeta) )
IconButton(onPressed: () {}, icon: Icon(Icons.menu)),
IconButton(
icon: const Icon(Icons.edit),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return PaginaConfiguracion(elementoAsociado: seleccionable);
}));
},
)
],
); );
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment