Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,4 @@ build/
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
/.vs
10 changes: 5 additions & 5 deletions lib/models/pad_button_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ class PadButtonItem {

/// [buttonText] optional parameter, the text to be displayed inside the
/// button. Omitted if [buttonImage] is set. Default value is empty string.
final String buttonText;
final String? buttonText;

/// [buttonImage] optional parameter, image which will be displayed inside
/// the button.
final Image buttonImage;
final Image? buttonImage;

/// [buttonIcon] optional parameter, image which will be displayed inside
/// the button.
final Icon buttonIcon;
final Icon? buttonIcon;

/// [backgroundColor] color of button in default state.
final Color backgroundColor;
Expand All @@ -34,12 +34,12 @@ class PadButtonItem {
final List<Gestures> supportedGestures;

const PadButtonItem({
@required this.index,
required this.index,
this.buttonText,
this.buttonImage,
this.buttonIcon,
this.backgroundColor = Colors.white54,
this.pressedColor = Colors.lightBlueAccent,
this.supportedGestures = const [Gestures.TAP],
}) : assert(index != null);
});
}
42 changes: 19 additions & 23 deletions lib/views/circle_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,25 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

class CircleView extends StatelessWidget {
final double size;
final double? size;

final Color color;
final Color? color;

final List<BoxShadow> boxShadow;
final List<BoxShadow>? boxShadow;

final Border border;
final Border? border;

final double opacity;
final Image? buttonImage;

final Image buttonImage;
final Icon? buttonIcon;

final Icon buttonIcon;

final String buttonText;
final String? buttonText;

CircleView({
this.size,
this.color = Colors.transparent,
this.boxShadow,
this.border,
this.opacity,
this.buttonImage,
this.buttonIcon,
this.buttonText,
Expand All @@ -34,19 +31,19 @@ class CircleView extends StatelessWidget {
return Container(
width: size,
height: size,
child: Center(
child: buttonIcon != null
? buttonIcon
: (buttonImage != null)
? buttonImage
: (buttonText != null) ? Text(buttonText) : null,
),
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
border: border,
boxShadow: boxShadow,
),
child: Center(
child: buttonIcon != null
? buttonIcon!
: (buttonImage != null)
? buttonImage!
: (buttonText != null) ? Text(buttonText!) : null,
),
);
}

Expand Down Expand Up @@ -87,11 +84,10 @@ class CircleView extends StatelessWidget {

factory CircleView.padBackgroundCircle(
double size, Color backgroundColour, borderColor, Color shadowColor,
{double opacity}) =>
{double? opacity}) =>
CircleView(
size: size,
color: backgroundColour,
opacity: opacity,
border: Border.all(
color: borderColor,
width: 4.0,
Expand All @@ -108,10 +104,10 @@ class CircleView extends StatelessWidget {

factory CircleView.padButtonCircle(
double size,
Color color,
Image image,
Icon icon,
String text,
Color? color,
Image? image,
Icon? icon,
String? text,
) =>
CircleView(
size: size,
Expand Down
122 changes: 62 additions & 60 deletions lib/views/joystick_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class JoystickView extends StatelessWidget {
///
/// Defaults to half of the width in the portrait
/// or half of the height in the landscape mode
final double size;
final double? size;

/// Color of the icons
///
Expand All @@ -35,12 +35,12 @@ class JoystickView extends StatelessWidget {
/// The opacity applies to the whole joystick including icons
///
/// Defaults to [null] which means there will be no [Opacity] widget used
final double opacity;
final double? opacity;

/// Callback to be called when user pans the joystick
///
/// Defaults to [null]
final JoystickDirectionCallback onDirectionChanged;
final JoystickDirectionCallback? onDirectionChanged;

/// Indicates how often the [onDirectionChanged] should be called.
///
Expand All @@ -50,36 +50,34 @@ class JoystickView extends StatelessWidget {
///
/// The exception is the [onDirectionChanged] callback being called
/// on the [onPanStart] and [onPanEnd] callbacks. It will be called immediately.
final Duration interval;
final Duration? interval;

/// Shows top/right/bottom/left arrows on top of Joystick
///
/// Defaults to [true]
final bool showArrows;

JoystickView(
{this.size,
this.iconsColor = Colors.white54,
this.backgroundColor = Colors.blueGrey,
this.innerCircleColor = Colors.blueGrey,
this.opacity,
this.onDirectionChanged,
this.interval,
this.showArrows = true});
JoystickView({
this.size,
this.iconsColor = Colors.white54,
this.backgroundColor = Colors.blueGrey,
this.innerCircleColor = Colors.blueGrey,
this.opacity,
this.onDirectionChanged,
this.interval,
this.showArrows = true});

@override
Widget build(BuildContext context) {
double actualSize = size != null
? size
: _math.min(MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height) *
0.5;
double innerCircleSize = actualSize / 2;
Offset lastPosition = Offset(innerCircleSize, innerCircleSize);
Offset joystickInnerPosition = _calculatePositionOfInnerCircle(
lastPosition, innerCircleSize, actualSize, Offset(0, 0));

DateTime _callbackTimestamp;
final actualSize = size != null
? size!
: _math.min(MediaQuery.of(context).size.width, MediaQuery.of(context).size.height) * 0.5;
final innerCircleSize = actualSize / 2;
var lastPosition = Offset(innerCircleSize, innerCircleSize);
var joystickInnerPosition = _calculatePositionOfInnerCircle(
lastPosition, innerCircleSize, actualSize, Offset(0, 0));

DateTime? _callbackTimestamp;

return Center(
child: StatefulBuilder(
Expand All @@ -91,12 +89,12 @@ class JoystickView extends StatelessWidget {
backgroundColor,
),
Positioned(
top: joystickInnerPosition.dy,
left: joystickInnerPosition.dx,
child: CircleView.joystickInnerCircle(
actualSize / 2,
innerCircleColor,
),
top: joystickInnerPosition.dy,
left: joystickInnerPosition.dx,
),
if (showArrows) ...createArrows(),
],
Expand All @@ -111,7 +109,7 @@ class JoystickView extends StatelessWidget {
onPanEnd: (details) {
_callbackTimestamp = null;
if (onDirectionChanged != null) {
onDirectionChanged(0, 0);
onDirectionChanged!(0, 0);
}
joystickInnerPosition = _calculatePositionOfInnerCircle(
Offset(innerCircleSize, innerCircleSize),
Expand All @@ -133,7 +131,7 @@ class JoystickView extends StatelessWidget {
setState(() => lastPosition = details.localPosition);
},
child: (opacity != null)
? Opacity(opacity: opacity, child: joystick)
? Opacity(opacity: opacity!, child: joystick)
: joystick,
);
},
Expand All @@ -144,67 +142,71 @@ class JoystickView extends StatelessWidget {
List<Widget> createArrows() {
return [
Positioned(
top: 7.0,
left: 0.0,
right: 0.0,
child: Icon(
Icons.arrow_upward,
color: iconsColor,
size: 15,
),
top: 16.0,
left: 0.0,
right: 0.0,
),
Positioned(
top: 0.0,
bottom: 0.0,
left: 7.0,
child: Icon(
Icons.arrow_back,
color: iconsColor,
size: 15,
),
top: 0.0,
bottom: 0.0,
left: 16.0,
),
Positioned(
top: 0.0,
bottom: 0.0,
right: 7.0,
child: Icon(
Icons.arrow_forward,
color: iconsColor,
size: 15,
),
top: 0.0,
bottom: 0.0,
right: 16.0,
),
Positioned(
bottom: 7.0,
left: 0.0,
right: 0.0,
child: Icon(
Icons.arrow_downward,
color: iconsColor,
size: 15,
),
bottom: 16.0,
left: 0.0,
right: 0.0,
),
];
}

DateTime _processGesture(double size, double ignoreSize, Offset offset,
DateTime callbackTimestamp) {
double middle = size / 2.0;
DateTime? _processGesture(double size, double ignoreSize, Offset offset,
DateTime? callbackTimestamp) {
final middle = size / 2.0;

double angle = _math.atan2(offset.dy - middle, offset.dx - middle);
double degrees = angle * 180 / _math.pi + 90;
final angle = _math.atan2(offset.dy - middle, offset.dx - middle);
var degrees = angle * 180 / _math.pi + 90;
if (offset.dx < middle && offset.dy < middle) {
degrees = 360 + degrees;
}

double dx = _math.max(0, _math.min(offset.dx, size));
double dy = _math.max(0, _math.min(offset.dy, size));
final dx = _math.max(0, _math.min(offset.dx, size));
final dy = _math.max(0, _math.min(offset.dy, size));

double distance =
final distance =
_math.sqrt(_math.pow(middle - dx, 2) + _math.pow(middle - dy, 2));

double normalizedDistance = _math.min(distance / (size / 2), 1.0);
final normalizedDistance = _math.min(distance / (size / 2), 1.0);

DateTime _callbackTimestamp = callbackTimestamp;
var _callbackTimestamp = callbackTimestamp;
if (onDirectionChanged != null &&
_canCallOnDirectionChanged(callbackTimestamp)) {
_callbackTimestamp = DateTime.now();
onDirectionChanged(degrees, normalizedDistance);
onDirectionChanged!(degrees, normalizedDistance);
}

return _callbackTimestamp;
Expand All @@ -214,11 +216,11 @@ class JoystickView extends StatelessWidget {
///
/// Returns true if enough time has passed since last time it was called
/// or when there is no [interval] set.
bool _canCallOnDirectionChanged(DateTime callbackTimestamp) {
bool _canCallOnDirectionChanged(DateTime? callbackTimestamp) {
if (interval != null && callbackTimestamp != null) {
int intervalMilliseconds = interval.inMilliseconds;
int timestampMilliseconds = callbackTimestamp.millisecondsSinceEpoch;
int currentTimeMilliseconds = DateTime.now().millisecondsSinceEpoch;
final intervalMilliseconds = interval!.inMilliseconds;
final timestampMilliseconds = callbackTimestamp.millisecondsSinceEpoch;
final currentTimeMilliseconds = DateTime.now().millisecondsSinceEpoch;

if (currentTimeMilliseconds - timestampMilliseconds <=
intervalMilliseconds) {
Expand All @@ -231,16 +233,16 @@ class JoystickView extends StatelessWidget {

Offset _calculatePositionOfInnerCircle(
Offset lastPosition, double innerCircleSize, double size, Offset offset) {
double middle = size / 2.0;
final middle = size / 2.0;

double angle = _math.atan2(offset.dy - middle, offset.dx - middle);
double degrees = angle * 180 / _math.pi;
final angle = _math.atan2(offset.dy - middle, offset.dx - middle);
var degrees = angle * 180 / _math.pi;
if (offset.dx < middle && offset.dy < middle) {
degrees = 360 + degrees;
}
bool isStartPosition = lastPosition.dx == innerCircleSize &&
final isStartPosition = lastPosition.dx == innerCircleSize &&
lastPosition.dy == innerCircleSize;
double lastAngleRadians =
final lastAngleRadians =
(isStartPosition) ? 0 : (degrees) * (_math.pi / 180.0);

var rBig = size / 2;
Expand Down
Loading