Commit 0c50f171 by Diego Pérez Peña

Fusión con rama AñadirArchivo

parents 269a0fce d9c84e11
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'paginas/pagina_principal.dart';
import 'package:prueba_multimedia/modelo/lista_seleccionables.dart';
class ConvertexPrototipoApp extends StatelessWidget {
const ConvertexPrototipoApp({super.key});
......@@ -18,7 +19,12 @@ class ConvertexPrototipoApp extends StatelessWidget {
)
),
themeMode: ThemeMode.system,
home: PaginaPrincipal(),
home: MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => ListaSeleccionables())
],
child: PaginaPrincipal(),
),
);
}
}
import './conversion.dart';
import './elemento_seleccionable.dart';
class ArchivoIndividual extends ElementoSeleccionable{
final Conversion _conversion;
List<String> metadatos = [];
ArchivoIndividual({required super.id, required super.nombre, required super.localizacion,
required Conversion conversion}): _conversion = conversion;
@override
void convertir() {
_conversion.convertir();
}
}
\ No newline at end of file
import './conversion.dart';
import './elemento_seleccionable.dart';
import 'formato.dart';
class Carpeta extends ElementoSeleccionable{
bool _incluirSubcarpetas = false;
final Map<Formato, bool> _formatosAConvertir;
final Map<Formato, Conversion> _conversiones;
Carpeta({required super.id, required super.nombre, required super.localizacion}):
_formatosAConvertir = {}, _conversiones = {};
@override
void convertir() {
_conversiones.values.forEach((conv) => conv.convertir());
}
}
\ No newline at end of file
import 'formato.dart';
class Conversion{
final Formato _formatoOriginal;
Formato? _formatoDestino;
Conversion({required Formato formatoOriginal, Formato? formatoDestino = null}):
_formatoOriginal = formatoOriginal, _formatoDestino = formatoDestino;
void convertir(){
// TODO: <implement>
}
}
\ No newline at end of file
abstract class ElementoSeleccionable{
final String _id;
final String _nombre;
final String _localizacion;
String get id => this._id;
String get nombre => this._nombre;
String get localizacion => this._localizacion;
ElementoSeleccionable({required String id,
required String nombre,
required String localizacion}):
_id = id, _nombre = nombre, _localizacion = localizacion;
void convertir();
}
\ No newline at end of file
import 'archivo_individual.dart';
class Enlace extends ArchivoIndividual{
final RedSocial _redSocial;
Enlace({required super.id, required super.nombre,
required super.localizacion, required super.conversion,
required redSocial}): _redSocial = redSocial;
@override
void convertir() {
// TODO: <implement> Descarga desde internet...
super.convertir();
}
}
enum RedSocial{
FACEBOOK,
TWITTER,
INSTAGRAM
}
\ No newline at end of file
class Formato{
final ClaseFormato _extension;
Calidad _calidad;
Formato({required ClaseFormato extension, Calidad calidad = Calidad.ORIGINAL}):
_extension = extension, _calidad = calidad;
}
enum ClaseFormato{
PNG(".png", "Portable Network Graphics", "Calidad", "Nombre, Autor...", "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.");
final String _extension;
final String _nombre;
final String _descripcion;
final String _clasificacion;
final String _metadatos;
const ClaseFormato(this._extension, this._nombre, this._clasificacion, this._metadatos, this._descripcion);
}
enum Calidad{
BAJA,
MEDIA,
ALTA,
MUY_ALTA,
ORIGINAL
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:prueba_multimedia/modelo/seleccionables.dart';
class ListaSeleccionables extends ChangeNotifier {
final _seleccionables = <Seleccionable>[];
List<Seleccionable> get seleccionables => List.unmodifiable(_seleccionables);
void borraSeleccionable(int indice) {
_seleccionables.removeAt(indice);
notifyListeners();
}
void addSeleccionable(Seleccionable item) {
_seleccionables.add(item);
notifyListeners();
}
}
\ No newline at end of file
import 'formato.dart';
class Perfil{
final String _identificador;
final String _nombre;
final Formato _formatoCorrespondiente;
Perfil({required String identificador, required String nombre, required Formato formatoCorrespondiente}):
_identificador = identificador, _nombre = nombre, _formatoCorrespondiente = formatoCorrespondiente;
}
\ No newline at end of file
import 'dart:io';
import 'package:flutter/material.dart';
/// Representa un item que se puede añadir a la lista
/// de conversión.
class Seleccionable {
final String nombre;
final Icon icono;
Seleccionable({required this.nombre, required this.icono});
}
/// Las carpetas son seleccionables que contienen otros seleccionables
class Carpeta extends Seleccionable {
final Directory directory;
final listaConvertibles = <Seleccionable>[];
final bool recursiva;
Carpeta(this.directory, this.recursiva): super(
nombre: directory.path.split('/').last,
icono: const Icon(Icons.folder)
)
{
// TODO: mirar recursividad y Link objects
var listaCarpeta = directory.list(recursive: recursiva);
listaCarpeta.forEach( (entity) {
if (entity is File) {
listaConvertibles.add(Archivo(entity));
}
else if (entity is Directory) {
listaConvertibles.add(Carpeta(directory, false));
}
});
}
}
/// Seleccionables que pueden pasar por un proceso de conversion de formatos
class Convertible extends Seleccionable {
final String formatoInicial;
final String? formatoFinal;
Convertible({required super.nombre, required super.icono,
required this.formatoInicial, this.formatoFinal});
}
class Archivo extends Convertible {
final File file;
Archivo(this.file): super(
nombre: file.path.split('/').last,
icono: Icon(Icons.description_outlined),
formatoInicial: file.path.split('.').last,
formatoFinal: null
);
}
class Enlace extends Convertible {
final String enlace;
Enlace(this.enlace): super(
nombre: enlace,
icono: Icon(Icons.link_outlined),
formatoInicial: enlace,
formatoFinal: null
);
}
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:prueba_multimedia/widget/convertex_fab_bar.dart';
import 'package:prueba_multimedia/modelo/lista_seleccionables.dart';
import 'pagina_principal_llena.dart';
import 'pagina_principal_vacia.dart';
import 'dart:math';
class PaginaPrincipal extends StatefulWidget {
class PaginaPrincipal extends StatelessWidget {
const PaginaPrincipal({super.key});
@override
State<PaginaPrincipal> createState() => _PaginaPrincipalState();
}
/*
* Al igual que en la lista de la compra, tenemos un estado que son
* los archivos seleccionados para realizar la conversión.
*
* Ese estado es consultado por la pantalla principal para determinar
* si
*/
class _PaginaPrincipalState extends State<PaginaPrincipal> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Convertex Prototipo'),
backgroundColor: Theme.of(context).primaryColor,
actions: [
IconButton(onPressed: () {
showDialog<void>(
context: context,
builder: (context) {
return AlertDialog(
content: Text('Funcionalidad a implementar'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('CLOSE'),
),
],
);
},
);
},
icon: Icon(Icons.settings_outlined))
],
IconButton(
icon: Icon(Icons.settings_outlined),
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text('Funcionalidad a implementar'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('CLOSE'),
)
]
);
}
);
}
)
]
),
body: _construirCuerpo(context),
floatingActionButton: Padding(
padding: const EdgeInsets.all(16.0),
child: ConVertexFabBar()
floatingActionButton: Padding (
padding: const EdgeInsets.all(16.0),
child: ConVertexFabBar()
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
Widget _construirCuerpo(BuildContext context) {
if(false){
return PaginaPrincipalLlena();
}
else{
return PaginaPrincipalVacia();
}
return Consumer<ListaSeleccionables>(
builder: (context, manager, child) {
if (manager.seleccionables.isNotEmpty) {
return PaginaPrincipalLlena(listaSeleccionables: manager);
} else {
return PaginaPrincipalVacia();
}
}
);
}
}
import 'package:flutter/material.dart';
import 'package:prueba_multimedia/widget/archivo_widget.dart';
import 'package:prueba_multimedia/modelo/lista_seleccionables.dart';
import 'package:prueba_multimedia/modelo/seleccionables.dart';
import 'package:prueba_multimedia/widget/seleccionable_widget.dart';
class PaginaPrincipalLlena extends StatelessWidget {
const PaginaPrincipalLlena({super.key});
final ListaSeleccionables listaSeleccionables;
const PaginaPrincipalLlena({super.key, required this.listaSeleccionables});
static const ejemplo = ["Hola", "Esto es una prueba", "Ayuda"];
@override
Widget build(BuildContext context) {
final seleccionables = listaSeleccionables.seleccionables;
return Padding(
padding: const EdgeInsets.all(10.0),
child: ListView.separated(
itemCount: ejemplo.length,
itemCount: seleccionables.length,
separatorBuilder: (context, index) {
return const SizedBox(height: 8.0);
},
itemBuilder: (context, index) {
return ArchivoWidget(key: key, texto: ejemplo[index]);
return SeleccionableWidget(seleccionable: seleccionables[index]);
},
),
);
......
import 'package:flutter/material.dart';
class ActionButton extends StatelessWidget {
final ActionButtonTypes tipoBoton;
final VoidCallback? onPressed;
final String label;
final Icon icon;
const ActionButton({super.key, this.onPressed, required this.label, required this.icon});
ActionButton({super.key, required this.tipoBoton, required this.onPressed});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Directionality(
textDirection: TextDirection.rtl,
child: FilledButton.icon(
onPressed: onPressed,
label: Text(label, textScaler: const TextScaler.linear(1.2)),
icon: icon
label: Text(tipoBoton.label, textScaler: const TextScaler.linear(1.2)),
icon: tipoBoton.icon,
),
);
}
}
enum ActionButtonTypes {
archivo('Archivo', Icon(Icons.description_outlined)),
carpeta('Carpeta', Icon(Icons.folder_outlined)),
enlace('Enlace', Icon(Icons.link_outlined)),
copiar('Copiar', Icon(Icons.copy)),
comprimir('Comprimir', Icon(Icons.splitscreen_outlined)),
reemplazar('Reemplazar', Icon(Icons.change_circle_outlined));
final String label;
final Icon icon;
const ActionButtonTypes(this.label, this.icon);
}
\ No newline at end of file
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:prueba_multimedia/modelo/lista_seleccionables.dart';
import 'package:prueba_multimedia/modelo/seleccionables.dart';
import './action_button.dart';
import 'action_button.dart';
import 'expandable_fab.dart';
class ConVertexFabBar extends StatefulWidget {
......@@ -23,6 +29,8 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
@override
Widget build(BuildContext context) {
final manager = Provider.of<ListaSeleccionables>(context, listen: false);
return SizedBox(
width: MediaQuery.of(context).size.width,
height: (_agregarOpen || _convertirOpen)? MediaQuery.of(context).size.height : 56,
......@@ -30,7 +38,6 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
children: [
GestureDetector(
onTap: () {
print('Hola');
setState(() {
_agregarOpen = false;
_convertirOpen = false;
......@@ -44,7 +51,7 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: [..._buildFABs()],
children: [..._buildFABs(manager)],
),
)
],
......@@ -52,10 +59,28 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
);
}
List<Widget> _buildFABs() {
List<Widget> _loadConvertirActionButtons (BuildContext context) {
final buttons = <Widget>[
ActionButton(
tipoBoton: ActionButtonTypes.copiar,
onPressed: () {},
),
ActionButton(
tipoBoton: ActionButtonTypes.reemplazar,
onPressed: () {}
),
ActionButton(
tipoBoton: ActionButtonTypes.comprimir,
onPressed: () {}
)
];
return buttons;
}
List<Widget> _buildFABs(ListaSeleccionables manager) {
final toRet = <Widget>[];
// Convertir FAB
// FAB con las opciones para convertir
toRet.add(Stack(
alignment: Alignment.bottomLeft,
children: [
......@@ -64,8 +89,8 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
distance: 60,
icon: Icon(Icons.label_important_outline),
label: 'Convertir',
children: _loadConvertirActionButtons(context),
key: _convertirKey
key: _convertirKey,
children: _loadConvertirActionButtons(context)
),
SizedBox(
width: 130,
......@@ -76,7 +101,6 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
_agregarOpen = false;
_convertirOpen = !_convertirOpen;
});
print("Hola");
_agregarKey.currentState?.close();
_convertirKey.currentState?.tap();
},
......@@ -85,15 +109,16 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
],
));
// Agregar FAB
// FAB con las opciones para agregar archivos, carpetas, enlaces...
toRet.add(Stack(
alignment: Alignment.bottomRight,
children: [
ExpandableFab(
distance: 60,
icon: Icon(Icons.add),
children: _loadAgregarActionButtons(context),
key: _agregarKey,
children: _loadAgregarActionButtons(context, manager),
),
SizedBox(
width: 56,
......@@ -115,66 +140,39 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
return toRet;
}
List<Widget> _loadAgregarActionButtons(BuildContext context){
final buttons = <Widget>[];
for(var element in _AgregarActionButtons.values){
buttons.add(ActionButton(
onPressed: () => _showPlaceholderAction(context),
label: element.label,
icon: element.icon)
);
}
return buttons;
}
List<Widget> _loadConvertirActionButtons(BuildContext context){
final buttons = <Widget>[];
for(var element in _ConvertirActionButtons.values){
buttons.add(ActionButton(
onPressed: () => _showPlaceholderAction(context),
label: element.label,
icon: element.icon)
);
}
// TODO: TERMINAR MÉTODOS
List<Widget> _loadAgregarActionButtons(BuildContext context,
ListaSeleccionables manager)
{
final buttons = <Widget>[
ActionButton(
tipoBoton: ActionButtonTypes.enlace,
onPressed: () {}
),
ActionButton(
tipoBoton: ActionButtonTypes.carpeta,
onPressed: () async {
String? path = await FilePicker.platform.getDirectoryPath();
if (path != null) {
// TODO: AÑADIR POSIBILIDAD RECURSIVIDAD
var directory = Directory(path);
manager.addSeleccionable(Carpeta(directory, false));
}
}
),
ActionButton(
tipoBoton: ActionButtonTypes.archivo,
onPressed: () async {
FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result != null) {
File file = File(result.files.first.path!);
manager.addSeleccionable(Archivo(file));
} else {
//
}
},
),
];
return buttons;
}
void _showPlaceholderAction(BuildContext context) {
showDialog<void>(
context: context,
builder: (context) {
return AlertDialog(
content: Text('Funcionalidad a implementar'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('CLOSE'),
),
],
);
},
);
}
}
enum _AgregarActionButtons{
NUEVO_ARCHIVO('Archivo', Icon(Icons.description_outlined)),
NUEVA_CARPETA('Carpeta', Icon(Icons.folder_outlined)),
NUEVO_ENLACE('Enlace', Icon(Icons.link_outlined));
final String label;
final Icon icon;
const _AgregarActionButtons(this.label, this.icon);
}
enum _ConvertirActionButtons{
COPIAR_ARCHIVOS('Copiar', Icon(Icons.copy)),
COMPRIMIR_ARCHIVOS('Comprimir', Icon(Icons.splitscreen_outlined)),
REEMPLAZAR_ARCHIVOS('Reemplazar', Icon(Icons.change_circle_outlined));
final String label;
final Icon icon;
const _ConvertirActionButtons(this.label, this.icon);
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:prueba_multimedia/modelo/seleccionables.dart';
class ArchivoWidget extends StatelessWidget {
final texto;
class SeleccionableWidget extends StatelessWidget {
final Seleccionable seleccionable;
const ArchivoWidget({super.key, this.texto});
const SeleccionableWidget({super.key, required this.seleccionable});
// TODO: ACTUALIZAR PARA MOSTRAR FORMATOS Y CAMBIOS
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
const Icon(
Icons.file_open,
size: 30,
),
seleccionable.icono,
const SizedBox(width: 10,),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
texto,
seleccionable.nombre,
style: Theme.of(context).textTheme.bodyLarge
),
Text(
texto,
style: Theme.of(context).textTheme.bodySmall
),
],
)
),
if (seleccionable is Carpeta)
IconButton(onPressed: () {}, icon: Icon(Icons.menu)),
IconButton(
icon: Icon(Icons.edit),
icon: const Icon(Icons.edit),
onPressed: () {},
)
],
......
......@@ -5,6 +5,8 @@
import FlutterMacOS
import Foundation
import file_picker
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
}
......@@ -41,6 +41,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.1"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
url: "https://pub.dev"
source: hosted
version: "0.3.4+2"
cupertino_icons:
dependency: "direct main"
description:
......@@ -57,6 +65,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.2"
ffi:
dependency: transitive
description:
name: ffi
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
file_picker:
dependency: "direct main"
description:
name: file_picker
sha256: "7423298f08f6fc8cce05792bae329f9a93653fc9c08712831b1a55540127995d"
url: "https://pub.dev"
source: hosted
version: "9.0.2"
flutter:
dependency: "direct main"
description: flutter
......@@ -70,11 +94,24 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "5a1e6fb2c0561958d7e4c33574674bda7b77caaca7a33b758876956f2902eea3"
url: "https://pub.dev"
source: hosted
version: "2.0.27"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
leak_tracker:
dependency: transitive
description:
......@@ -147,6 +184,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.1"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
provider:
dependency: "direct main"
description:
......@@ -224,6 +269,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "14.3.1"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
win32:
dependency: transitive
description:
name: win32
sha256: b89e6e24d1454e149ab20fbb225af58660f0c0bf4475544650700d8e2da54aef
url: "https://pub.dev"
source: hosted
version: "5.11.0"
sdks:
dart: ">=3.7.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
flutter: ">=3.27.0"
......@@ -35,6 +35,7 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
provider: ^6.1.2
file_picker: ^9.0.2
dev_dependencies:
flutter_test:
......
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