Commit 0c50f171 by Diego Pérez Peña

Fusión con rama AñadirArchivo

parents 269a0fce d9c84e11
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'paginas/pagina_principal.dart'; import 'paginas/pagina_principal.dart';
import 'package:prueba_multimedia/modelo/lista_seleccionables.dart';
class ConvertexPrototipoApp extends StatelessWidget { class ConvertexPrototipoApp extends StatelessWidget {
const ConvertexPrototipoApp({super.key}); const ConvertexPrototipoApp({super.key});
...@@ -18,7 +19,12 @@ class ConvertexPrototipoApp extends StatelessWidget { ...@@ -18,7 +19,12 @@ class ConvertexPrototipoApp extends StatelessWidget {
) )
), ),
themeMode: ThemeMode.system, 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:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:prueba_multimedia/widget/convertex_fab_bar.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_llena.dart';
import 'pagina_principal_vacia.dart'; import 'pagina_principal_vacia.dart';
import 'dart:math';
class PaginaPrincipal extends StatefulWidget { class PaginaPrincipal extends StatelessWidget {
const PaginaPrincipal({super.key}); const PaginaPrincipal({super.key});
@override @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) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text('Convertex Prototipo'), title: Text('Convertex Prototipo'),
backgroundColor: Theme.of(context).primaryColor, backgroundColor: Theme.of(context).primaryColor,
actions: [ actions: [
IconButton(onPressed: () { IconButton(
showDialog<void>( icon: Icon(Icons.settings_outlined),
context: context, onPressed: () {
builder: (context) { showDialog(
return AlertDialog( context: context,
content: Text('Funcionalidad a implementar'), builder: (context) {
actions: [ return AlertDialog(
TextButton( content: Text('Funcionalidad a implementar'),
onPressed: () => Navigator.of(context).pop(), actions: [
child: const Text('CLOSE'), TextButton(
), onPressed: () => Navigator.of(context).pop(),
], child: const Text('CLOSE'),
); )
}, ]
); );
}, }
icon: Icon(Icons.settings_outlined)) );
], }
)
]
), ),
body: _construirCuerpo(context), body: _construirCuerpo(context),
floatingActionButton: Padding( floatingActionButton: Padding (
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: ConVertexFabBar() child: ConVertexFabBar()
), ),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
); );
} }
Widget _construirCuerpo(BuildContext context) { Widget _construirCuerpo(BuildContext context) {
if(false){ return Consumer<ListaSeleccionables>(
return PaginaPrincipalLlena(); builder: (context, manager, child) {
} if (manager.seleccionables.isNotEmpty) {
else{ return PaginaPrincipalLlena(listaSeleccionables: manager);
return PaginaPrincipalVacia(); } else {
} return PaginaPrincipalVacia();
}
}
);
} }
} }
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:prueba_multimedia/modelo/lista_seleccionables.dart';
import 'package:prueba_multimedia/widget/archivo_widget.dart'; import 'package:prueba_multimedia/modelo/seleccionables.dart';
import 'package:prueba_multimedia/widget/seleccionable_widget.dart';
class PaginaPrincipalLlena extends StatelessWidget { 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final seleccionables = listaSeleccionables.seleccionables;
return Padding( return Padding(
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
child: ListView.separated( child: ListView.separated(
itemCount: ejemplo.length, itemCount: seleccionables.length,
separatorBuilder: (context, index) { separatorBuilder: (context, index) {
return const SizedBox(height: 8.0); return const SizedBox(height: 8.0);
}, },
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ArchivoWidget(key: key, texto: ejemplo[index]); return SeleccionableWidget(seleccionable: seleccionables[index]);
}, },
), ),
); );
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ActionButton extends StatelessWidget { class ActionButton extends StatelessWidget {
final ActionButtonTypes tipoBoton;
final VoidCallback? onPressed; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context);
return Directionality( return Directionality(
textDirection: TextDirection.rtl, textDirection: TextDirection.rtl,
child: FilledButton.icon( child: FilledButton.icon(
onPressed: onPressed, onPressed: onPressed,
label: Text(label, textScaler: const TextScaler.linear(1.2)), label: Text(tipoBoton.label, textScaler: const TextScaler.linear(1.2)),
icon: icon 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: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'; import 'expandable_fab.dart';
class ConVertexFabBar extends StatefulWidget { class ConVertexFabBar extends StatefulWidget {
...@@ -23,6 +29,8 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> { ...@@ -23,6 +29,8 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final manager = Provider.of<ListaSeleccionables>(context, listen: false);
return SizedBox( return SizedBox(
width: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width,
height: (_agregarOpen || _convertirOpen)? MediaQuery.of(context).size.height : 56, height: (_agregarOpen || _convertirOpen)? MediaQuery.of(context).size.height : 56,
...@@ -30,7 +38,6 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> { ...@@ -30,7 +38,6 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
children: [ children: [
GestureDetector( GestureDetector(
onTap: () { onTap: () {
print('Hola');
setState(() { setState(() {
_agregarOpen = false; _agregarOpen = false;
_convertirOpen = false; _convertirOpen = false;
...@@ -44,7 +51,7 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> { ...@@ -44,7 +51,7 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [..._buildFABs()], children: [..._buildFABs(manager)],
), ),
) )
], ],
...@@ -52,10 +59,28 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> { ...@@ -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>[]; final toRet = <Widget>[];
// Convertir FAB // FAB con las opciones para convertir
toRet.add(Stack( toRet.add(Stack(
alignment: Alignment.bottomLeft, alignment: Alignment.bottomLeft,
children: [ children: [
...@@ -64,8 +89,8 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> { ...@@ -64,8 +89,8 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
distance: 60, distance: 60,
icon: Icon(Icons.label_important_outline), icon: Icon(Icons.label_important_outline),
label: 'Convertir', label: 'Convertir',
children: _loadConvertirActionButtons(context), key: _convertirKey,
key: _convertirKey children: _loadConvertirActionButtons(context)
), ),
SizedBox( SizedBox(
width: 130, width: 130,
...@@ -76,7 +101,6 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> { ...@@ -76,7 +101,6 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
_agregarOpen = false; _agregarOpen = false;
_convertirOpen = !_convertirOpen; _convertirOpen = !_convertirOpen;
}); });
print("Hola");
_agregarKey.currentState?.close(); _agregarKey.currentState?.close();
_convertirKey.currentState?.tap(); _convertirKey.currentState?.tap();
}, },
...@@ -85,15 +109,16 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> { ...@@ -85,15 +109,16 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
], ],
)); ));
// Agregar FAB
// FAB con las opciones para agregar archivos, carpetas, enlaces...
toRet.add(Stack( toRet.add(Stack(
alignment: Alignment.bottomRight, alignment: Alignment.bottomRight,
children: [ children: [
ExpandableFab( ExpandableFab(
distance: 60, distance: 60,
icon: Icon(Icons.add), icon: Icon(Icons.add),
children: _loadAgregarActionButtons(context),
key: _agregarKey, key: _agregarKey,
children: _loadAgregarActionButtons(context, manager),
), ),
SizedBox( SizedBox(
width: 56, width: 56,
...@@ -115,66 +140,39 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> { ...@@ -115,66 +140,39 @@ class _ConVertexFabBarState extends State<ConVertexFabBar> {
return toRet; return toRet;
} }
List<Widget> _loadAgregarActionButtons(BuildContext context){ // TODO: TERMINAR MÉTODOS
final buttons = <Widget>[]; List<Widget> _loadAgregarActionButtons(BuildContext context,
for(var element in _AgregarActionButtons.values){ ListaSeleccionables manager)
buttons.add(ActionButton( {
onPressed: () => _showPlaceholderAction(context), final buttons = <Widget>[
label: element.label, ActionButton(
icon: element.icon) tipoBoton: ActionButtonTypes.enlace,
); onPressed: () {}
} ),
return buttons; ActionButton(
} tipoBoton: ActionButtonTypes.carpeta,
onPressed: () async {
List<Widget> _loadConvertirActionButtons(BuildContext context){ String? path = await FilePicker.platform.getDirectoryPath();
final buttons = <Widget>[]; if (path != null) {
for(var element in _ConvertirActionButtons.values){ // TODO: AÑADIR POSIBILIDAD RECURSIVIDAD
buttons.add(ActionButton( var directory = Directory(path);
onPressed: () => _showPlaceholderAction(context), manager.addSeleccionable(Carpeta(directory, false));
label: element.label, }
icon: element.icon) }
); ),
} 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; 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:flutter/material.dart';
import 'package:prueba_multimedia/modelo/seleccionables.dart';
class ArchivoWidget extends StatelessWidget { class SeleccionableWidget extends StatelessWidget {
final texto; final Seleccionable seleccionable;
const ArchivoWidget({super.key, this.texto}); const SeleccionableWidget({super.key, required this.seleccionable});
// TODO: ACTUALIZAR PARA MOSTRAR FORMATOS Y CAMBIOS
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
children: <Widget>[ children: <Widget>[
const Icon( seleccionable.icono,
Icons.file_open,
size: 30,
),
const SizedBox(width: 10,), const SizedBox(width: 10,),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
texto, seleccionable.nombre,
style: Theme.of(context).textTheme.bodyLarge 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( IconButton(
icon: Icon(Icons.edit), icon: const Icon(Icons.edit),
onPressed: () {}, onPressed: () {},
) )
], ],
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import file_picker
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
} }
...@@ -41,6 +41,14 @@ packages: ...@@ -41,6 +41,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.19.1" 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: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
...@@ -57,6 +65,22 @@ packages: ...@@ -57,6 +65,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.2" 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: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
...@@ -70,11 +94,24 @@ packages: ...@@ -70,11 +94,24 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.0" 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: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
...@@ -147,6 +184,14 @@ packages: ...@@ -147,6 +184,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" 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: provider:
dependency: "direct main" dependency: "direct main"
description: description:
...@@ -224,6 +269,22 @@ packages: ...@@ -224,6 +269,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.3.1" 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: sdks:
dart: ">=3.7.0 <4.0.0" dart: ">=3.7.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54" flutter: ">=3.27.0"
...@@ -35,6 +35,7 @@ dependencies: ...@@ -35,6 +35,7 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
provider: ^6.1.2 provider: ^6.1.2
file_picker: ^9.0.2
dev_dependencies: dev_dependencies:
flutter_test: 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