import 'dart:async'; import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:touch_demonstrator/model/buttonData.dart'; import 'package:touch_demonstrator/model/touchData.dart'; import 'package:touch_demonstrator/model/vibrate.dart'; import 'package:touch_demonstrator/src/blocs/BlocProvider.dart'; import 'package:flare_flutter/flare_actor.dart'; import 'dart:core'; import 'package:vibrate/vibrate.dart'; Duration durationSensorAnimation = Duration(milliseconds: 200); class Battery extends StatelessWidget { @override Widget build(BuildContext context) { final bBloc = BlocProvider.of(context).bluetoothBlocGetter; return Positioned( bottom: 0, left: 0, child: StreamBuilder( stream: bBloc.batteryValue$, initialData: null, builder: (BuildContext context, AsyncSnapshot snapshotBattery) { return Container( padding: EdgeInsets.all(0.0), color: Colors.transparent, child: Padding( padding: const EdgeInsets.all(3.0), child: (snapshotBattery.data == null) ? Container() : Container( padding: EdgeInsets.all(4.0), decoration: BoxDecoration( color: Theme.of(context).accentColor, borderRadius: new BorderRadius.all( const Radius.circular(3.0))), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Icon( Icons.battery_full, color: Colors.white, ), Text( '${snapshotBattery.data.toString()}%', style: TextStyle(color: Colors.white), ), ], ), ), ), ); }), ); } } class Sensor extends StatefulWidget { @override SensorState createState() { return SensorState(); } } class SensorState extends State { static var animationDisabled = false; static var firstTimeDidChangeDependencies = true; static var canVibrate = false; _drawButtons( BuildContext context, double _heightSensor, double _widthSensor) { final bBloc = BlocProvider.of(context).bluetoothBlocGetter; final _buttonsPosY = [0.74 * _heightSensor, 0.57 * _heightSensor, 0.39 * _heightSensor]; final _buttonsPosX = 0.074 * _widthSensor; final _buttonWidth = 0.08 * _widthSensor; return StreamBuilder>( stream: bBloc.buttonsState$, initialData: [false, false, false], builder: (BuildContext context, AsyncSnapshot> snapshotButtons) { if (snapshotButtons.hasData) { return Stack( children: [ _singleButton(0, _buttonsPosX, _buttonsPosY[0], _buttonWidth, snapshotButtons.data[0]), _singleButton(1, _buttonsPosX, _buttonsPosY[1], _buttonWidth, snapshotButtons.data[1]), _singleButton(2, _buttonsPosX, _buttonsPosY[2], _buttonWidth, snapshotButtons.data[2]) ], ); } else return Container(); }); } _singleButton(int buttonNumber, double _buttonPosX, double _buttonPosY, double _widthButton, bool _isPressed) { ///draws a button of Type home(0), multitasking(1) or back(2). assert(buttonNumber >= 0 && buttonNumber <= 2); Color buttonColor; _isPressed ? buttonColor = Theme.of(context).primaryColor : buttonColor = Colors.white; if (buttonNumber >= 3) return Container(); return AnimatedPositioned( duration: durationSensorAnimation, left: _buttonPosX, bottom: _buttonPosY, child: Image( image: AssetImage('assets/buttons/button$buttonNumber.png'), color: buttonColor, width: _widthButton, ), ); } _notConnectedText() { return Positioned.fill( child: Stack( children: [ BackdropFilter( filter: ImageFilter.blur( sigmaX: 0.3, sigmaY: 0.3, ), child: Container(color: Colors.black.withOpacity(0)), ), Center( child: Container( padding: EdgeInsets.all(15.0), decoration: BoxDecoration( color: Colors.black45, shape: BoxShape.rectangle, borderRadius: BorderRadius.circular(8.0), ), child: Text( 'Not connected to a device', style: TextStyle(color: Colors.white), ))), ], )); } _successAnimation() { if (animationDisabled) { print('no animation'); return Container(); } print('animation'); return Container( padding: EdgeInsets.all(80.0), child: Center( child: FlareActor( 'assets/Animations/success_hide.flr', animation: 'success', fit: BoxFit.contain, alignment: Alignment.center, isPaused: false, ), ), ); } _middleSensor( BuildContext context, double _heightSensor, double _widthSensor) { final bBloc = BlocProvider.of(context).bluetoothBlocGetter; final _marginRight = _widthSensor * 0.168; final _marginLeft = _widthSensor * 0.18; return StreamBuilder>( stream: bBloc.getTouchesToVisualise$, initialData: [TouchData(5, 0, 1200, 1200)], builder: (BuildContext context, AsyncSnapshot> snapshotTouches) { if (snapshotTouches.hasData) { return Container( height: _heightSensor, width: _widthSensor, margin: EdgeInsets.only(left: _marginLeft, right: _marginRight), child: MiddleOfSensor( snapshotTouches.data, context, _heightSensor, _widthSensor), // CustomPaint( foregroundPainter:TouchPainter(context, snapshotTouches.data), ), ); } else { return Container(); } }); } _sensorImage(){ return BoxDecoration( image: DecorationImage( image: ExactAssetImage('assets/sensor.png'), fit: BoxFit.fitHeight), ); } @override Widget build(BuildContext context) { final bBloc = BlocProvider.of(context).bluetoothBlocGetter; final widthOfScreen = MediaQuery.of(context).size.width; final heightOfScreen = MediaQuery.of(context).size.height; var _heightSensorConnected; var _widthSensorConnected; var _heightSensorDisconnected; var _widthSensorDisconnected; (heightOfScreen > widthOfScreen) ? _widthSensorConnected = _heightSensorConnected = widthOfScreen * 0.8 : _heightSensorConnected = _widthSensorConnected = heightOfScreen * 0.8; _heightSensorDisconnected = _heightSensorConnected * 0.8; _widthSensorDisconnected = _widthSensorConnected * 0.8; return StreamBuilder( stream: bBloc.isConnected$, initialData: false, builder: (BuildContext context, AsyncSnapshot snapshotIsConnected) { if (snapshotIsConnected.hasData) { return Hero( tag: 'touchpadHero', child: AnimatedOpacity( duration: durationSensorAnimation, opacity: snapshotIsConnected.data ? 1.0 : 0.8, child: Container( // margin: EdgeInsets.only(left: 10.0, right: 10.0), width: _widthSensorConnected, height: _heightSensorConnected, child: Center( child: AnimatedContainer( duration: durationSensorAnimation, decoration: _sensorImage(), height: snapshotIsConnected.data ? _heightSensorConnected : _heightSensorDisconnected, width: snapshotIsConnected.data ? _widthSensorConnected : _widthSensorDisconnected, child: Stack( children: [ _middleSensor(context, _heightSensorConnected, _widthSensorConnected), snapshotIsConnected.data ? _drawButtons( context, _heightSensorConnected, _widthSensorConnected) : _drawButtons(context, _heightSensorDisconnected, _widthSensorDisconnected), DrawSlider(context, _widthSensorConnected, _heightSensorConnected), snapshotIsConnected.data ? _successAnimation() : _notConnectedText(), Battery(), ], ), ), ), ), ), ); } else return Container(); }); } void _disableSuccessAnimation(bool isConnected) { // If already are connected to a device -> prevent success animation from showing again. // This would happens if you change orientation of device. if (isConnected) { vibrateDelayed(FeedbackType.success,Duration(milliseconds: 50)); Future.delayed(Duration(seconds: 1), () { animationDisabled = true; }); } else { // Activate success animation for next connection. animationDisabled = false; } } void _vibrateDependingOnButtonEvent(buttonsState buttonState) { (buttonState == buttonsState.pressed) ? vibrate(FeedbackType.selection) : vibrate(FeedbackType.impact); } @override void didChangeDependencies() { final bBloc = BlocProvider.of(context).bluetoothBlocGetter; print('didchangedependencies'); if (firstTimeDidChangeDependencies) { bBloc.isConnected$.listen((data) => _disableSuccessAnimation(data)); bBloc.vibrationButton$ .listen((data) => _vibrateDependingOnButtonEvent(data)); firstTimeDidChangeDependencies = false; } super.didChangeDependencies(); } } class DrawSlider extends StatefulWidget { final BuildContext context; final double _heightSensor, _widthSensor; DrawSlider(this.context, this._heightSensor, this._widthSensor); @override _DrawSliderState createState() => _DrawSliderState(); } class _DrawSliderState extends State with SingleTickerProviderStateMixin { AnimationController _controller; Animation _animation; static const Duration sliderAnimationMoveDuration = Duration(milliseconds: 1); static int lastPercent = 101; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: Duration(milliseconds: 300), ); _animation = Tween( begin: 1.0, end: 0.0, ).animate(_controller); _controller.reverse(); } @override void dispose() { _controller.dispose(); _controller = null; super.dispose(); } _sliderFadeAwayAnimation() { if (_controller != null) { _controller.reset(); } Future.delayed(Duration(seconds: 1), () { if (_controller != null) { _controller.forward(); } }); } _drawSlider(double percent) { var _sliderPosY = 0.12 * widget._heightSensor + percent * widget._heightSensor * 0.01 * 0.78; var _sliderPosX = widget._widthSensor * 0.83; return Positioned( left: _sliderPosX, bottom: _sliderPosY, child: Image( color: Theme.of(context).primaryColor, image: AssetImage('assets/slider_4.png'), width: widget._widthSensor * 0.15, ), ); } _drawSliderText(int percent) { var _sliderTextPosX = widget._widthSensor * 0.73; var _sliderTextPosY = 0.13 * widget._heightSensor + percent * widget._heightSensor * 0.01 * 0.78; // return AnimatedPositioned( return Positioned( // duration: sliderAnimationMoveDuration, left: _sliderTextPosX, bottom: _sliderTextPosY, child: Container( width: 25.0, child: Text( '$percent', textAlign: TextAlign.end, style: TextStyle(color: Colors.white), ), ), ); } @override Widget build(BuildContext context) { final bBloc = BlocProvider.of(context).bluetoothBlocGetter; // print('slider build'); return StreamBuilder( stream: bBloc.slider$, builder: (BuildContext context, AsyncSnapshot sliderSnapshot) { if (sliderSnapshot.hasData) { if (sliderSnapshot.data <= 100 && sliderSnapshot.data >= 0) { if (lastPercent != sliderSnapshot.data) _sliderFadeAwayAnimation(); lastPercent = sliderSnapshot.data; print('Slider: ${sliderSnapshot.data}'); print(''); return FadeTransition( opacity: _animation, child: Stack( children: [ _drawSlider(sliderSnapshot.data.toDouble()), _drawSliderText(lastPercent), ], ), ); } else return Container(); } else return Container(); }); } } class MiddleOfSensor extends StatefulWidget { final List touchCollection; final BuildContext context; final double _heightSensor; final double _widthSensor; MiddleOfSensor(this.touchCollection, this.context, this._heightSensor, this._widthSensor); @override _MiddleOfSensorState createState() => _MiddleOfSensorState(); } class _MiddleOfSensorState extends State { Duration durationTouchPointChange = Duration(microseconds: 200); static const leftLine = 100.0; static const rightLine = 900.0; static const maxSize = rightLine - leftLine; static const yMaxSize = 1023; static const fingerSize = 25.0; static const List _fingerColors = [ Colors.lightBlue, Colors.green, Colors.deepPurple, Colors.deepOrangeAccent, Colors.pink, ]; Widget _buildMiddleOfSensor(List _lastFingerTouchDataPoints) { Widget _touchPoint(TouchData item, BoxConstraints constraints) { var _height = constraints.constrainHeight(); var _width = constraints.constrainWidth(); var _xCircle = _width * (item.x.toDouble() - leftLine) / maxSize; var _yCircle = 0.04 * _height + 0.84 * _height * item.y.toDouble() / yMaxSize; Text _buildTouchPointText = Text( '${item.fingerNumber.toString()}', style: TextStyle(fontSize: 20, color: Colors.white), ); return AnimatedPositioned( // return Positioned( duration: durationTouchPointChange, left: _xCircle - fingerSize / 2, top: _yCircle - fingerSize / 2, child: Container( width: fingerSize, height: fingerSize, decoration: BoxDecoration( color: _fingerColors[item.fingerNumber], shape: BoxShape.circle, ), child: Center( child: _buildTouchPointText)) ); } return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return Stack( children: _lastFingerTouchDataPoints .where((TouchData item) => item.event == 4 || item.event == 1) .where((TouchData item) => item.x < 1200 && item.y < 1200) .where((TouchData item) => item.fingerNumber < 5) .map((TouchData item) => _touchPoint(item, constraints)) .toList()); }); } @override Widget build(BuildContext context) { return _buildMiddleOfSensor(widget.touchCollection); } } /*class TouchPainter extends CustomPainter { /// Draws Slider or touches in the middle of the touch pad. List touchCollection; BuildContext context; double width; TouchPainter(this.context, this.touchCollection); static List _colorOfTouches = [ Colors.lightBlue, Colors.green, Colors.deepPurple, Colors.deepOrangeAccent, Colors.pink, ]; @override void paint(Canvas canvas, Size size) { double leftLine = 80.0; double rightLine = 900.0; double maxSize = rightLine - leftLine; double xCircle, xText; double yCircle, yText; Offset offsetCircle, offsetText; void _drawCircle(int fingerNumber, Offset offset, Color v) { // double xCircle; // double yCircle; // xCircle = x.toDouble() / maxSize * size.width; // yCircle = y.toDouble() / 1100 * size.height; Paint line = Paint() ..color = v ..strokeCap = StrokeCap.round ..style = PaintingStyle.fill ..strokeWidth = 15.0; // print("C: $xCircle / ${size.width}"); canvas.drawCircle(offset, 16.0, line); } void _drawPercent(int fingerNumber, Offset offset){ TextSpan span = TextSpan(style: TextStyle(color: Colors.white, fontSize: 20), text: '$fingerNumber'); TextPainter tp = TextPainter(text: span, textAlign: TextAlign.right, textDirection: TextDirection.ltr); tp.layout(); tp.paint(canvas, offset); } touchCollection.forEach((k) { // print("${k.f} ${k.e} ${k.x} ${k.y}"); if (k.x >= leftLine && k.x <= rightLine && k.fingerNumber <=4) { switch (k.event) { case 5: break; case 4: continue move; move: case 1: { xCircle = (k.x.toDouble() - leftLine) / maxSize * size.width; yCircle = k.y.toDouble() / 1100 * size.height; print("Incoming (${k.x.round().toString()}|${k.y.round().toString()})"); xText = (k.x.toDouble() - leftLine - 27.0) / maxSize * size.width; yText = (k.y.toDouble() - 44.0) / 1100 * size.height; offsetCircle = Offset(xCircle, yCircle ); offsetText = Offset(xText, yText); _drawCircle(k.fingerNumber, offsetCircle, _colorOfTouches[k.fingerNumber.toInt()]); _drawPercent(k.fingerNumber, offsetText); break; } default: { print("Data is Corrupt"); break; } } } }); } @override bool shouldRepaint(CustomPainter oldDelegate) { return true; } }*/