Add All the files from the touchpad_demonstrator Flutter App which can be build for Android and iOS
This commit is contained in:
+69
@@ -0,0 +1,69 @@
|
||||
import 'package:touch_demonstrator/src/blocs/BlocProvider.dart';
|
||||
import 'package:touch_demonstrator/pages/DebugPage.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:touch_demonstrator/src/blocs/bluetoothBloc.dart';
|
||||
import 'package:touch_demonstrator/src/blocs/debugCounterBloc.dart';
|
||||
|
||||
Widget buildAppBarPageTouchPoints(BuildContext context) {
|
||||
void _pageRouteDebugView() {
|
||||
print('Debug button pressed');
|
||||
Navigator.push(
|
||||
context, MaterialPageRoute(builder: (context) => DebugView()));
|
||||
}
|
||||
|
||||
IconButton _buildDebugIconButton() {
|
||||
return IconButton(
|
||||
key: Key('debugButton'),
|
||||
icon: Icon(Icons.bug_report),
|
||||
onPressed: _pageRouteDebugView,
|
||||
);
|
||||
}
|
||||
|
||||
StreamBuilder<bool> titleAppBar() {
|
||||
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
|
||||
var appBarText;
|
||||
return StreamBuilder<bool>(
|
||||
stream: bBloc.isConnected$,
|
||||
initialData: false,
|
||||
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
snapshot.data
|
||||
? appBarText = 'Connected'
|
||||
: appBarText = 'Disconnected';
|
||||
return AnimatedSwitcher(
|
||||
duration: Duration(milliseconds: 300),
|
||||
transitionBuilder: (Widget child, Animation<double> opacity) {
|
||||
return FadeTransition(child: child, opacity: opacity);
|
||||
},
|
||||
child: Text(
|
||||
'$appBarText',
|
||||
key: ValueKey<String>(appBarText),
|
||||
textDirection: TextDirection.ltr,
|
||||
),
|
||||
);
|
||||
} else
|
||||
return Text('Disconnected');
|
||||
});
|
||||
}
|
||||
|
||||
List<Widget> actionsAppBar() {
|
||||
final debugBloc = BlocProvider.of(context).debugBlocGetter;
|
||||
return <Widget>[
|
||||
StreamBuilder<bool>(
|
||||
stream: debugBloc.debugEnabledStatus$,
|
||||
initialData: false,
|
||||
builder: (BuildContext context, AsyncSnapshot<bool> snapshotDebug) {
|
||||
if (snapshotDebug.hasData) {
|
||||
return snapshotDebug.data ? _buildDebugIconButton() : Container();
|
||||
} else
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
return AppBar(
|
||||
title: titleAppBar(),
|
||||
actions: actionsAppBar(),
|
||||
);
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_blue/flutter_blue.dart';
|
||||
import 'package:touch_demonstrator/src/blocs/BlocProvider.dart'; //bloc:
|
||||
import 'package:flare_flutter/flare_actor.dart';
|
||||
|
||||
class ConnectButtonWithAnimations extends StatefulWidget {
|
||||
@override
|
||||
ConnectButtonWithAnimationsState createState() {
|
||||
return new ConnectButtonWithAnimationsState();
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectButtonWithAnimationsState
|
||||
extends State<ConnectButtonWithAnimations>
|
||||
with SingleTickerProviderStateMixin {
|
||||
/// Builds Button if the app is not connected to a touch pad or animates search progress.
|
||||
Animation animation;
|
||||
AnimationController animationController;
|
||||
static const _heightContainer = 60.0;
|
||||
static const _widthContainer = 170.0;
|
||||
static const EdgeInsets _containerEdgeInset = EdgeInsets.all(8.0);
|
||||
static const durationOpacity = Duration(milliseconds: 150);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
|
||||
return Hero(
|
||||
tag: 'buttonHero',
|
||||
child: Container(
|
||||
margin: _containerEdgeInset,
|
||||
height: _heightContainer,
|
||||
width: _widthContainer,
|
||||
child: StreamBuilder<BluetoothState>(
|
||||
stream: bBloc.bluetoothState$,
|
||||
initialData: BluetoothState.on,
|
||||
builder: (_, AsyncSnapshot<BluetoothState> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
if (snapshot.data == BluetoothState.on) {
|
||||
// Bluetooth is on, button enabled.
|
||||
return StreamBuilder<bool>(
|
||||
stream: bBloc.isScanning$,
|
||||
initialData: false,
|
||||
builder: (_, AsyncSnapshot<bool> snapshotIsScanning) {
|
||||
if (snapshotIsScanning.hasData) {
|
||||
return AnimatedSwitcher(
|
||||
duration: durationOpacity,
|
||||
transitionBuilder:
|
||||
(Widget child, Animation<double> opacity) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: FadeTransition(
|
||||
child: child, opacity: opacity),
|
||||
);
|
||||
},
|
||||
child: (snapshotIsScanning.data)
|
||||
? ScanAnimation()
|
||||
: ConnectButton());
|
||||
} else
|
||||
return Container();
|
||||
});
|
||||
} else {
|
||||
// Bluetooth is off, button disabled.
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ScanAnimation extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlareActor(
|
||||
'assets/Animations/loading2.flr',
|
||||
animation: 'Loading',
|
||||
fit: BoxFit.contain,
|
||||
alignment: Alignment.center,
|
||||
isPaused: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var _primaryColor = Theme.of(context).primaryColor;
|
||||
var _buttonTextStyle = TextStyle(color: _primaryColor, fontSize: 20.0);
|
||||
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
|
||||
var _buttonText;
|
||||
var _buttonFunction;
|
||||
return StreamBuilder<bool>(
|
||||
stream: bBloc.isConnected$,
|
||||
initialData: false,
|
||||
builder: (_, AsyncSnapshot<bool> snapshotIsConnected) {
|
||||
if (snapshotIsConnected.hasData) {
|
||||
if (snapshotIsConnected.data) {
|
||||
_buttonText = Text(
|
||||
"Disconnect",
|
||||
style: _buttonTextStyle,
|
||||
key: Key('DisconnectButton'), // for integration test
|
||||
);
|
||||
_buttonFunction = () => bBloc.disconnect.add(null);
|
||||
} else {
|
||||
_buttonText = Text(
|
||||
"Connect",
|
||||
style: _buttonTextStyle,
|
||||
key: Key('ConnectButton'), // for integration test
|
||||
);
|
||||
_buttonFunction = () => bBloc.scan.add(null);
|
||||
}
|
||||
return Center(
|
||||
child: OutlineButton(
|
||||
key: Key('ConnectionButton'), // for integration test
|
||||
borderSide: BorderSide(color: _primaryColor),
|
||||
shape: BeveledRectangleBorder(),
|
||||
onPressed: _buttonFunction,
|
||||
child: _buttonText,
|
||||
),
|
||||
);
|
||||
} else
|
||||
return Container();
|
||||
});
|
||||
}
|
||||
}
|
||||
Executable
+65
@@ -0,0 +1,65 @@
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:touch_demonstrator/src/blocs/BlocProvider.dart'; //bloc:
|
||||
|
||||
class LogoWithText extends StatelessWidget {
|
||||
final _heightLogo;
|
||||
|
||||
LogoWithText(this._heightLogo);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
TextAboveLogo(),
|
||||
Logo(_heightLogo),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Logo extends StatelessWidget {
|
||||
final _heightLogo;
|
||||
|
||||
Logo(this._heightLogo);
|
||||
|
||||
_buildLogo() {
|
||||
// returns Logo of PolyIC
|
||||
return Container(
|
||||
key: Key('Logo'), // for integration test
|
||||
height: _heightLogo,
|
||||
child: Center(
|
||||
child: Image.asset(
|
||||
'assets/PolyIC/PolyIC_Logo.png',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final debugBloc = BlocProvider.of(context).debugBlocGetter;
|
||||
return Hero(
|
||||
tag: 'logoHero',
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: GestureDetector(
|
||||
onTap: () => debugBloc.debugCounterIncrement.add(null),
|
||||
// onLongPress: () => debugBloc.debugEnable.add(null),
|
||||
child: _buildLogo()),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TextAboveLogo extends StatelessWidget {
|
||||
/// Line of text above the logo that adds information to it.
|
||||
static const _textStyle = TextStyle(fontSize: 18);
|
||||
static const _buildText = Text('Touchpad Demonstrator by',
|
||||
textDirection: TextDirection.ltr, style: _textStyle);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildText;
|
||||
}
|
||||
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
import 'package:flutter_blue/flutter_blue.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:touch_demonstrator/src/blocs/BlocProvider.dart';
|
||||
|
||||
|
||||
class Modal {
|
||||
_buildScanResult(
|
||||
BuildContext context, Map<DeviceIdentifier, ScanResult> scanResults) {
|
||||
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
|
||||
return scanResults.values
|
||||
.where((result) => result.advertisementData.localName
|
||||
.contains('Touchpad Demonstrator'))
|
||||
.map((result) => ScanResultTile(
|
||||
result: result,
|
||||
onTapCallback: () {
|
||||
bBloc.connect.add(result);
|
||||
print('Connect to ${result.device.name} pressed!');
|
||||
Navigator.pop(context);
|
||||
}))
|
||||
.toList();
|
||||
}
|
||||
|
||||
_buildHeaderBottomSheet(BuildContext context) {
|
||||
return Container(
|
||||
color: Theme.of(context).primaryColor,
|
||||
child: ListTile(
|
||||
title: Center(
|
||||
child: Text(
|
||||
"CHOOSE TOUCHPAD",
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
connectBottomSheet(
|
||||
BuildContext context, Map<DeviceIdentifier, ScanResult> scanResults) {
|
||||
print('Modal Sheet - Result Count: ${scanResults.length}');
|
||||
var _bottomSheetElements = List<Widget>();
|
||||
_bottomSheetElements.add(_buildHeaderBottomSheet(context));
|
||||
_bottomSheetElements.addAll(_buildScanResult(context, scanResults));
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return Container(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(0.0),
|
||||
child: ListView(
|
||||
shrinkWrap: true, children: _bottomSheetElements)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ScanResultTile extends StatelessWidget {
|
||||
final ScanResult result;
|
||||
final VoidCallback onTapCallback;
|
||||
|
||||
const ScanResultTile({Key key, this.result, this.onTapCallback})
|
||||
: super(key: key);
|
||||
|
||||
_buildTitle(BuildContext context) {
|
||||
var textOfTile;
|
||||
(result.device.name.length > 0)
|
||||
? textOfTile = result.device.name
|
||||
: textOfTile = result.device.id.toString();
|
||||
|
||||
return Flexible(
|
||||
child: Container(
|
||||
child: Text(
|
||||
textOfTile,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
)));
|
||||
}
|
||||
|
||||
_buildConnectButton(BuildContext context) {
|
||||
return OutlineButton(
|
||||
borderSide: BorderSide(color: Theme.of(context).primaryColor),
|
||||
shape: BeveledRectangleBorder(),
|
||||
onPressed: (result.advertisementData.connectable) ? onTapCallback : null,
|
||||
child: Text('CONNECT'),
|
||||
); // Name of devices
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
_buildTitle(context),
|
||||
_buildConnectButton(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Executable
+582
@@ -0,0 +1,582 @@
|
||||
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<int>(
|
||||
stream: bBloc.batteryValue$,
|
||||
initialData: null,
|
||||
builder: (BuildContext context, AsyncSnapshot<int> 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: <Widget>[
|
||||
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<Sensor> {
|
||||
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<List<bool>>(
|
||||
stream: bBloc.buttonsState$,
|
||||
initialData: [false, false, false],
|
||||
builder:
|
||||
(BuildContext context, AsyncSnapshot<List<bool>> snapshotButtons) {
|
||||
if (snapshotButtons.hasData) {
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
_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: <Widget>[
|
||||
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<List<TouchData>>(
|
||||
stream: bBloc.getTouchesToVisualise$,
|
||||
initialData: [TouchData(5, 0, 1200, 1200)],
|
||||
builder: (BuildContext context,
|
||||
AsyncSnapshot<List<TouchData>> 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<bool> 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: <Widget>[
|
||||
|
||||
_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<DrawSlider>
|
||||
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<int>(
|
||||
stream: bBloc.slider$,
|
||||
builder: (BuildContext context, AsyncSnapshot<int> 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: <Widget>[
|
||||
_drawSlider(sliderSnapshot.data.toDouble()),
|
||||
_drawSliderText(lastPercent),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else
|
||||
return Container();
|
||||
} else
|
||||
return Container();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class MiddleOfSensor extends StatefulWidget {
|
||||
final List<TouchData> 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<MiddleOfSensor> {
|
||||
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<Color> _fingerColors = [
|
||||
Colors.lightBlue,
|
||||
Colors.green,
|
||||
Colors.deepPurple,
|
||||
Colors.deepOrangeAccent,
|
||||
Colors.pink,
|
||||
];
|
||||
|
||||
Widget _buildMiddleOfSensor(List<TouchData> _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<TouchData> touchCollection;
|
||||
BuildContext context;
|
||||
double width;
|
||||
|
||||
TouchPainter(this.context, this.touchCollection);
|
||||
static List<Color> _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;
|
||||
}
|
||||
}*/
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:touch_demonstrator/pages/SideBarContent.dart';
|
||||
|
||||
class LeftDrawer extends StatelessWidget {
|
||||
static const htmlPrivacyNotes = 'assets/html/privacy_notice.html';
|
||||
static const htmlImprint = 'assets/html/imprint.html';
|
||||
static const htmlTermsOfUse = 'assets/html/terms_of_use.html';
|
||||
static const htmlDisclaimer = 'assets/html/disclaimer.html';
|
||||
static const headerText = Text(
|
||||
'Touch Demonstrator App',
|
||||
textScaleFactor: 1.5,
|
||||
style: TextStyle(color: Colors.white),
|
||||
);
|
||||
|
||||
DrawerHeader _buildHeader(BuildContext context) {
|
||||
return DrawerHeader(
|
||||
key: Key('DrawerHeader'),
|
||||
child: headerText,
|
||||
decoration: BoxDecoration(color: Theme.of(context).primaryColor),
|
||||
);
|
||||
}
|
||||
|
||||
ListTile _buildTile(BuildContext context, String title, String html) {
|
||||
return ListTile(
|
||||
key: Key(title),
|
||||
title: Text(title),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) =>
|
||||
SideBarContent(title: title, html: html)));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Drawer(
|
||||
key: Key('Drawer'),
|
||||
child: ListView(
|
||||
padding: EdgeInsets.zero,
|
||||
children: <Widget>[
|
||||
_buildHeader(context),
|
||||
_buildTile(context, 'Privacy Notes', htmlPrivacyNotes),
|
||||
_buildTile(context, 'Imprint', htmlImprint),
|
||||
_buildTile(context, 'Terms of Use', htmlTermsOfUse),
|
||||
_buildTile(context, 'Disclaimer', htmlDisclaimer),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
//import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:touch_demonstrator/pages/PageTouchPoints.dart';
|
||||
import 'package:touch_demonstrator/src/blocs/BlocProvider.dart'; //bloc:
|
||||
import 'package:flutter/animation.dart';
|
||||
|
||||
class Button extends StatelessWidget {
|
||||
/// Button that navigates to the page 'PageTouchPoints'
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Hero(
|
||||
tag: 'SearchHero',
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: RaisedButton(
|
||||
color: Theme.of(context).accentColor,
|
||||
shape: StadiumBorder(),
|
||||
onPressed: () {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (context) => PageTouchPoints()));
|
||||
},
|
||||
child: Text(
|
||||
'Search and Connect Touchpad',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TouchPad extends StatelessWidget {
|
||||
/// Image of the touch pad
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Image.asset('assets/trackpad.png'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ReusableWidgets {
|
||||
getAppBar(String title) {
|
||||
return AppBar(
|
||||
title: Text(title),
|
||||
);
|
||||
}
|
||||
|
||||
getAppBar2(BuildContext context) {
|
||||
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
|
||||
String appBarText;
|
||||
|
||||
return AppBar(
|
||||
title: StreamBuilder<bool>(
|
||||
stream: bBloc.isConnected$,
|
||||
initialData: false,
|
||||
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
if (snapshot.data) {
|
||||
appBarText = 'Connected';
|
||||
} else {
|
||||
// _showSnackbar('Disconnected');
|
||||
// return Text('Disconnected');
|
||||
appBarText = 'Disconnected';
|
||||
}
|
||||
return AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 350),
|
||||
transitionBuilder: (Widget child, Animation<double> opacity) {
|
||||
return FadeTransition(child: child, opacity: opacity);
|
||||
},
|
||||
child: new Text(
|
||||
'$appBarText',
|
||||
// Must have this key to build a unique widget when _count changes.
|
||||
key: new ValueKey<String>(appBarText),
|
||||
textScaleFactor: 1.0,
|
||||
|
||||
textDirection: TextDirection.ltr,
|
||||
),
|
||||
);
|
||||
} else
|
||||
return Text('Disconnected');
|
||||
}),
|
||||
// actions: <Widget>[
|
||||
// enableDebug ?IconButton(
|
||||
// icon: Icon(Icons.build),
|
||||
// onPressed: () {
|
||||
// print('pressed');
|
||||
// Navigator.push(
|
||||
// context, MaterialPageRoute(builder: (context) => DebugView()));
|
||||
// },
|
||||
// ):Container(),
|
||||
// ],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user