diff --git a/lib/src/power_logger.dart b/lib/src/power_logger.dart index bff87f9..3201e55 100644 --- a/lib/src/power_logger.dart +++ b/lib/src/power_logger.dart @@ -2,6 +2,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:power_logger/power_logger.dart'; +import 'package:power_logger/src/widgets/logger_fab.dart'; class PowerLogger { /// debug tag @@ -19,91 +20,8 @@ class PowerLogger { if (debug) Overlay.of(context).insert(OverlayEntry( builder: (context) { - return LoggerFAB(); + return LoggerFab(); }, )); } } - -///Logger FAB -class LoggerFAB extends StatefulWidget { - LoggerFAB({Key key}) : super(key: key); - - @override - _LoggerFABState createState() => _LoggerFABState(); -} - -class _LoggerFABState extends State { - double _x = 50; - double _y = 70; - - double get screenWidth => MediaQuery.of(context).size.width; - double get screenHeight => MediaQuery.of(context).size.height; - bool _moving = false; - - bool showSubPage = false; - @override - Widget build(BuildContext context) { - return AnimatedPositioned( - curve: Curves.easeInOutCubic, - duration: _moving ? Duration.zero : Duration(milliseconds: 300), - left: _x - 25, - top: _y - 25, - child: AnimatedOpacity( - duration: Duration(milliseconds: 300), - curve: Curves.easeInOutCubic, - opacity: showSubPage ? 0 : 1, - child: GestureDetector( - onPanStart: (detail) { - _moving = true; - }, - onPanUpdate: (details) { - setState(() { - _x = details.globalPosition.dx; - _y = details.globalPosition.dy; - }); - }, - onPanEnd: (detail) { - if ((_x < screenWidth / 2)) { - _x = 50; - } else - _x = screenWidth - 50; - if (_y > screenHeight - 50) { - _y = screenHeight - 50; - } else if (_y < 50) _y = 50; - - _moving = false; - setState(() {}); - }, - onTap: showSubPage - ? null - : () async { - showSubPage = true; - await Navigator.push( - context, - MaterialPageRoute(builder: (context) => PowerLoggerView()), - ); - showSubPage = false; - }, - child: ClipOval( - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), - child: Container( - decoration: BoxDecoration( - color: Colors.blueAccent.withOpacity(0.4), - borderRadius: BorderRadius.circular(25), - ), - height: 50, - width: 50, - child: Icon( - Icons.code, - color: Colors.white70, - ), - ), - ), - ), - ), - ), - ); - } -} diff --git a/lib/src/widgets/logger_fab.dart b/lib/src/widgets/logger_fab.dart new file mode 100644 index 0000000..39d6a21 --- /dev/null +++ b/lib/src/widgets/logger_fab.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/physics.dart'; +import 'package:power_logger/src/power_logger_view.dart'; + +class LoggerFab extends StatefulWidget { + LoggerFab({Key key}) : super(key: key); + + @override + _LoggerFabState createState() => _LoggerFabState(); +} + +class _LoggerFabState extends State + with SingleTickerProviderStateMixin { + bool showSubPage = false; + AnimationController _animationController; + var _dragAlignment = Alignment.center; + Animation _animation; + final _spring = + const SpringDescription(mass: 15, stiffness: 1000, damping: 0.7); + + double _normalizeVelocity(Offset velocity, Size size) { + final normalizedVelocity = Offset( + velocity.dx / size.width, + velocity.dy / size.height, + ); + return -normalizedVelocity.distance; + } + + void _runAnimation(Offset velocity, Size size) { + Alignment calcAlignment = _dragAlignment; + if (_dragAlignment.x < 0) calcAlignment = Alignment(-0.9, calcAlignment.y); + if (_dragAlignment.x >= 0) calcAlignment = Alignment(0.9, calcAlignment.y); + if (_dragAlignment.y >= 0.9) + calcAlignment = Alignment(calcAlignment.x, 0.9); + if (_dragAlignment.y <= -0.9) + calcAlignment = Alignment(calcAlignment.x, -0.9); + _animation = _animationController.drive( + AlignmentTween( + begin: _dragAlignment, + end: calcAlignment, + ), + ); + + final simulation = + SpringSimulation(_spring, 0, 1, _normalizeVelocity(velocity, size)); + + _animationController.animateWith(simulation); + } + + @override + void initState() { + super.initState(); + _animationController = AnimationController.unbounded(vsync: this) + ..addListener(() => setState(() => _dragAlignment = _animation.value)); + } + + @override + void dispose() { + _animationController?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final size = MediaQuery.of(context).size; + return Align( + alignment: _dragAlignment, + child: AnimatedOpacity( + duration: Duration(milliseconds: 300), + opacity: showSubPage ? 0 : 1, + child: GestureDetector( + onPanStart: (details) => _animationController.stop(canceled: true), + onPanUpdate: (details) => setState(() => _dragAlignment += Alignment( + details.delta.dx / (size.width / 2), + details.delta.dy / (size.height / 2), + )), + onPanEnd: (details) => + _runAnimation(details.velocity.pixelsPerSecond, size), + onTap: showSubPage + ? null + : () async { + showSubPage = true; + await Navigator.push( + context, + MaterialPageRoute(builder: (context) => PowerLoggerView()), + ); + showSubPage = false; + }, + child: const _FabButton(), + ), + ), + ); + } +} + +class _FabButton extends StatelessWidget { + const _FabButton({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ClipOval( + child: Container( + decoration: BoxDecoration( + color: Colors.blueAccent.withOpacity(0.4), + borderRadius: BorderRadius.circular(25), + ), + height: 50, + width: 50, + child: Icon( + Icons.code, + color: Colors.white70, + ), + ), + ); + } +}