Floating Action Buttons implementados, falta que se cierren al pulsar en cualquier otro sitio

parent d0951d38
...@@ -32,6 +32,8 @@ migrate_working_dir/ ...@@ -32,6 +32,8 @@ migrate_working_dir/
.pub-cache/ .pub-cache/
.pub/ .pub/
/build/ /build/
*generated_plugin*
*GeneratedPluginRegistrant*
# Symbolication related # Symbolication related
app.*.symbols app.*.symbols
......
import 'package:flutter/material.dart';
class ActionButton extends StatelessWidget {
final VoidCallback? onPressed;
final String label;
final Icon icon;
const ActionButton({super.key, this.onPressed, required this.label, required this.icon});
@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
),
);
}
}
import 'package:flutter/material.dart';
import 'package:prueba_multimedia/action_button.dart';
import 'package:prueba_multimedia/expandable_fab.dart';
class AgregarArchivoExpandableFab extends StatelessWidget {
const AgregarArchivoExpandableFab({super.key});
@override
Widget build(BuildContext context) {
return ExpandableFab(
distance: 60,
icon: Icon(Icons.add),
children: loadActionButtons(context)
);
}
void _showAction(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'),
),
],
);
},
);
}
List<Widget> loadActionButtons(BuildContext context){
final buttons = <Widget>[];
for(var element in _ActionButtons.values){
buttons.add(ActionButton(
onPressed: () => _showAction(context),
label: element.label,
icon: element.icon)
);
}
return buttons;
}
}
enum _ActionButtons{
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 _ActionButtons(this.label, this.icon);
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:prueba_multimedia/action_button.dart';
import 'package:prueba_multimedia/expandable_fab.dart';
class ConvertirExpandableFab extends StatelessWidget {
const ConvertirExpandableFab({super.key});
@override
Widget build(BuildContext context) {
return ExpandableFab(
invert: true,
distance: 60,
icon: Icon(Icons.label_important_outline),
label: 'Convertir',
children: loadActionButtons(context)
);
}
void _showAction(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'),
),
],
);
},
);
}
List<Widget> loadActionButtons(BuildContext context){
final buttons = <Widget>[];
for(var element in _ActionButtons.values){
buttons.add(ActionButton(
onPressed: () => _showAction(context),
label: element.label,
icon: element.icon)
);
}
return buttons;
}
}
enum _ActionButtons{
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 _ActionButtons(this.label, this.icon);
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'dart:math';
@immutable
class ExpandableFab extends StatefulWidget {
final bool? initialOpen;
final bool invert;
final Icon icon;
final String? label;
final double distance;
final List<Widget> children;
const ExpandableFab({
super.key,
this.initialOpen,
this.invert = false,
required this.icon,
this.label,
required this.distance,
required this.children
});
@override
State<ExpandableFab> createState() => _ExpandableFabState();
}
class _ExpandableFabState extends State<ExpandableFab>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<double> _expandAnimation;
bool _open = false;
@override
void initState() {
super.initState();
_open = widget.initialOpen ?? false;
_controller = AnimationController(
value: _open? 1.0 : 0.0,
duration: const Duration(milliseconds: 250),
vsync: this
);
_expandAnimation = CurvedAnimation(
curve: Curves.fastOutSlowIn,
reverseCurve: Curves.easeOutQuad,
parent: _controller
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _toggle(){
setState(() {
_open = !_open;
if(_open){
_controller.forward();
}
else{
_controller.reverse();
}
});
}
@override
Widget build(BuildContext context) {
return Container(
width: (widget.label != null || _open)? 170 : 56,
height: _open? 56 + (widget.distance*widget.children.length) : 56,
child: SizedBox.expand(
child: Stack(
alignment: widget.invert? Alignment.bottomLeft : Alignment.bottomRight,
clipBehavior: Clip.none,
children: [
_buildTapToCloseFab(),
..._buildExpandingActionButtons(),
_buildTapToOpenFab()]
),
),
);
}
Widget _buildTapToCloseFab() {
return SizedBox(
width: 56,
height: 56,
child: Center(
child: Material(
shape: const RoundedRectangleBorder(),
clipBehavior: Clip.antiAlias,
elevation: 4,
child: InkWell(
onTap: _toggle,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(Icons.close,
color: Theme.of(context).primaryColor
),
),
),
),
),
);
}
Widget _buildTapToOpenFab() {
return IgnorePointer(
ignoring: _open,
child: AnimatedContainer(
transformAlignment: Alignment.center,
transform: Matrix4.diagonal3Values(
_open? 0.7 : 1.0,
_open? 0.7 : 1.0,
1.0
),
duration: const Duration(milliseconds: 250),
curve: const Interval(0.0, 0.5, curve: Curves.easeOut),
child: AnimatedOpacity(
opacity: _open? 0.0 : 1.0,
curve: const Interval(0.0, 0.5, curve: Curves.easeInOut),
duration: const Duration(milliseconds: 250),
child: _loadFloatingActionButton()
),
),
);
}
Widget _loadFloatingActionButton(){
if(widget.label == null){
return FloatingActionButton(
onPressed: _toggle,
child: widget.icon,
);
}
return FloatingActionButton.extended(
onPressed: _toggle,
icon: widget.icon,
label: Text(
widget.label!,
textScaler: TextScaler.linear(1.2)
)
);
}
List<Widget> _buildExpandingActionButtons() {
final children = <Widget>[];
final count = widget.children.length;
final step = 90.0 / (count - 1);
for(var i = 0; i < count; i++){
children.add(
_ExpandingActionButton(
invert: widget.invert,
directionInDegrees: 90,
maxDistance: widget.distance * (i+1),
progress: _expandAnimation,
child: widget.children[i]
)
);
}
return children;
}
}
class _ExpandingActionButton extends StatelessWidget {
final bool invert;
final double directionInDegrees;
final double maxDistance;
final Animation<double> progress;
final Widget child;
const _ExpandingActionButton({
super.key,
this.invert = false,
required this.directionInDegrees,
required this.maxDistance,
required this.progress,
required this.child
});
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: progress,
builder: (context, child) {
final offset = Offset.fromDirection(
directionInDegrees * (pi / 180.0),
progress.value * maxDistance
);
return Positioned(
right: invert? null : 4.0 + offset.dx,
bottom: 4.0 + offset.dy,
//top: 0,
left: invert? 4.0 + offset.dx : null,
child: child!
);
},
child: FadeTransition(opacity: progress, child: child)
);
}
}
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:prueba_multimedia/agregar_archivo_expandable_fab.dart';
import 'package:prueba_multimedia/convertir_expandable_fab.dart';
class PaginaPrincipal extends StatelessWidget { class PaginaPrincipal extends StatelessWidget {
const PaginaPrincipal({super.key}); const PaginaPrincipal({super.key});
...@@ -21,37 +23,47 @@ class PaginaPrincipal extends StatelessWidget { ...@@ -21,37 +23,47 @@ class PaginaPrincipal extends StatelessWidget {
Widget _construirCuerpo(BuildContext context) { Widget _construirCuerpo(BuildContext context) {
return SafeArea( return SafeArea(
child: Column( child: Center(
mainAxisAlignment: MainAxisAlignment.center, child: Column(
children: [ mainAxisAlignment: MainAxisAlignment.center,
Text( crossAxisAlignment: CrossAxisAlignment.center,
'No hay archivos seleccionados', children: [
style: Theme.of(context).textTheme.titleLarge, Text(
), 'No hay archivos seleccionados',
const SizedBox(height: 16.0), style: Theme.of(context).textTheme.titleLarge,
Text( ),
'Pulsa el botón + para agregarlos', const SizedBox(height: 16.0),
style: Theme.of(context).textTheme.bodyMedium, Text(
) 'Pulsa el botón + para agregarlos',
], style: Theme.of(context).textTheme.bodyLarge,
)
],
),
) )
); );
} }
Widget contruirFBAs(BuildContext context) { Widget contruirFBAs(BuildContext context) {
return Padding( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(16.0),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
FloatingActionButton( /*FloatingActionButton.extended(
onPressed: () {}, onPressed: () {},
child: Icon(Icons.label_important_outline), icon: Icon(
), Icons.label_important_outline,
FloatingActionButton( size: 30.0,
onPressed: () {}, ),
child: Icon(Icons.add), label: Text(
) 'Convertir',
style: Theme.of(context).textTheme.bodyLarge,
textScaler: TextScaler.linear(1.2),
)
),*/
ConvertirExpandableFab(),
AgregarArchivoExpandableFab()
], ],
), ),
); );
......
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