Commit 11223e47 by Diego Pérez Peña

Merge inicial con paginaConversion (queda fusionar los seleccionables)

parents 0c50f171 99ae5780
import 'dart:io';
import 'package:flutter/material.dart';
import './convertible.dart';
import './elemento_seleccionable.dart';
import 'formato.dart';
class Archivo extends Convertible{
final File _file;
List<String> metadatos = [];
File get file => _file;
Archivo({required String id, required File file}):
_file = file,
super(id: id,
nombre: file.path.split('/').last,
formatoOriginal: ClaseFormato.fromExtension(file.path.split('/').last.split('.').last),
icon: Icon(Icons.insert_drive_file_outlined));
}
\ No newline at end of file
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:prueba_multimedia/modelo/archivo.dart';
import './convertible.dart';
import './elemento_seleccionable.dart';
import 'formato.dart';
class Carpeta extends ElementoSeleccionable{
bool _incluirSubcarpetas = false;
final List<InfoFormato> _formatos;
List<InfoFormato> get formatos => List.unmodifiable(_formatos);
Carpeta({required id, required nombre}):
_formatos = [],
super(id: id, nombre: nombre, icon: const Icon(Icons.folder_outlined));
@override
void convertir() {
// TODO: <implement>
}
}
class InfoFormato {
final bool _subcarpeta;
bool _seleccionado;
final Convertible _conversion;
final List<File> _archivos;
bool get subcarpeta => _subcarpeta;
bool get seleccionado => _seleccionado;
Convertible get conversion => _conversion;
ClaseFormato get formatoOriginal => _conversion.formatoOriginal;
Formato? get formatoDestino => _conversion.formatoDestino;
List<Archivo> get archivos => List.unmodifiable(_archivos);
set seleccionado(bool value) => _seleccionado = value;
set formato(Formato? destino) => _conversion.formatoDestino = destino;
InfoFormato({required ClaseFormato formato,
required String nombreCarpeta,
required bool subCarpeta,
bool? seleccionado,
required List<File> archivos}):
_subcarpeta = subCarpeta, _seleccionado = seleccionado ?? !subCarpeta, _archivos = archivos,
_conversion = Convertible(id: "null", nombre: '${nombreCarpeta} > ${formato.name}',
icon: Icon(Icons.find_in_page_outlined), formatoOriginal: formato);
void convertir(){
if(_conversion.formatoDestino != null){
// TODO: <implement>
}
}
}
\ No newline at end of file
import 'package:prueba_multimedia/modelo/elemento_seleccionable.dart';
import 'formato.dart';
class Convertible extends ElementoSeleccionable{
final ClaseFormato _formatoOriginal;
Formato? _formatoDestino;
ClaseFormato get formatoOriginal => _formatoOriginal;
Formato? get formatoDestino => _formatoDestino;
set formatoDestino(Formato? siguiente) => _formatoDestino = siguiente;
Convertible({required super.id, required super.nombre, required super.icon,
required ClaseFormato formatoOriginal, Formato? formatoDestino = null}):
_formatoOriginal = formatoOriginal,
_formatoDestino = formatoDestino;
void convertir(){
// TODO: <implement>
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
abstract class ElementoSeleccionable{
final String _id;
final String _nombre;
final Icon _icon;
String get id => this._id;
String get nombre => this._nombre;
Icon get icon => this._icon;
ElementoSeleccionable({required String id,
required String nombre,
required Icon icon}):
_id = id, _nombre = nombre, _icon = icon;
void convertir();
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:prueba_multimedia/modelo/convertible.dart';
import 'formato.dart';
class Enlace extends Convertible{
final String _direccion;
final RedSocial _redSocial;
List<String> metadatos = [];
Enlace({required String id, required String direccion}):
_direccion = direccion,
_redSocial = RedSocial.FACEBOOK,
super(id: id,
nombre: direccion.split('/').last,
formatoOriginal: ClaseFormato.fromExtension('png'),
icon: Icon(Icons.insert_drive_file_outlined));
@override
void convertir() {
// TODO: <implement> Descarga desde internet...
super.convertir();
}
}
enum RedSocial{
FACEBOOK,
TWITTER,
INSTAGRAM
}
\ No newline at end of file
import 'package:flutter/material.dart';
class Formato{
final ClaseFormato _extension;
Calidad _calidad;
ClaseFormato get claseFormato => _extension;
Calidad get calidad => _calidad;
set calidad(Calidad calidad) => _calidad = calidad;
bool get isVideo => _extension.tipoMultimedia == TipoMultimedia.VIDEO;
Formato({required ClaseFormato extension, Calidad calidad = Calidad.Media}):
_extension = extension, _calidad = calidad;
Formato copiaSiNulo({ClaseFormato? formato, Calidad? calidad}){
return Formato(
extension: formato ?? this.claseFormato,
calidad: calidad ?? this._calidad
);
}
}
enum ClaseFormato{
PNG('png',
'Portable Network Graphics',
TipoMultimedia.IMAGEN,
'Calidad',
[],
'Formato gráfico basado en un algoritmo de compresión sin pérdida para bitmaps no sujeto a patentes. Fue desarrollado en buena parte para solventar las deficiencias del formato GIF y permite almacenar imágenes con una mayor profundidad de contraste y otros datos importantes.'
),
JPG('jpg',
'Joint Photographic Experts Group',
TipoMultimedia.IMAGEN,
'Ligero',
[],
'A pesar de ser un método de compresión, es a menudo considerado como un formato de archivo. Es el formato de imagen más común, utilizado por las cámaras fotográficas digitales y otros dispositivos de captura de imagen'
),
TIF('tif',
'Tagged Image File Format',
TipoMultimedia.IMAGEN,
'Calidad',
[],
'Un formato de archivo informático para almacenar imágenes de mapa de bits. Es prevalente en la industria gráfica y en la fotografía profesional por su versatilidad y compresión no destructiva.'
),
MP3('mp3',
'MPEG-1 Layer III',
TipoMultimedia.AUDIO,
'Calidad',
[ Metadato('Título', 'title'),
Metadato('Artista', 'artist'),
Metadato('Álbum', 'album'),
Metadato('Género', 'genre'),
Metadato('Compositor', 'composer'),
Metadato('Número de pista', 'track', true),
Metadato('Idioma', 'language')],
'Un formato de compresión de audio digital que usa un algoritmo con pérdida para conseguir un menor tamaño de archivo. Es un formato de audio común utilizado para música tanto en computadoras como en reproductores de audio portátil.'
),
OGG('ogg',
'Xiph.org Ogg',
TipoMultimedia.AUDIO,
'Versátil',
[],
'Un formato contenedor libre y abierto, desarrollado y mantenido por la Fundación Xiph.Org que no está restringido por las patentes de software, y está diseñado para proporcionar una difusión de flujo eficiente y manipulación de multimedios digitales de alta calidad.'
),
WAV('wav',
'Waveform Audio File Format',
TipoMultimedia.AUDIO,
'Calidad',
[],
'Un formato de audio digital con o sin compresión de datos desarrollado por Microsoft e IBM que se utiliza para almacenar flujos digitales de audio en el PC, mono y estéreo a diversas resoluciones y velocidades de muestreo.'
),
MP4('mp4',
'MPEG-4 Parte 14',
TipoMultimedia.VIDEO,
'Calidad',
[ Metadato('Título', 'title'),
Metadato('Autor', 'author'),
Metadato('Álbum', 'album'),
Metadato('Año', 'year', true),
Metadato('Género', 'genre'),
Metadato('Compositor', 'composer'),
Metadato('Número de pista', 'track', true),
Metadato('Descripción', 'description'),
Metadato('Comentario', 'comment')],
'Un formato contenedor especificado como parte del estándar internacional MPEG-4 de ISO/IEC. Es utilizado para almacenar los formatos audiovisuales especificados por ISO/IEC y el grupo MPEG (Moving Picture Experts Group) al igual que otros formatos audiovisuales disponibles.'
),
MKV('mkv',
'Matroshka',
TipoMultimedia.VIDEO,
'Calidad',
[ Metadato('Título', 'title'),
Metadato('Descripción', 'description'),
Metadato('Idioma', 'language')],
'Un formato contenedor abierto que puede almacenar una cantidad muy grande de vídeo, audio, imagen o pistas de subtítulos dentro de un solo archivo. Su finalidad es la de servir como formato universal para el almacenamiento de contenidos audiovisuales y multimedia, como películas o programas de televisión, imágenes y textos.'
),
WMV('wmv',
'Windows Media Video',
TipoMultimedia.VIDEO,
'Calidad',
[ Metadato('Título', 'title'),
Metadato('Autor', 'author'),
Metadato('Valoración', 'rating'),
Metadato('Comentario', 'comment')],
'Un formato de vídeo desarrollado por Microsoft, que forma parte del framework Windows Media. No está contruida solo con tecnología interna de Microsoft. Desde la versión 7 (WMV1), Microsoft ha utilizado su propia versión no estandarizada de MPEG-4. El vídeo a menudo se combina con sonido en formato Windows Media Audio.'
);
final String _extension;
final String _nombre;
final TipoMultimedia _tipoMultimedia;
final String _descripcion;
final String _clasificacion;
final List<Metadato> _metadatos;
String get extension => _extension;
String get nombre => _nombre;
String get descripcion => _descripcion;
TipoMultimedia get tipoMultimedia => _tipoMultimedia;
List<Metadato> get metadatos => _metadatos;
const ClaseFormato(this._extension, this._nombre, this._tipoMultimedia, this._clasificacion, this._metadatos, this._descripcion);
static List<ClaseFormato> listadoFormatos({required TipoMultimedia tipo, ClaseFormato? excepcion}){
final toRet = <ClaseFormato>[];
for(var formato in ClaseFormato.values){
if(formato._tipoMultimedia == tipo && formato != excepcion) {
toRet.add(formato);
}
}
return toRet;
}
static ClaseFormato fromExtension(String extension){
for(ClaseFormato f in ClaseFormato.values){
if(f._extension == extension){
return f;
}
}
return ClaseFormato.PNG;
}
}
enum TipoMultimedia{
VIDEO(Icon(Icons.movie_creation_outlined)),
AUDIO(Icon(Icons.music_note_outlined)),
IMAGEN(Icon(Icons.image_outlined));
final Icon icono;
const TipoMultimedia(this.icono);
}
enum Calidad{
Baja,
Media,
Alta,
Muy_alta;
}
class Metadato{
final String _nombreMostrado;
final String _nombreInterno;
final bool _numerico;
String get nombreMostrado => _nombreMostrado;
String get nombreInterno => _nombreInterno;
bool get numerico => _numerico;
const Metadato(String nombreMostrado, String nombreInterno, [bool numerico = false]):
_nombreMostrado = nombreMostrado, _nombreInterno = nombreInterno, _numerico = numerico;
}
\ No newline at end of file
import 'formato.dart';
enum Perfil{
IMAGEN_ALTA_CALIDAD('Alta calidad', ClaseFormato.PNG, ClaseFormato.WAV, ClaseFormato.MKV ,Calidad.Muy_alta),
IMAGEN_COMPARTIR('Para compartir', ClaseFormato.JPG, ClaseFormato.MP3, ClaseFormato.MP4, Calidad.Baja);
final String nombre;
final ClaseFormato? extensionImagen;
final ClaseFormato? extensionMusica;
final ClaseFormato? extensionVideo;
final Calidad calidad;
const Perfil(this.nombre, this.extensionImagen, this.extensionMusica,
this.extensionVideo, this.calidad);
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:prueba_multimedia/modelo/archivo.dart';
import 'package:prueba_multimedia/modelo/convertible.dart';
import 'package:prueba_multimedia/modelo/elemento_seleccionable.dart';
import 'package:prueba_multimedia/modelo/carpeta.dart';
import 'package:prueba_multimedia/modelo/enlace.dart';
import 'package:prueba_multimedia/paginas/pagina_configuracion_carpeta.dart';
import 'package:prueba_multimedia/paginas/pagina_conversion.dart';
import 'package:prueba_multimedia/paginas/pagina_fotograma.dart';
import 'package:prueba_multimedia/paginas/pagina_metadatos.dart';
import '../modelo/formato.dart';
class PaginaConfiguracion extends StatefulWidget {
final ElementoSeleccionable _elementoAsociado;
const PaginaConfiguracion({
super.key,
required elementoAsociado
}): _elementoAsociado = elementoAsociado;
@override
State<PaginaConfiguracion> createState() => _PaginaConfiguracionState();
}
class _PaginaConfiguracionState extends State<PaginaConfiguracion> {
int _categoriaActiva = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: RichText(
text: TextSpan(
children: [
WidgetSpan(
child: Icon(_getIcon())
),
TextSpan(
text: _getShownName(),
style: Theme.of(context).textTheme.titleLarge,
)
]
)
),
),
body: _construirCuerpo(),
bottomNavigationBar: _construirBarraNavegacion()
);
}
IconData _getIcon(){
if(widget._elementoAsociado is Carpeta){
return Icons.folder;
}
if(widget._elementoAsociado is Enlace){
return Icons.link;
}
return Icons.insert_drive_file;
}
String _getShownName() {
final sb = StringBuffer();
sb.write(" ");
sb.write(widget._elementoAsociado.nombre);
return sb.toString();
}
Widget _construirCuerpo() {
if(widget._elementoAsociado is Convertible){
final arch = widget._elementoAsociado as Convertible;
if(_categoriaActiva == 0){
return PaginaConversion(formatoOriginal: arch.formatoOriginal);
}
else if(_categoriaActiva == 2){
return PaginaMetadatos(formato: arch.formatoOriginal);
}
else{
if(arch.formatoOriginal.tipoMultimedia == TipoMultimedia.VIDEO){
return PaginaFotograma();
}
// Página de metadatos
return PaginaMetadatos(formato: arch.formatoOriginal);
}
}
// Páginas de carpetas y formato de archivo para carpeta
// TODO: Carpeta de ejemplo, cargar carpetas de verdad
return PaginaConfiguracionCarpeta(formatosCarpeta: [
InfoFormato(formato: ClaseFormato.JPG,
nombreCarpeta: widget._elementoAsociado.nombre,
subCarpeta: false,
archivos: []),
InfoFormato(formato: ClaseFormato.MP4,
nombreCarpeta: widget._elementoAsociado.nombre,
subCarpeta: false,
archivos: []),
InfoFormato(formato: ClaseFormato.TIF,
nombreCarpeta: widget._elementoAsociado.nombre,
subCarpeta: true,
archivos: []),
InfoFormato(formato: ClaseFormato.PNG,
nombreCarpeta: widget._elementoAsociado.nombre,
subCarpeta: true,
archivos: [])
]);
}
BottomNavigationBar? _construirBarraNavegacion(){
if(widget._elementoAsociado is Archivo){
return BottomNavigationBar(
currentIndex: _categoriaActiva,
onTap: (int indice) {
setState(() {
_categoriaActiva = indice;
});
},
items: _construirElementosBarraNavegacion()
);
}
return null;
}
List<BottomNavigationBarItem> _construirElementosBarraNavegacion(){
final toRet = <BottomNavigationBarItem>[];
bool isVideo = false;
if(widget._elementoAsociado is Archivo){
final arch = widget._elementoAsociado as Archivo;
if(arch.formatoOriginal.tipoMultimedia == TipoMultimedia.VIDEO){
isVideo = true;
}
}
// Página de selección de conversión
toRet.add(BottomNavigationBarItem(
icon: Icon(Icons.sync),
label: isVideo ? 'Conversión' : 'Formato de Conversión'
));
// Página de selección de fotograma
if(isVideo){
toRet.add(BottomNavigationBarItem(
icon: Icon(Icons.local_movies),
label: 'Fotograma'
));
}
// Página de edición de metadatos
toRet.add(BottomNavigationBarItem(
icon: Icon(Icons.format_list_bulleted),
label: 'Metadatos'
));
return toRet;
}
}
import 'package:flutter/material.dart';
import 'package:prueba_multimedia/modelo/carpeta.dart';
import 'package:prueba_multimedia/paginas/pagina_configuracion.dart';
import '../modelo/formato.dart';
class PaginaConfiguracionCarpeta extends StatefulWidget {
final List<InfoFormato> _formatosCarpeta;
const PaginaConfiguracionCarpeta({super.key, required List<InfoFormato> formatosCarpeta}):
_formatosCarpeta = formatosCarpeta;
@override
State<PaginaConfiguracionCarpeta> createState() => _PaginaConfiguracionCarpetaState();
}
class _PaginaConfiguracionCarpetaState extends State<PaginaConfiguracionCarpeta> {
final Map<TipoMultimedia, List<ClaseFormato>> _formatos = {};
final Map<TipoMultimedia, bool?> _allOfType = {};
final Map<ClaseFormato, bool> _seleccionados = {};
late bool _incluirSubcarpetas;
@override
void initState() {
super.initState();
_incluirSubcarpetas = false;
for(TipoMultimedia t in TipoMultimedia.values){
_formatos[t] = [];
}
for(InfoFormato i in widget._formatosCarpeta){
_formatos[i.formatoOriginal.tipoMultimedia]?.add(i.formatoOriginal);
_seleccionados[i.formatoOriginal] = i.seleccionado;
}
for(TipoMultimedia t in TipoMultimedia.values){
_allOfType[t] = _actualizarCheckboxTipo(t);
}
}
bool? _actualizarCheckboxTipo(TipoMultimedia t){
if(_formatos[t]!.isEmpty) return false;
bool primero = _seleccionados[_formatos[t]!.first]!;
for(int i = 1; i<_formatos[t]!.length; i++){
if(primero != _seleccionados[_formatos[t]![i]]){
return null;
}
}
return primero;
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCheckboxTodos(context),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
RichText(
text: TextSpan(
children: [
const WidgetSpan(
child: Icon(Icons.folder_copy_outlined)
),
const WidgetSpan(
child: SizedBox(width: 15)
),
TextSpan(
text: 'Incluir subcarpetas',
style: Theme.of(context).textTheme.bodyLarge
)
]
)
),
Checkbox(
value: _incluirSubcarpetas,
onChanged: (bool? value) {
setState(() {
_incluirSubcarpetas = value!;
});
})
],
),
),
Divider(
thickness: 3
),
_buildFilasFormato(context)
],
),
);
}
Widget _buildCheckboxTodos(BuildContext context){
const Map<TipoMultimedia, String> textos = {
TipoMultimedia.IMAGEN : 'Todas las imágenes',
TipoMultimedia.AUDIO : 'Todos los audios',
TipoMultimedia.VIDEO : 'Todos los vídeos'
};
final lista = <Widget>[];
for(TipoMultimedia t in TipoMultimedia.values){
if(_formatos[t]!.isNotEmpty){
lista.add(Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
RichText(
text: TextSpan(
children: [
WidgetSpan(
child: t.icono
),
const WidgetSpan(
child: SizedBox(width: 15)
),
TextSpan(
text: textos[t]!,
style: Theme.of(context).textTheme.bodyLarge
)
]
)
),
Checkbox(
tristate: true,
value: _allOfType[t],
onChanged: (bool? value) {
setState(() {
_allOfType[t] = value ??
((_allOfType[t] == null)? true : !(_allOfType[t]!));
for(InfoFormato i in widget._formatosCarpeta){
if(_formatos[t]!.contains(i.formatoOriginal) && !(!_incluirSubcarpetas && i.subcarpeta)){
_seleccionados[i.formatoOriginal] = _allOfType[t]!;
}
}
});
})
],
));
}
}
return Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: lista,
));
}
Widget _buildFilasFormato(BuildContext context){
List<Widget> listaCarpeta = <Widget>[];
List<Widget> listaSubcarpeta = <Widget>[];
for(InfoFormato i in widget._formatosCarpeta){
Widget fila = Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
RichText(
text: TextSpan(
children: [
WidgetSpan(
child: i.formatoOriginal.tipoMultimedia.icono
),
const WidgetSpan(
child: SizedBox(width: 15)
),
TextSpan(
text: 'Archivos ${i.formatoOriginal.name}',
style: Theme.of(context).textTheme.bodyLarge
)
]
)
),
Wrap(
children: [
Checkbox(
value: _seleccionados[i.formatoOriginal]!,
onChanged: (i.subcarpeta && !_incluirSubcarpetas)? null :
(bool? value) {
setState(() {
_seleccionados[i.formatoOriginal] = value!;
_allOfType[i.formatoOriginal.tipoMultimedia] =
_actualizarCheckboxTipo(i.formatoOriginal.tipoMultimedia);
});
}),
IconButton(
icon: const Icon(Icons.edit),
onPressed: ((i.subcarpeta && !_incluirSubcarpetas) || !_seleccionados[i.formatoOriginal]!)? null :
() {
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return PaginaConfiguracion(elementoAsociado: i.conversion);
}
));
},
)
]
)
],
);
if(i.subcarpeta) listaSubcarpeta.add(fila);
else listaCarpeta.add(fila);
}
return Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
...listaCarpeta,
...listaSubcarpeta
]
));
}
}
import 'dart:async';
import 'package:flutter/material.dart';
class PaginaFotograma extends StatefulWidget {
const PaginaFotograma({super.key});
@override
State<PaginaFotograma> createState() => _PaginaFotogramaState();
}
class _PaginaFotogramaState extends State<PaginaFotograma> {
int _fotogramaSeleccionado = 0;
int _ultimoFotograma = 1200;
final _buttons = _IconButtons.values;
Timer? timer;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: (MediaQuery.of(context).size.width - 16.0*2)*9.0/16.0,
child: Placeholder(),
),
const SizedBox(height: 48.0),
Slider(
min: 0.0,
max: _ultimoFotograma.toDouble(),
divisions: _ultimoFotograma,
value: _fotogramaSeleccionado.toDouble(),
label: _fotogramaSeleccionado.toString(),
onChanged: (value) {
setState(() { _fotogramaSeleccionado = value.toInt(); });
}
),
RichText(
text: TextSpan(
children: [
TextSpan(
text: 'Fotograma seleccionado: ',
style: Theme.of(context).textTheme.titleLarge,
),
TextSpan(
text: _fotogramaSeleccionado.toString(),
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold
),
)
]
),
),
const SizedBox(height: 24.0),
_construirBotonesFotograma(context)
],
),
);
}
Widget _construirBotonesFotograma(BuildContext context) {
final buttons = <Widget>[];
for(var button in _buttons){
buttons.add(GestureDetector(
onLongPressStart: (detail) {
setState(() {
timer = Timer.periodic(const Duration(milliseconds: 100), (t) {
setState(() {
_fotogramaSeleccionado += button.variation;
if(_fotogramaSeleccionado < 0) _fotogramaSeleccionado = 0;
if(_fotogramaSeleccionado > _ultimoFotograma) _fotogramaSeleccionado = _ultimoFotograma;
});
});
});
},
onLongPressEnd: (detail) {
if (timer != null) {
timer!.cancel();
}
},
child: Ink(
decoration: ShapeDecoration(
color: Theme.of(context).colorScheme.surfaceTint,
shape: CircleBorder()
),
child: IconButton(
icon: button.icon,
color: Colors.white,
onPressed: () {
setState(() {
_fotogramaSeleccionado += button.variation;
if(_fotogramaSeleccionado < 0) _fotogramaSeleccionado = 0;
if(_fotogramaSeleccionado > _ultimoFotograma) _fotogramaSeleccionado = _ultimoFotograma;
});
},
),
),
),
);
}
return Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 15.0,
children: buttons,
);
}
}
enum _IconButtons{
DOBLE_ATRAS(Icon(Icons.keyboard_double_arrow_left), -60),
ATRAS(Icon(Icons.chevron_left), -1),
ADELANTE(Icon(Icons.chevron_right), 1),
DOBLE_ADELANTE(Icon(Icons.keyboard_double_arrow_right), 60);
final Icon icon;
final int variation;
const _IconButtons(this.icon, this.variation);
}
import 'package:flutter/material.dart';
import '../modelo/formato.dart';
class PaginaMetadatos extends StatefulWidget {
final ClaseFormato _formato;
const PaginaMetadatos({super.key, required formato}): _formato = formato;
@override
State<PaginaMetadatos> createState() => _PaginaMetadatosState();
}
class _PaginaMetadatosState extends State<PaginaMetadatos> {
late final List<Metadato> _metadatos;
final List<TextEditingController> _controladores = [];
final List<String> _valoresElegidos = [];
@override
void initState() {
super.initState();
_metadatos = widget._formato.metadatos;
for(int i = 0; i<_metadatos.length; i++){
_valoresElegidos.add('');
_controladores.add(TextEditingController());
if(_metadatos[i].numerico){
_controladores[i].addListener(() {
setState(() {
String texto = _controladores[i].value.text;
if(texto.isEmpty || RegExp(r'^[0-9]+$').hasMatch(texto)){
_valoresElegidos[i] = _controladores[i].text;
}
else{
_controladores[i].text = _valoresElegidos[i];
}
});
});
}
else{
_controladores[i].addListener(() {
setState(() { _valoresElegidos[i] = _controladores[i].text; });
});
}
}
}
@override
void dispose() {
while(_controladores.isNotEmpty){
_controladores.first.dispose();
_controladores.removeAt(0);
}
super.dispose();
}
@override
Widget build(BuildContext context) {
if(_metadatos.length > 0){
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
children: _buildTextFields()
),
),
);
}
else{
final sb = StringBuffer();
sb.write('El formato seleccionado (.');
sb.write(widget._formato.extension);
sb.write(') no admite metadatos.');
return Padding(
padding: const EdgeInsets.all(15.0),
child: SafeArea(
child: Align(
alignment: Alignment.center,
child: Text(
sb.toString(),
style: Theme.of(context).textTheme.titleLarge,
textAlign: TextAlign.center,
),
)
),
);
}
}
List<Widget> _buildTextFields(){
final toRet = <Widget>[];
for(int i = 0; i<_metadatos.length && i<4; i++){
toRet.add(_buildTextFieldLabel(i));
toRet.add(_buildTextField(i));
if(i < _metadatos.length-1) toRet.add(const SizedBox(height: 16.0));
}
if(_metadatos.length > 4){
final sublist = <Widget>[];
sublist.add(const SizedBox(height: 16.0));
for(int i = 4; i<_metadatos.length; i++){
sublist.add(_buildTextFieldLabel(i));
sublist.add(_buildTextField(i));
if(i < _metadatos.length-1) sublist.add(const SizedBox(height: 16.0));
}
toRet.add(ExpansionTile(
collapsedBackgroundColor: Theme.of(context).colorScheme.inversePrimary,
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Center(child: Text('Mostrar más metadatos')),
children: [Container(
color: Theme.of(context).scaffoldBackgroundColor,
child: Column(
children: sublist,
),
)]
));
}
return toRet;
}
Widget _buildTextFieldLabel(int index){
return Text(
_metadatos[index].nombreMostrado,
style: Theme.of(context).textTheme.titleMedium
);
}
Widget _buildTextField(int index){
return TextField(
controller: _controladores[index],
decoration: InputDecoration(
hintText: '${_metadatos[index].nombreMostrado}...',
border: OutlineInputBorder(),
),
keyboardType:
(_metadatos[index].numerico)? const TextInputType.numberWithOptions(
signed: true,
decimal: false
): null,
);
}
}
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:prueba_multimedia/modelo/archivo.dart';
import 'package:prueba_multimedia/modelo/carpeta.dart';
import 'package:prueba_multimedia/modelo/convertible.dart';
import 'package:prueba_multimedia/modelo/enlace.dart';
import 'package:prueba_multimedia/modelo/formato.dart';
import 'package:prueba_multimedia/paginas/pagina_configuracion.dart';
import 'package:prueba_multimedia/paginas/pagina_configuracion_carpeta.dart';
import 'package:prueba_multimedia/widget/convertex_fab_bar.dart';
import 'package:prueba_multimedia/modelo/lista_seleccionables.dart';
......
import 'package:flutter/material.dart';
import 'package:prueba_multimedia/paginas/pagina_configuracion.dart';
import '../modelo/carpeta.dart';
class PaginaPrincipalVacia extends StatelessWidget {
const PaginaPrincipalVacia({super.key});
......@@ -19,6 +22,16 @@ class PaginaPrincipalVacia extends StatelessWidget {
Text(
'Pulsa el botón + para agregarlos',
style: Theme.of(context).textTheme.bodyLarge,
),
TextButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return PaginaConfiguracion(elementoAsociado: Carpeta(id: "1", nombre: 'miCarpeta'));
}
));
},
child: Text('Pulsá')
)
],
),
......
......@@ -148,6 +148,7 @@ class ExpandableFabState extends State<ExpandableFab>
);
}
return FloatingActionButton.extended(
heroTag: (widget.invert)? 'btn1' : 'btn2',
onPressed: _toggle,
icon: widget.icon,
label: Text(
......
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