Comentando código y otros cambios

parent ea0518c7
...@@ -15,7 +15,6 @@ abstract class Conversor { ...@@ -15,7 +15,6 @@ abstract class Conversor {
ReturnCode? returnCode; ReturnCode? returnCode;
String nuevoPath = "$pathSalida/${archivo.nombre}.${archivo.formatoDestino!.name}"; String nuevoPath = "$pathSalida/${archivo.nombre}.${archivo.formatoDestino!.name}";
// TODO: REVISAR EL SACAR FOTOGRAMA. NO PARECE SALIR NADA
bool conversionVideoAOtro = bool conversionVideoAOtro =
archivo.formatoOriginal.tipoMultimedia == TipoMultimedia.video archivo.formatoOriginal.tipoMultimedia == TipoMultimedia.video
&& archivo.formatoDestino?.tipoMultimedia != TipoMultimedia.video; && archivo.formatoDestino?.tipoMultimedia != TipoMultimedia.video;
...@@ -55,7 +54,6 @@ abstract class Conversor { ...@@ -55,7 +54,6 @@ abstract class Conversor {
continue; continue;
} }
var campos = linea.split("="); var campos = linea.split("=");
// TODO: Chequear metadatos que falten
try { try {
metadatos.add( metadatos.add(
Metadato( Metadato(
......
...@@ -59,7 +59,6 @@ class Enlace extends Convertible { ...@@ -59,7 +59,6 @@ class Enlace extends Convertible {
nombre = nombre.substring(1, nombre.length-1); nombre = nombre.substring(1, nombre.length-1);
} }
// filename* encoding, habría que parsear en UTF8 // filename* encoding, habría que parsear en UTF8
// TODO: parsear en el caso de filename*
else { else {
nombre = nombre.substring(7); nombre = nombre.substring(7);
} }
......
...@@ -6,13 +6,17 @@ import 'package:permission_handler/permission_handler.dart'; ...@@ -6,13 +6,17 @@ import 'package:permission_handler/permission_handler.dart';
import 'package:prueba_multimedia/modelo/provider_ajustes.dart'; import 'package:prueba_multimedia/modelo/provider_ajustes.dart';
import 'package:prueba_multimedia/widgets/action_button.dart'; import 'package:prueba_multimedia/widgets/action_button.dart';
/// Página de Ajustes de la aplicación
class PaginaAjustes extends StatefulWidget { class PaginaAjustes extends StatefulWidget {
/// Las diferentes opciones del modo de conversión
static const opcionesModoConversion = <String>[ static const opcionesModoConversion = <String>[
"Preguntar siempre", "Copiar", "Comprimir" "Preguntar siempre", "Copiar", "Comprimir"
]; ];
/// Las diferentes opciones de la inclusión de subcarpetas
static const opcionesIncluirSubcarpetas = <String>[ static const opcionesIncluirSubcarpetas = <String>[
"Sí", "No" "Sí", "No"
]; ];
/// Las diferentes opciones de la selección de carpeta de salida
// Si hemos elegido una carpeta entonces se tendría que mostrar // Si hemos elegido una carpeta entonces se tendría que mostrar
static const opcionesCarpetaSalida = <String>[ static const opcionesCarpetaSalida = <String>[
"Elegir", "Preguntar siempre" "Elegir", "Preguntar siempre"
......
...@@ -23,8 +23,6 @@ class PaginaConfiguracionCarpeta extends StatefulWidget { ...@@ -23,8 +23,6 @@ class PaginaConfiguracionCarpeta extends StatefulWidget {
State<PaginaConfiguracionCarpeta> createState() => _PaginaConfiguracionCarpetaState(); State<PaginaConfiguracionCarpeta> createState() => _PaginaConfiguracionCarpetaState();
} }
// TODO: Se muestran formatos repetidos porque se consideran distintos
// al ser un de subcarpeta y otro no
class _PaginaConfiguracionCarpetaState extends State<PaginaConfiguracionCarpeta> { class _PaginaConfiguracionCarpetaState extends State<PaginaConfiguracionCarpeta> {
final Map<TipoMultimedia, List<Formato>> _formatos = {}; final Map<TipoMultimedia, List<Formato>> _formatos = {};
final Map<TipoMultimedia, bool?> _allOfType = {}; final Map<TipoMultimedia, bool?> _allOfType = {};
......
...@@ -136,7 +136,6 @@ class _PaginaConversionState extends State<PaginaConversion> ...@@ -136,7 +136,6 @@ class _PaginaConversionState extends State<PaginaConversion>
selected: elemento == _calidadActual, selected: elemento == _calidadActual,
shape: StadiumBorder(), shape: StadiumBorder(),
label: Text(elemento.texto), label: Text(elemento.texto),
// TODO: CAMBIAR CALIDAD FORMATO
onSelected: (_formatoConvertido != null && _formatoConvertido!.listCalidades.contains(elemento))? onSelected: (_formatoConvertido != null && _formatoConvertido!.listCalidades.contains(elemento))?
(selected) { setState((){_calidadActual = elemento;}); } : null, (selected) { setState((){_calidadActual = elemento;}); } : null,
); );
......
...@@ -6,6 +6,7 @@ import 'package:file_picker/file_picker.dart'; ...@@ -6,6 +6,7 @@ import 'package:file_picker/file_picker.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:prueba_multimedia/modelo/modelo.dart'; import 'package:prueba_multimedia/modelo/modelo.dart';
/// Clase que representa los botones de acción de la página principal
class ActionButton extends StatelessWidget { class ActionButton extends StatelessWidget {
final ActionButtonTypes tipoBoton; final ActionButtonTypes tipoBoton;
final ListaSeleccionables manager; final ListaSeleccionables manager;
...@@ -174,7 +175,6 @@ class ActionButton extends StatelessWidget { ...@@ -174,7 +175,6 @@ class ActionButton extends StatelessWidget {
} }
/// Igual que copiar pero guarda el resultado en un .zip /// Igual que copiar pero guarda el resultado en un .zip
/// TODO (RAFA): CREAR LOS ARCHIVOS TEMPORALES EN OTRO SITIO
void comprimirAction() async { void comprimirAction() async {
if(await _comprobacionesPreviasConversion(context)) { if(await _comprobacionesPreviasConversion(context)) {
// Averiguamos donde colocar los archivos de salida // Averiguamos donde colocar los archivos de salida
...@@ -216,7 +216,6 @@ class ActionButton extends StatelessWidget { ...@@ -216,7 +216,6 @@ class ActionButton extends StatelessWidget {
else if (sel is Convertible){ else if (sel is Convertible){
sel.convertir(directorioSalida); sel.convertir(directorioSalida);
File tempFile = await File( File tempFile = await File(
// TODO: Esto da bug con los enlaces por el nombre
"$directorioSalida/${sel.nombre}.${sel.formatoDestino?.name}" "$directorioSalida/${sel.nombre}.${sel.formatoDestino?.name}"
).create(); ).create();
resultsZip.add( resultsZip.add(
...@@ -272,7 +271,6 @@ class ActionButton extends StatelessWidget { ...@@ -272,7 +271,6 @@ class ActionButton extends StatelessWidget {
} }
/// Comprueba si tenemos permisos suficientes /// Comprueba si tenemos permisos suficientes
// TODO: Añadir aquí una notificación para avisar al usuario de si quiere realmente convertir una carpeta muy grande
Future<bool> _comprobacionesPreviasConversion(BuildContext context) async { Future<bool> _comprobacionesPreviasConversion(BuildContext context) async {
if(!await comprobacionPermisoArchivos(context)){ if(!await comprobacionPermisoArchivos(context)){
return false; return false;
......
...@@ -8,7 +8,9 @@ import 'dart:math'; ...@@ -8,7 +8,9 @@ import 'dart:math';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
import 'package:uuid/v1.dart'; import 'package:uuid/v1.dart';
/// Widget para mostrar carpetas en la lista de archivos de la página principal
class CarpetaWidget extends StatefulWidget { class CarpetaWidget extends StatefulWidget {
/// Índice de la carpeta en cuestión en la lista de seleccionables
final int indice; final int indice;
final Carpeta carpeta; final Carpeta carpeta;
final ListaSeleccionables lista; final ListaSeleccionables lista;
...@@ -24,16 +26,22 @@ class _CarpetaWidgetState extends State<CarpetaWidget> ...@@ -24,16 +26,22 @@ class _CarpetaWidgetState extends State<CarpetaWidget>
with SingleTickerProviderStateMixin with SingleTickerProviderStateMixin
{ {
static const double maxHeight = 50; static const double maxHeight = 50;
/// Controlador para la animación de apertura y cierre
late final AnimationController _controller; late final AnimationController _controller;
/// Archivos (o carpetas si es recursivo) que el usuario no ha eliminado /// Archivos (o carpetas si es recursivo) que el usuario no ha eliminado
/// de la lista y que coinciden con los formatos elegidos /// de la lista y que coinciden con los formatos elegidos
List<InfoFormato> seleccionados = []; List<InfoFormato> seleccionados = [];
/// Controla si el widget está abierto o cerrado
bool open = false; bool open = false;
/// Controla la rotación del icono de desplegar / colapsar
double rotAngle = 0; double rotAngle = 0;
/// Controla la altura de las entradas de formato
double height = 0; double height = 0;
/// Inicializa el controlador
@override @override
void initState() { void initState() {
super.initState();
_controller = AnimationController( _controller = AnimationController(
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
vsync: this vsync: this
...@@ -44,7 +52,13 @@ class _CarpetaWidgetState extends State<CarpetaWidget> ...@@ -44,7 +52,13 @@ class _CarpetaWidgetState extends State<CarpetaWidget>
height = maxHeight*_controller.value; height = maxHeight*_controller.value;
}); });
}); });
super.initState(); }
/// Libera los recursos del controlador
@override
void dispose() {
_controller.dispose();
super.dispose();
} }
@override @override
...@@ -74,6 +88,7 @@ class _CarpetaWidgetState extends State<CarpetaWidget> ...@@ -74,6 +88,7 @@ class _CarpetaWidgetState extends State<CarpetaWidget>
); );
} }
/// Construye la entrada de la carpeta
Widget buildMainContainer(){ Widget buildMainContainer(){
Carpeta carpeta = widget.carpeta; Carpeta carpeta = widget.carpeta;
// Para el plural indicando los formato(s) seleccionado(s) // Para el plural indicando los formato(s) seleccionado(s)
...@@ -164,6 +179,7 @@ class _CarpetaWidgetState extends State<CarpetaWidget> ...@@ -164,6 +179,7 @@ class _CarpetaWidgetState extends State<CarpetaWidget>
); );
} }
/// Construye las entradas de los formatos
List<Widget> buildSecondaryContainers(){ List<Widget> buildSecondaryContainers(){
Carpeta carpeta = widget.carpeta; Carpeta carpeta = widget.carpeta;
int index = 0; int index = 0;
...@@ -295,6 +311,7 @@ class _CarpetaWidgetState extends State<CarpetaWidget> ...@@ -295,6 +311,7 @@ class _CarpetaWidgetState extends State<CarpetaWidget>
} }
} }
/// Clase encargada de cortar las entradas de los formatos durante la animación
class _MyRectClipper extends CustomClipper<Rect> { class _MyRectClipper extends CustomClipper<Rect> {
final AnimationController _controller; final AnimationController _controller;
......
...@@ -11,9 +11,14 @@ import 'package:prueba_multimedia/paginas/pagina_configuracion.dart'; ...@@ -11,9 +11,14 @@ import 'package:prueba_multimedia/paginas/pagina_configuracion.dart';
import 'package:prueba_multimedia/paginas/pagina_configuracion_carpeta.dart'; import 'package:prueba_multimedia/paginas/pagina_configuracion_carpeta.dart';
import 'package:prueba_multimedia/widgets/widgets.dart'; import 'package:prueba_multimedia/widgets/widgets.dart';
/// Widget que representa la barra inferior de botones de acción de la pantalla
/// principal
class ConVertexFabBar extends StatefulWidget { class ConVertexFabBar extends StatefulWidget {
/// Si se permite o no convertir (o sea, si hay o no seleccionables en la lista)
final bool allowConversion; final bool allowConversion;
/// Acción que deben realizar los botones de conversión al finalizar
final VoidCallback onConvertSuccess; final VoidCallback onConvertSuccess;
/// Provider con los ajustes de la aplicación
final ProviderAjustes providerAjustes; final ProviderAjustes providerAjustes;
const ConVertexFabBar({ const ConVertexFabBar({
...@@ -28,17 +33,22 @@ class ConVertexFabBar extends StatefulWidget { ...@@ -28,17 +33,22 @@ class ConVertexFabBar extends StatefulWidget {
} }
class _ConVertexFabBarState extends State<ConVertexFabBar> { class _ConVertexFabBarState extends State<ConVertexFabBar> {
bool _convertirOpen = false; /// Si el botón de Agregar está expandido
bool _agregarOpen = false; bool _agregarOpen = false;
/// Si el botón de Convertir está expandido
bool _convertirOpen = false;
/// Clave que se utiliza para abrir y cerrar el botón de Agregar
final _agregarKey = GlobalKey<ExpandableFabState>(); final _agregarKey = GlobalKey<ExpandableFabState>();
/// Clave que se utiliza para abrir y cerrar el botón de Convertir
final _convertirKey = GlobalKey<ExpandableFabState>(); final _convertirKey = GlobalKey<ExpandableFabState>();
/// Los botones de acción dentro del botón expansible Agregar
static const agregarButtonTypes = <ActionButtonTypes>[ static const agregarButtonTypes = <ActionButtonTypes>[
ActionButtonTypes.archivo, ActionButtonTypes.archivo,
ActionButtonTypes.carpeta, ActionButtonTypes.carpeta,
ActionButtonTypes.enlace ActionButtonTypes.enlace
]; ];
/// Los botones de acción dentro del botón expansible Convertir
static const convertirButtonTypes= <ActionButtonTypes>[ static const convertirButtonTypes= <ActionButtonTypes>[
ActionButtonTypes.copiar, ActionButtonTypes.copiar,
ActionButtonTypes.comprimir ActionButtonTypes.comprimir
...@@ -78,21 +88,7 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> { ...@@ -78,21 +88,7 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
); );
} }
List<Widget> _loadConvertirActionButtons (BuildContext context, /// Construye los FABs expansibles
ListaSeleccionables manager)
{
return convertirButtonTypes.map((type) {
return ActionButton(
tipoBoton: type,
manager: manager,
context: context,
disabled: !(widget.allowConversion) && type.isConvertir,
onSuccess: widget.onConvertSuccess,
providerAjustes: widget.providerAjustes,
);
}).toList();
}
List<Widget> _buildFABs(ListaSeleccionables manager) { List<Widget> _buildFABs(ListaSeleccionables manager) {
final toRet = <Widget>[]; final toRet = <Widget>[];
...@@ -141,23 +137,13 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> { ...@@ -141,23 +137,13 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
disabledElevation: 0, disabledElevation: 0,
onPressed: (!widget.allowConversion)? null : onPressed: (!widget.allowConversion)? null :
() { () {
// Rafa: Esto es un poco un 'hack' switch(widget.providerAjustes.modoConversion){
case 1:
final tipoAccion = widget.providerAjustes.modoConversion == 1 convertirCopiar(manager);
? ActionButtonTypes.copiar break;
: ActionButtonTypes.comprimir; case 2:
convertirComprimir(manager);
final callback = ActionButton( break;
tipoBoton: tipoAccion,
manager: manager,
context: context,
disabled: !(widget.allowConversion) && tipoAccion.isConvertir,
onSuccess: widget.onConvertSuccess,
providerAjustes: widget.providerAjustes,
).getCallback();
if (callback != null) {
callback();
} }
}, },
)); ));
...@@ -195,6 +181,23 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> { ...@@ -195,6 +181,23 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
return toRet; return toRet;
} }
/// Construye los botones de acción dentro del botón expansible Convertir
List<Widget> _loadConvertirActionButtons (BuildContext context,
ListaSeleccionables manager)
{
return convertirButtonTypes.map((type) {
return ActionButton(
tipoBoton: type,
manager: manager,
context: context,
disabled: !(widget.allowConversion) && type.isConvertir,
onSuccess: widget.onConvertSuccess,
providerAjustes: widget.providerAjustes,
);
}).toList();
}
/// Construye los botones de acción dentro del botón expansible Agregar
List<Widget> _loadAgregarActionButtons(BuildContext context, List<Widget> _loadAgregarActionButtons(BuildContext context,
ListaSeleccionables manager) ListaSeleccionables manager)
{ {
...@@ -242,6 +245,110 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> { ...@@ -242,6 +245,110 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
}).toList(); }).toList();
} }
/// Acción que realiza el botón de convertir cuando el modo Copiar está
/// seleccionado en Ajustes
Future<void> convertirCopiar(ListaSeleccionables manager) async {
if(await ActionButton.comprobacionPermisoArchivos(context)){
String? directorioSalida;
if(widget.providerAjustes.carpetaSalida.isNotEmpty){
directorioSalida = widget.providerAjustes.carpetaSalida;
}
else{
directorioSalida = await FilePicker.platform.getDirectoryPath();
}
if(directorioSalida != null){
listenerActualizar(manager);
// Convertimos los archivos como tal
List<Archivo> archivos = manager.seleccionables.whereType<Archivo>().toList();
while(manager.seleccionables.isNotEmpty){
ElementoSeleccionable sel = manager.seleccionables.first;
if(sel is Carpeta){
for (var archivo in sel.elementosSeleccionados) {
Directory d = await Directory("$directorioSalida/${sel.nombre}").create();
Conversor.convertir(archivo, d.path);
}
}
else if (sel is Archivo){
Conversor.convertir(sel, directorioSalida);
}
manager.borraSeleccionable(0);
}
}
}
}
/// Acción que realiza el botón de convertir cuando el modo Comprimir está
/// seleccionado en Ajustes
Future<void> convertirComprimir(ListaSeleccionables manager) async {
if(await ActionButton.comprobacionPermisoArchivos(context)){
// Averiguamos donde colocar los archivos de salida
String? directorioSalida = widget.providerAjustes.carpetaSalida.isNotEmpty
? widget.providerAjustes.carpetaSalida
: await FilePicker.platform.getDirectoryPath();
if(directorioSalida != null) {
listenerActualizar(manager);
final now = DateTime.now();
final nombreZip = "ConVertex_"
"${now.day}-${now.month}-${now.year}_"
"${now.hour}-${now.minute}-${now.second}";
final zipFileEncoder = ZipFileEncoder();
zipFileEncoder.create("$directorioSalida/$nombreZip.zip");
final resultsZip = <Future<void>>[];
while(manager.seleccionables.isNotEmpty){
ElementoSeleccionable sel = manager.seleccionables.first;
// Conversion de carpetas
if(sel is Carpeta){
Directory d = await Directory("$directorioSalida/${sel.nombre}").create();
final resultsConversion = <Future<ReturnCode?>>[];
for (var archivo in sel.elementosSeleccionados) {
resultsConversion.add(Conversor.convertir(archivo, d.path));
}
// Esperamos a la conversión y añadimos a zip
for (final result in resultsConversion) {
await result;
}
resultsZip.add(
zipFileEncoder.addDirectory(d)..then((_) => d.delete(recursive: true))
);
}
// Conversion de archivos
else if (sel is Archivo){
// Esperamos a la conversión y añadimos a zip
await Conversor.convertir(sel, directorioSalida);
File tempFile = await File(
"$directorioSalida/${sel.nombre}.${sel.formatoDestino?.name}"
).create();
resultsZip.add(
zipFileEncoder.addFile(tempFile)..then((_) => tempFile.delete())
);
}
// Esperamos a que la compresión se termine y se borren los
// archivos temporales
if (manager.seleccionables.length == 1) {
for (final result in resultsZip) {
await result;
}
zipFileEncoder.closeSync();
}
// Esto hace que la barra de progreso suba
manager.borraSeleccionable(0);
}
}
}
}
/// Listener que se encarga de actualizar el progreso conforme se convierten
/// los archivos
Future<void> listenerActualizar(ListaSeleccionables manager) async { Future<void> listenerActualizar(ListaSeleccionables manager) async {
manager.iniciarConversion(); manager.iniciarConversion();
int initialSize = manager.seleccionables.length; int initialSize = manager.seleccionables.length;
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
/// Un pequeño Widget que contiene la barra de progreso de la conversión
class ConvertexProgressBar extends StatelessWidget { class ConvertexProgressBar extends StatelessWidget {
/// Progreso de la conversión
final int progress; final int progress;
/// Alto y ancho de la barra
final double? width; final double? width;
final double? height; final double? height;
/// Colores de fondo, borde y texto de la barra de progreso
final Color? background; final Color? background;
final Color? completedBackground; final Color? completedBackground;
final Color? borderColor; final Color? borderColor;
......
...@@ -4,12 +4,14 @@ import 'package:prueba_multimedia/modelo/provider_ajustes.dart'; ...@@ -4,12 +4,14 @@ import 'package:prueba_multimedia/modelo/provider_ajustes.dart';
import 'package:prueba_multimedia/paginas/paginas.dart'; import 'package:prueba_multimedia/paginas/paginas.dart';
import 'package:prueba_multimedia/modelo/modelo.dart'; import 'package:prueba_multimedia/modelo/modelo.dart';
/// Clase que representa la aplicación de ConVertex
class ConvertexPrototipoApp extends StatelessWidget { class ConvertexPrototipoApp extends StatelessWidget {
const ConvertexPrototipoApp({super.key}); const ConvertexPrototipoApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: 'ConVertex',
theme: ThemeData( theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple) colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple)
), ),
......
...@@ -2,7 +2,9 @@ import 'package:flutter/material.dart'; ...@@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
import 'package:prueba_multimedia/paginas/paginas.dart'; import 'package:prueba_multimedia/paginas/paginas.dart';
import 'package:prueba_multimedia/modelo/modelo.dart'; import 'package:prueba_multimedia/modelo/modelo.dart';
/// Widget para mostrar archivos y enlaces en la lista de archivos de la página principal
class ConvertibleWidget extends StatelessWidget { class ConvertibleWidget extends StatelessWidget {
/// Índice del archivo o enlace en cuestión en la lista de seleccionables
final int indice; final int indice;
final Convertible convertible; final Convertible convertible;
final ListaSeleccionables lista; final ListaSeleccionables lista;
...@@ -10,7 +12,6 @@ class ConvertibleWidget extends StatelessWidget { ...@@ -10,7 +12,6 @@ class ConvertibleWidget extends StatelessWidget {
const ConvertibleWidget({super.key, const ConvertibleWidget({super.key,
required this.indice, required this.convertible, required this.lista}); required this.indice, required this.convertible, required this.lista});
// TODO: ACTUALIZAR PARA MOSTRAR FORMATOS Y CAMBIOS
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final String nombre = convertible.nombre; final String nombre = convertible.nombre;
......
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
/// Widget que contiene un botón expansible, como los usados en la página
/// principal. Inspirados en [https://docs.flutter.dev/cookbook/effects/expandable-fab]
@immutable @immutable
class ExpandableFab extends StatefulWidget { class ExpandableFab extends StatefulWidget {
final bool? initialOpen; final bool? initialOpen;
/// Si es true, se alinea a la izquierda
final bool invert; final bool invert;
/// Icono del FAB
final Icon icon; final Icon icon;
/// Texto del FAB
final String? label; final String? label;
/// Distancia a la que se crean los hijos del FAB
final double distance; final double distance;
/// Lista de hijos del FAB
final List<Widget> children; final List<Widget> children;
const ExpandableFab({ const ExpandableFab({
...@@ -26,8 +33,10 @@ class ExpandableFab extends StatefulWidget { ...@@ -26,8 +33,10 @@ class ExpandableFab extends StatefulWidget {
class ExpandableFabState extends State<ExpandableFab> class ExpandableFabState extends State<ExpandableFab>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
/// Controladores de la animación
late final AnimationController _controller; late final AnimationController _controller;
late final Animation<double> _expandAnimation; late final Animation<double> _expandAnimation;
/// Si está abierto
bool _open = false; bool _open = false;
@override @override
...@@ -52,16 +61,19 @@ class ExpandableFabState extends State<ExpandableFab> ...@@ -52,16 +61,19 @@ class ExpandableFabState extends State<ExpandableFab>
super.dispose(); super.dispose();
} }
/// Simula una pulsación sobre el botón
void tap(){ void tap(){
_toggle(); _toggle();
} }
/// Simula el cierre del botón
void close(){ void close(){
if(_open){ if(_open){
_toggle(); _toggle();
} }
} }
/// Alterna el estado abierto-cerrado
void _toggle(){ void _toggle(){
setState(() { setState(() {
_open = !_open; _open = !_open;
...@@ -95,6 +107,7 @@ class ExpandableFabState extends State<ExpandableFab> ...@@ -95,6 +107,7 @@ class ExpandableFabState extends State<ExpandableFab>
); );
} }
/// Construye la X que contrae el FAB
Widget _buildTapToCloseFab() { Widget _buildTapToCloseFab() {
return SizedBox( return SizedBox(
width: 56, width: 56,
...@@ -118,6 +131,7 @@ class ExpandableFabState extends State<ExpandableFab> ...@@ -118,6 +131,7 @@ class ExpandableFabState extends State<ExpandableFab>
); );
} }
/// Construye el contenedor que expande las opciones
Widget _buildTapToOpenFab() { Widget _buildTapToOpenFab() {
return IgnorePointer( return IgnorePointer(
ignoring: _open, ignoring: _open,
...@@ -140,6 +154,7 @@ class ExpandableFabState extends State<ExpandableFab> ...@@ -140,6 +154,7 @@ class ExpandableFabState extends State<ExpandableFab>
); );
} }
/// Construye el botón del FAB que expande las opciones
Widget _loadFloatingActionButton(){ Widget _loadFloatingActionButton(){
if(widget.label == null){ if(widget.label == null){
return FloatingActionButton( return FloatingActionButton(
...@@ -158,6 +173,7 @@ class ExpandableFabState extends State<ExpandableFab> ...@@ -158,6 +173,7 @@ class ExpandableFabState extends State<ExpandableFab>
); );
} }
/// Construye las opciones del FAB
List<Widget> _buildExpandingActionButtons() { List<Widget> _buildExpandingActionButtons() {
final children = <Widget>[]; final children = <Widget>[];
final count = widget.children.length; final count = widget.children.length;
...@@ -178,6 +194,7 @@ class ExpandableFabState extends State<ExpandableFab> ...@@ -178,6 +194,7 @@ class ExpandableFabState extends State<ExpandableFab>
return children; return children;
} }
/// Obtiene la anchura que debería tener el botón
double _computeWidth() { double _computeWidth() {
if(_open){ if(_open){
if(widget.label != null) return 170.0; if(widget.label != null) return 170.0;
...@@ -190,6 +207,7 @@ class ExpandableFabState extends State<ExpandableFab> ...@@ -190,6 +207,7 @@ class ExpandableFabState extends State<ExpandableFab>
} }
} }
/// Clase que se utiliza para construir la animación del FAB abriéndose y cerrándose
class _ExpandingActionButton extends StatelessWidget { class _ExpandingActionButton extends StatelessWidget {
final bool invert; final bool invert;
final double directionInDegrees; final double directionInDegrees;
......
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