upgrade null-safe

This commit is contained in:
csf 2022-02-17 15:22:14 +08:00
parent f5f496f1cf
commit b4ed72435c
9 changed files with 298 additions and 281 deletions

View File

@ -7,7 +7,7 @@ typedef F = String Function(String);
typedef FMethod = String Function(String, dynamic);
class Translator {
static F call;
static late F call;
}
class MyTheme {
@ -32,8 +32,8 @@ final ButtonStyle flatButtonStyle = TextButton.styleFrom(
),
);
void Function() loadingCancelCallback;
void showLoading(String text, BuildContext context) {
void Function()? loadingCancelCallback;
void showLoading(String text, BuildContext? context) {
if (_hasDialog && context != null) {
Navigator.pop(context);
_hasDialog = false;
@ -43,34 +43,34 @@ void showLoading(String text, BuildContext context) {
EasyLoading.show(status: text, maskType: EasyLoadingMaskType.black);
return;
}
EasyLoading.showWidget(
Container(
constraints: BoxConstraints(maxWidth: 300),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(child: CircularProgressIndicator()),
SizedBox(height: 20),
Center(
child: Text(Translator.call(text),
style: TextStyle(fontSize: 15))),
SizedBox(height: 20),
Center(
child: TextButton(
style: flatButtonStyle,
onPressed: () {
// with out loadingCancelCallback, we can see unexpected input password
// dialog shown in home, no clue why, so use this as workaround
// why no such issue on android?
if (loadingCancelCallback != null)
loadingCancelCallback();
Navigator.pop(context);
},
child: Text(Translator.call('Cancel'),
style: TextStyle(color: MyTheme.accent))))
],
)),
maskType: EasyLoadingMaskType.black);
// EasyLoading.showWidget(
// Container(
// constraints: BoxConstraints(maxWidth: 300),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Center(child: CircularProgressIndicator()),
// SizedBox(height: 20),
// Center(
// child: Text(Translator.call(text),
// style: TextStyle(fontSize: 15))),
// SizedBox(height: 20),
// Center(
// child: TextButton(
// style: flatButtonStyle,
// onPressed: () {
// // with out loadingCancelCallback, we can see unexpected input password
// // dialog shown in home, no clue why, so use this as workaround
// // why no such issue on android?
// if (loadingCancelCallback != null)
// loadingCancelCallback();
// Navigator.pop(context);
// },
// child: Text(Translator.call('Cancel'),
// style: TextStyle(color: MyTheme.accent))))
// ],
// )),
// maskType: EasyLoadingMaskType.black);
}
void dismissLoading() {
@ -82,8 +82,9 @@ bool _hasDialog = false;
typedef BuildAlertDailog = Tuple3<Widget, Widget, List<Widget>> Function(
void Function(void Function()));
Future<T> showAlertDialog<T>(BuildContext context, BuildAlertDailog build,
[WillPopCallback onWillPop,
// ??
Future<T?> showAlertDialog<T>(BuildContext context, BuildAlertDailog build,
[WillPopCallback? onWillPop,
bool barrierDismissible = false,
double contentPadding = 20]) async {
dismissLoading();
@ -112,7 +113,7 @@ Future<T> showAlertDialog<T>(BuildContext context, BuildAlertDailog build,
}
void msgbox(String type, String title, String text, BuildContext context,
[bool hasCancel]) {
{bool? hasCancel}) {
var wrap = (String text, void Function() onPressed) => ButtonTheme(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
@ -148,26 +149,44 @@ void msgbox(String type, String title, String text, BuildContext context,
dismissLoading();
}));
}
EasyLoading.showWidget(
Container(
constraints: BoxConstraints(maxWidth: 300),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(Translator.call(title), style: TextStyle(fontSize: 21)),
SizedBox(height: 20),
Text(Translator.call(text), style: TextStyle(fontSize: 15)),
SizedBox(height: 20),
Row(
children: buttons,
)
],
)),
maskType: EasyLoadingMaskType.black);
EasyLoading.show(
status: "",
maskType: EasyLoadingMaskType.black,
indicator: Container(
constraints: BoxConstraints(maxWidth: 300),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(Translator.call(title), style: TextStyle(fontSize: 21)),
SizedBox(height: 20),
Text(Translator.call(text), style: TextStyle(fontSize: 15)),
SizedBox(height: 20),
Row(
children: buttons,
)
],
))
);
// EasyLoading.showWidget(
// Container(
// constraints: BoxConstraints(maxWidth: 300),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(Translator.call(title), style: TextStyle(fontSize: 21)),
// SizedBox(height: 20),
// Text(Translator.call(text), style: TextStyle(fontSize: 15)),
// SizedBox(height: 20),
// Row(
// children: buttons,
// )
// ],
// )),
// maskType: EasyLoadingMaskType.black);
}
class PasswordWidget extends StatefulWidget {
PasswordWidget({Key key, this.controller}) : super(key: key);
PasswordWidget({Key? key, required this.controller}) : super(key: key);
final TextEditingController controller;
@ -221,4 +240,4 @@ bool isAndroid = false;
bool isIOS = false;
bool isWeb = false;
bool isDesktop = false;
BuildContext nowCtx;
BuildContext? currentCtx;

View File

@ -14,8 +14,8 @@ const kScaleSlop = kPrecisePointerPanSlop / 10;
class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
CustomTouchGestureRecognizer({
Object debugOwner,
Set<PointerDeviceKind> supportedDevices,
Object? debugOwner,
Set<PointerDeviceKind>? supportedDevices,
}) : super(
debugOwner: debugOwner,
supportedDevices: supportedDevices,
@ -24,24 +24,24 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
}
// oneFingerPan
GestureDragStartCallback onOneFingerPanStart;
GestureDragUpdateCallback onOneFingerPanUpdate;
GestureDragEndCallback onOneFingerPanEnd;
GestureDragStartCallback? onOneFingerPanStart;
GestureDragUpdateCallback? onOneFingerPanUpdate;
GestureDragEndCallback? onOneFingerPanEnd;
// twoFingerScale
GestureScaleStartCallback onTwoFingerScaleStart;
GestureScaleUpdateCallback onTwoFingerScaleUpdate;
GestureScaleEndCallback onTwoFingerScaleEnd;
GestureScaleStartCallback? onTwoFingerScaleStart;
GestureScaleUpdateCallback? onTwoFingerScaleUpdate;
GestureScaleEndCallback? onTwoFingerScaleEnd;
// twoFingerVerticalDrag
GestureDragStartCallback onTwoFingerVerticalDragStart;
GestureDragUpdateCallback onTwoFingerVerticalDragUpdate;
GestureDragEndCallback onTwoFingerVerticalDragEnd;
GestureDragStartCallback? onTwoFingerVerticalDragStart;
GestureDragUpdateCallback? onTwoFingerVerticalDragUpdate;
GestureDragEndCallback? onTwoFingerVerticalDragEnd;
// twoFingerHorizontalDrag
GestureDragStartCallback onTwoFingerHorizontalDragStart;
GestureDragUpdateCallback onTwoFingerHorizontalDragUpdate;
GestureDragEndCallback onTwoFingerHorizontalDragEnd;
GestureDragStartCallback? onTwoFingerHorizontalDragStart;
GestureDragUpdateCallback? onTwoFingerHorizontalDragUpdate;
GestureDragEndCallback? onTwoFingerHorizontalDragEnd;
void _init() {
debugPrint("CustomTouchGestureRecognizer init");
@ -66,22 +66,22 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
switch (_currentState) {
case CustomTouchGestureState.oneFingerPan:
if (onOneFingerPanUpdate != null) {
onOneFingerPanUpdate(_getDragUpdateDetails(d));
onOneFingerPanUpdate!(_getDragUpdateDetails(d));
}
break;
case CustomTouchGestureState.twoFingerScale:
if (onTwoFingerScaleUpdate != null) {
onTwoFingerScaleUpdate(d);
onTwoFingerScaleUpdate!(d);
}
break;
case CustomTouchGestureState.twoFingerHorizontalDrag:
if (onTwoFingerHorizontalDragUpdate != null) {
onTwoFingerHorizontalDragUpdate(_getDragUpdateDetails(d));
onTwoFingerHorizontalDragUpdate!(_getDragUpdateDetails(d));
}
break;
case CustomTouchGestureState.twoFingerVerticalDrag:
if (onTwoFingerVerticalDragUpdate != null) {
onTwoFingerVerticalDragUpdate(_getDragUpdateDetails(d));
onTwoFingerVerticalDragUpdate!(_getDragUpdateDetails(d));
}
break;
default:
@ -97,25 +97,25 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
case CustomTouchGestureState.oneFingerPan:
debugPrint("TwoFingerState.pan onEnd");
if (onOneFingerPanEnd != null) {
onOneFingerPanEnd(_getDragEndDetails(d));
onOneFingerPanEnd!(_getDragEndDetails(d));
}
break;
case CustomTouchGestureState.twoFingerScale:
debugPrint("TwoFingerState.scale onEnd");
if (onTwoFingerScaleEnd != null) {
onTwoFingerScaleEnd(d);
onTwoFingerScaleEnd!(d);
}
break;
case CustomTouchGestureState.twoFingerHorizontalDrag:
debugPrint("TwoFingerState.horizontal onEnd");
if (onTwoFingerHorizontalDragEnd != null) {
onTwoFingerHorizontalDragEnd(_getDragEndDetails(d));
onTwoFingerHorizontalDragEnd!(_getDragEndDetails(d));
}
break;
case CustomTouchGestureState.twoFingerVerticalDrag:
debugPrint("TwoFingerState.vertical onEnd");
if (onTwoFingerVerticalDragEnd != null) {
onTwoFingerVerticalDragEnd(_getDragEndDetails(d));
onTwoFingerVerticalDragEnd!(_getDragEndDetails(d));
}
break;
default:
@ -129,7 +129,7 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
var _currentState = CustomTouchGestureState.none;
var _isWatch = false;
Timer _timer;
Timer? _timer;
double _sumScale = 0;
double _sumVertical = 0;
double _sumHorizontal = 0;
@ -143,7 +143,7 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
void _reset() {
_isWatch = false;
_clearSum();
if (_timer != null) _timer.cancel();
if (_timer != null) _timer!.cancel();
}
void _updateCompute(ScaleUpdateDetails d) {
@ -155,14 +155,14 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
debugPrint("start Scale");
_currentState = CustomTouchGestureState.twoFingerScale;
if (onOneFingerPanStart != null) {
onOneFingerPanStart(_getDragStartDetails(d));
onOneFingerPanStart!(_getDragStartDetails(d));
}
_reset();
} else if (_sumHorizontal.abs() > kPrecisePointerPanSlop) {
debugPrint("start Horizontal");
_currentState = CustomTouchGestureState.twoFingerHorizontalDrag;
if (onTwoFingerHorizontalDragUpdate != null) {
onTwoFingerHorizontalDragUpdate(_getDragUpdateDetails(d));
onTwoFingerHorizontalDragUpdate!(_getDragUpdateDetails(d));
}
_reset();
} else if (_sumVertical.abs() > kPrecisePointerPanSlop) {
@ -179,7 +179,7 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
debugPrint("startWatchTimer");
_isWatch = true;
_clearSum();
if (_timer != null) _timer.cancel();
if (_timer != null) _timer!.cancel();
_timer = Timer(const Duration(milliseconds: 200), _reset);
}
@ -201,24 +201,24 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
class HoldTapMoveGestureRecognizer extends GestureRecognizer {
HoldTapMoveGestureRecognizer({
Object debugOwner,
Set<PointerDeviceKind> supportedDevices,
Object? debugOwner,
Set<PointerDeviceKind>? supportedDevices,
}) : super(
debugOwner: debugOwner,
supportedDevices: supportedDevices,
);
GestureDragStartCallback onHoldDragStart;
GestureDragUpdateCallback onHoldDragUpdate;
GestureDragDownCallback onHoldDragDown;
GestureDragCancelCallback onHoldDragCancel;
GestureDragStartCallback? onHoldDragStart;
GestureDragUpdateCallback? onHoldDragUpdate;
GestureDragDownCallback? onHoldDragDown;
GestureDragCancelCallback? onHoldDragCancel;
bool _isStart = false;
Timer _firstTapUpTimer; // reject
Timer _secondTapDownTimer; // reject drag update
_TapTracker _firstTap;
_TapTracker _secondTap;
Timer? _firstTapUpTimer; // reject
Timer? _secondTapDownTimer; // reject drag update
_TapTracker? _firstTap;
_TapTracker? _secondTap;
final Map<int, _TapTracker> _trackers = <int, _TapTracker>{};
@ -244,11 +244,11 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
void addAllowedPointer(PointerDownEvent event) {
//
if (_firstTap != null) {
if (!_firstTap.isWithinGlobalTolerance(event, kDoubleTapSlop)) {
if (!_firstTap!.isWithinGlobalTolerance(event, kDoubleTapSlop)) {
// Ignore out-of-bounds second taps.
return;
} else if (!_firstTap.hasElapsedMinTime() ||
!_firstTap.hasSameButton(event)) {
} else if (!_firstTap!.hasElapsedMinTime() ||
!_firstTap!.hasSameButton(event)) {
// Restart when the second tap is too close to the first (touch screens
// often detect touches intermittently), or when buttons mismatch.
_reset();
@ -256,7 +256,7 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
} else if (onHoldDragDown != null) {
invokeCallback<void>(
'onHoldDragDown',
() => onHoldDragDown(DragDownDetails(
() => onHoldDragDown!(DragDownDetails(
globalPosition: event.position,
localPosition: event.localPosition)));
}
@ -269,7 +269,7 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
_stopSecondTapDownTimer();
final _TapTracker tracker = _TapTracker(
event: event,
entry: GestureBinding.instance.gestureArena.add(event.pointer, this),
entry: GestureBinding.instance!.gestureArena.add(event.pointer, this),
doubleTapMinTime: kDoubleTapMinTime,
gestureSettings: gestureSettings,
);
@ -279,7 +279,7 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
// down后一段时间没有抬起则表示start
void _handleEvent(PointerEvent event) {
final _TapTracker tracker = _trackers[event.pointer];
final _TapTracker tracker = _trackers[event.pointer]!;
if (event is PointerUpEvent) {
if (_firstTap == null && _secondTap == null) {
_registerFirstTap(tracker);
@ -294,17 +294,17 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
} else if (event is PointerMoveEvent) {
// first tap move second tap move且已经通过竞技场则update
if (!tracker.isWithinGlobalTolerance(event, kDoubleTapTouchSlop)) {
if (_firstTap != null && _firstTap.pointer == event.pointer) {
if (_firstTap != null && _firstTap!.pointer == event.pointer) {
// first tap move
_reject(tracker);
} else if (_secondTap != null && _secondTap.pointer == event.pointer) {
} else if (_secondTap != null && _secondTap!.pointer == event.pointer) {
// debugPrint("_secondTap move");
// second tap move
if (!_isStart) {
_resolve();
}
if (onHoldDragUpdate != null)
onHoldDragUpdate(DragUpdateDetails(
onHoldDragUpdate!(DragUpdateDetails(
globalPosition: event.position,
localPosition: event.localPosition,
delta: event.delta));
@ -320,9 +320,9 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
@override
void rejectGesture(int pointer) {
_TapTracker tracker = _trackers[pointer];
_TapTracker? tracker = _trackers[pointer];
// If tracker isn't in the list, check if this is the first tap tracker
if (tracker == null && _firstTap != null && _firstTap.pointer == pointer) {
if (tracker == null && _firstTap != null && _firstTap!.pointer == pointer) {
tracker = _firstTap;
}
// If tracker is still null, we rejected ourselves already
@ -333,11 +333,11 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
void _resolve() {
_stopSecondTapDownTimer();
_firstTap?.entry?.resolve(GestureDisposition.accepted);
_secondTap?.entry?.resolve(GestureDisposition.accepted);
_firstTap?.entry.resolve(GestureDisposition.accepted);
_secondTap?.entry.resolve(GestureDisposition.accepted);
_isStart = true;
// TODO start details
if (onHoldDragStart != null) onHoldDragStart(DragStartDetails());
if (onHoldDragStart != null) onHoldDragStart!(DragStartDetails());
}
void _reject(_TapTracker tracker) {
@ -367,16 +367,16 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
}
// Note, order is important below in order for the resolve -> reject logic
// to work properly.
final _TapTracker tracker = _firstTap;
final _TapTracker tracker = _firstTap!;
_firstTap = null;
_reject(tracker);
GestureBinding.instance.gestureArena.release(tracker.pointer);
GestureBinding.instance!.gestureArena.release(tracker.pointer);
if (_secondTap != null) {
final _TapTracker tracker = _secondTap;
final _TapTracker tracker = _secondTap!;
_secondTap = null;
_reject(tracker);
GestureBinding.instance.gestureArena.release(tracker.pointer);
GestureBinding.instance!.gestureArena.release(tracker.pointer);
}
}
// TODO
@ -387,7 +387,7 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
void _registerFirstTap(_TapTracker tracker) {
_startFirstTapUpTimer();
GestureBinding.instance.gestureArena.hold(tracker.pointer);
GestureBinding.instance!.gestureArena.hold(tracker.pointer);
// Note, order is important below in order for the clear -> reject logic to
// work properly.
_freezeTracker(tracker);
@ -399,12 +399,12 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
// first tap的状态
if (_firstTap != null) {
_stopFirstTapUpTimer();
_freezeTracker(_firstTap);
_freezeTracker(_firstTap!);
_firstTap = null;
}
_startSecondTapDownTimer();
GestureBinding.instance.gestureArena.hold(tracker.pointer);
GestureBinding.instance!.gestureArena.hold(tracker.pointer);
_secondTap = tracker;
@ -430,21 +430,21 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
void _stopFirstTapUpTimer() {
if (_firstTapUpTimer != null) {
_firstTapUpTimer.cancel();
_firstTapUpTimer!.cancel();
_firstTapUpTimer = null;
}
}
void _stopSecondTapDownTimer() {
if (_secondTapDownTimer != null) {
_secondTapDownTimer.cancel();
_secondTapDownTimer!.cancel();
_secondTapDownTimer = null;
}
}
void _checkCancel() {
if (onHoldDragCancel != null) {
invokeCallback<void>('onHoldDragCancel', onHoldDragCancel);
invokeCallback<void>('onHoldDragCancel', onHoldDragCancel!);
}
}
@ -454,19 +454,19 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
DoubleFinerTapGestureRecognizer({
Object debugOwner,
Set<PointerDeviceKind> supportedDevices,
Object? debugOwner,
Set<PointerDeviceKind>? supportedDevices,
}) : super(
debugOwner: debugOwner,
supportedDevices: supportedDevices,
);
GestureTapDownCallback onDoubleFinerTapDown;
GestureTapDownCallback onDoubleFinerTap;
GestureTapCancelCallback onDoubleFinerTapCancel;
GestureTapDownCallback? onDoubleFinerTapDown;
GestureTapDownCallback? onDoubleFinerTap;
GestureTapCancelCallback? onDoubleFinerTapCancel;
Timer _firstTapTimer; // reject
_TapTracker _firstTap;
Timer? _firstTapTimer; // reject
_TapTracker? _firstTap;
var _isStart = false;
@ -505,7 +505,7 @@ class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
kind: getKindForPointer(event.pointer),
);
invokeCallback<void>(
'onDoubleFinerTapDown', () => onDoubleFinerTapDown(details));
'onDoubleFinerTapDown', () => onDoubleFinerTapDown!(details));
}
} else {
// first tap
@ -518,7 +518,7 @@ class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
void _trackTap(PointerDownEvent event) {
final _TapTracker tracker = _TapTracker(
event: event,
entry: GestureBinding.instance.gestureArena.add(event.pointer, this),
entry: GestureBinding.instance!.gestureArena.add(event.pointer, this),
doubleTapMinTime: kDoubleTapMinTime,
gestureSettings: gestureSettings,
);
@ -531,7 +531,7 @@ class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
// down后一段时间没有抬起则表示start
void _handleEvent(PointerEvent event) {
final _TapTracker tracker = _trackers[event.pointer];
final _TapTracker tracker = _trackers[event.pointer]!;
if (event is PointerUpEvent) {
debugPrint("PointerUpEvent");
_upTap.add(tracker.pointer);
@ -548,9 +548,9 @@ class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
@override
void rejectGesture(int pointer) {
_TapTracker tracker = _trackers[pointer];
_TapTracker? tracker = _trackers[pointer];
// If tracker isn't in the list, check if this is the first tap tracker
if (tracker == null && _firstTap != null && _firstTap.pointer == pointer) {
if (tracker == null && _firstTap != null && _firstTap!.pointer == pointer) {
tracker = _firstTap;
}
// If tracker is still null, we rejected ourselves already
@ -589,7 +589,7 @@ class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
}
void _registerTap(_TapTracker tracker) {
GestureBinding.instance.gestureArena.hold(tracker.pointer);
GestureBinding.instance!.gestureArena.hold(tracker.pointer);
// Note, order is important below in order for the clear -> reject logic to
// work properly.
}
@ -609,7 +609,7 @@ class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
void _stopFirstTapUpTimer() {
if (_firstTapTimer != null) {
_firstTapTimer.cancel();
_firstTapTimer!.cancel();
_firstTapTimer = null;
}
}
@ -626,7 +626,7 @@ class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
void _resolve() {
// TODO tap down details
if (onDoubleFinerTap != null) onDoubleFinerTap(TapDownDetails());
if (onDoubleFinerTap != null) onDoubleFinerTap!(TapDownDetails());
_trackers.forEach((key, value) {
value.entry.resolve(GestureDisposition.accepted);
});
@ -635,7 +635,7 @@ class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
void _checkCancel() {
if (onDoubleFinerTapCancel != null) {
invokeCallback<void>('onHoldDragCancel', onDoubleFinerTapCancel);
invokeCallback<void>('onHoldDragCancel', onDoubleFinerTapCancel!);
}
}
@ -647,10 +647,10 @@ class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
/// larger gesture.
class _TapTracker {
_TapTracker({
PointerDownEvent event,
this.entry,
Duration doubleTapMinTime,
this.gestureSettings,
required PointerDownEvent event,
required this.entry,
required Duration doubleTapMinTime,
required this.gestureSettings,
}) : assert(doubleTapMinTime != null),
assert(event != null),
assert(event.buttons != null),
@ -660,7 +660,7 @@ class _TapTracker {
_doubleTapMinTimeCountdown =
_CountdownZoned(duration: doubleTapMinTime);
final DeviceGestureSettings gestureSettings;
final DeviceGestureSettings? gestureSettings;
final int pointer;
final GestureArenaEntry entry;
final Offset _initialGlobalPosition;
@ -669,17 +669,17 @@ class _TapTracker {
bool _isTrackingPointer = false;
void startTrackingPointer(PointerRoute route, Matrix4 transform) {
void startTrackingPointer(PointerRoute route, Matrix4? transform) {
if (!_isTrackingPointer) {
_isTrackingPointer = true;
GestureBinding.instance.pointerRouter.addRoute(pointer, route, transform);
GestureBinding.instance!.pointerRouter.addRoute(pointer, route, transform);
}
}
void stopTrackingPointer(PointerRoute route) {
if (_isTrackingPointer) {
_isTrackingPointer = false;
GestureBinding.instance.pointerRouter.removeRoute(pointer, route);
GestureBinding.instance!.pointerRouter.removeRoute(pointer, route);
}
}
@ -700,7 +700,7 @@ class _TapTracker {
/// CountdownZoned tracks whether the specified duration has elapsed since
/// creation, honoring [Zone].
class _CountdownZoned {
_CountdownZoned({Duration duration}) : assert(duration != null) {
_CountdownZoned({required Duration duration}) : assert(duration != null) {
Timer(duration, _onTimeout);
}
@ -714,18 +714,19 @@ class _CountdownZoned {
}
RawGestureDetector getMixinGestureDetector({
Widget child,
GestureTapUpCallback onTapUp,
GestureDoubleTapCallback onDoubleTap,
GestureDragStartCallback onHoldDragStart,
GestureDragUpdateCallback onHoldDragUpdate,
GestureDragCancelCallback onHoldDragCancel,
GestureTapDownCallback onDoubleFinerTap,
GestureDragStartCallback onOneFingerPanStart,
GestureDragUpdateCallback onOneFingerPanUpdate,
GestureScaleUpdateCallback onTwoFingerScaleUpdate,
GestureDragUpdateCallback onTwoFingerHorizontalDragUpdate,
GestureDragUpdateCallback onTwoFingerVerticalDragUpdate,
Widget? child,
GestureTapUpCallback? onTapUp,
GestureDoubleTapCallback? onDoubleTap,
GestureDragStartCallback? onHoldDragStart,
GestureDragUpdateCallback? onHoldDragUpdate,
GestureDragCancelCallback? onHoldDragCancel,
GestureTapDownCallback? onDoubleFinerTap,
GestureDragStartCallback? onOneFingerPanStart,
GestureDragUpdateCallback? onOneFingerPanUpdate,
GestureScaleUpdateCallback? onTwoFingerScaleUpdate,
GestureScaleEndCallback? onTwoFingerScaleEnd,
GestureDragUpdateCallback? onTwoFingerHorizontalDragUpdate,
GestureDragUpdateCallback? onTwoFingerVerticalDragUpdate,
}) {
return RawGestureDetector(
child: child,
@ -763,6 +764,7 @@ RawGestureDetector getMixinGestureDetector({
..onOneFingerPanStart = onOneFingerPanStart
..onOneFingerPanUpdate = onOneFingerPanUpdate
..onTwoFingerScaleUpdate = onTwoFingerScaleUpdate
..onTwoFingerScaleEnd = onTwoFingerScaleEnd
..onTwoFingerHorizontalDragUpdate = onTwoFingerHorizontalDragUpdate
..onTwoFingerVerticalDragUpdate = onTwoFingerVerticalDragUpdate;
})

View File

@ -8,7 +8,7 @@ import 'model.dart';
import 'remote_page.dart';
class HomePage extends StatefulWidget {
HomePage({Key key, this.title}) : super(key: key);
HomePage({Key? key, required this.title}) : super(key: key);
final String title;
@ -55,18 +55,20 @@ class _HomePageState extends State<HomePage> {
},
onTap: () {
() async {
var value = await showMenu(
var value = await showMenu<dynamic>(
context: context,
position: this._menuPos,
items: [
PopupMenuItem<String>(
child: Text(translate('ID Server')),
value: 'id_server'),
// TODO test
isAndroid
? PopupMenuItem<String>(
? PopupMenuItem<dynamic>(
child: Text(translate('Share My Screen')),
value: 'server')
: null,
: PopupMenuItem<dynamic>(
child: SizedBox.shrink(), value: ''),
PopupMenuItem<String>(
child: Text(translate('About') + ' RustDesk'),
value: 'about'),
@ -328,8 +330,8 @@ void showServer(BuildContext context) {
labelText: translate('ID Server'),
),
validator: validate,
onSaved: (String value) {
id = value.trim();
onSaved: (String? value) {
if (value != null) id = value.trim();
},
),
/*
@ -350,8 +352,8 @@ void showServer(BuildContext context) {
labelText: 'Key',
),
validator: null,
onSaved: (String value) {
key = value.trim();
onSaved: (String? value) {
if (value != null) key = value.trim();
},
),
])),
@ -366,8 +368,8 @@ void showServer(BuildContext context) {
TextButton(
style: flatButtonStyle,
onPressed: () {
if (formKey.currentState.validate()) {
formKey.currentState.save();
if (formKey.currentState != null && formKey.currentState!.validate()) {
formKey.currentState!.save();
if (id != id0)
FFI.setByName('option',
'{"name": "custom-rendezvous-server", "value": "$id"}');
@ -391,7 +393,7 @@ Future<Null> showAbout(BuildContext context) async {
showAlertDialog(
context,
(setState) => Tuple3(
null,
SizedBox.shrink(), // TODO test old:null
Wrap(direction: Axis.vertical, spacing: 12, children: [
Text('Version: $version'),
InkWell(
@ -409,12 +411,12 @@ Future<Null> showAbout(BuildContext context) async {
)),
)),
]),
null),
[]),
() async => true,
true);
}
String validate(value) {
String? validate(value) {
value = value.trim();
if (value.isEmpty) {
return null;

View File

@ -11,15 +11,15 @@ import 'common.dart';
import 'native_model.dart' if (dart.library.html) 'web_model.dart';
class FfiModel with ChangeNotifier {
PeerInfo _pi;
Display _display;
PeerInfo _pi = PeerInfo();
Display _display = Display();
var _decoding = false;
bool _waitForImage;
bool _waitForImage = false;
bool _initialized = false;
var _inputBlocked = false;
final _permissions = Map<String, bool>();
bool _secure;
bool _direct;
bool? _secure;
bool? _direct;
get permissions => _permissions;
@ -32,6 +32,7 @@ class FfiModel with ChangeNotifier {
get direct => _direct;
get pi => _pi;
get inputBlocked => _inputBlocked;
set inputBlocked(v) {
@ -75,8 +76,8 @@ class FfiModel with ChangeNotifier {
_direct = direct;
}
Image getConnectionImage() {
String icon;
Image? getConnectionImage() {
String? icon;
if (secure == true && direct == true) {
icon = 'secure';
} else if (secure == false && direct == true) {
@ -200,11 +201,11 @@ class FfiModel with ChangeNotifier {
}
class ImageModel with ChangeNotifier {
ui.Image _image;
ui.Image? _image;
ui.Image get image => _image;
ui.Image? get image => _image;
void update(ui.Image image) {
void update(ui.Image? image) {
if (_image == null && image != null) {
if (isDesktop) {
FFI.canvasModel.updateViewStyle();
@ -223,28 +224,26 @@ class ImageModel with ChangeNotifier {
double get maxScale {
if (_image == null) return 1.0;
final size = MediaQueryData.fromWindow(ui.window).size;
final xscale = size.width / _image.width;
final yscale = size.height / _image.height;
final xscale = size.width / _image!.width;
final yscale = size.height / _image!.height;
return max(1.0, max(xscale, yscale));
}
double get minScale {
if (_image == null) return 1.0;
final size = MediaQueryData.fromWindow(ui.window).size;
final xscale = size.width / _image.width;
final yscale = size.height / _image.height;
final xscale = size.width / _image!.width;
final yscale = size.height / _image!.height;
return min(xscale, yscale);
}
}
class CanvasModel with ChangeNotifier {
double _x;
double _y;
double _scale;
double _x = 0;
double _y = 0;
double _scale = 1.0;
CanvasModel() {
clear();
}
CanvasModel();
double get x => _x;
@ -355,7 +354,7 @@ class CanvasModel with ChangeNotifier {
}
class CursorModel with ChangeNotifier {
ui.Image _image;
ui.Image? _image;
final _images = Map<int, Tuple3<ui.Image, double, double>>();
double _x = -10000;
double _y = -10000;
@ -364,7 +363,7 @@ class CursorModel with ChangeNotifier {
double _displayOriginX = 0;
double _displayOriginY = 0;
ui.Image get image => _image;
ui.Image? get image => _image;
double get x => _x - _displayOriginX;
@ -445,7 +444,7 @@ class CursorModel with ChangeNotifier {
var tryMoveCanvasX = false;
if (dx > 0) {
final maxCanvasCanMove =
_displayOriginX + FFI.imageModel.image.width - r.right;
_displayOriginX + FFI.imageModel.image!.width - r.right;
tryMoveCanvasX = _x + dx > cx && maxCanvasCanMove > 0;
if (tryMoveCanvasX) {
dx = min(dx, maxCanvasCanMove);
@ -466,7 +465,7 @@ class CursorModel with ChangeNotifier {
var tryMoveCanvasY = false;
if (dy > 0) {
final mayCanvasCanMove =
_displayOriginY + FFI.imageModel.image.height - r.bottom;
_displayOriginY + FFI.imageModel.image!.height - r.bottom;
tryMoveCanvasY = _y + dy > cy && mayCanvasCanMove > 0;
if (tryMoveCanvasY) {
dy = min(dy, mayCanvasCanMove);
@ -567,12 +566,12 @@ class CursorModel with ChangeNotifier {
}
class ClientState {
bool isStart;
bool isFileTransfer;
String name;
String peerId;
bool isStart = false;
bool isFileTransfer = false;
String name = "";
String peerId = "";
ClientState({this.isStart, this.isFileTransfer, this.name, this.peerId});
ClientState(this.isStart, this.isFileTransfer, this.name, this.peerId);
ClientState.fromJson(Map<String, dynamic> json) {
isStart = json['is_start'];
@ -592,20 +591,17 @@ class ClientState {
}
class ServerModel with ChangeNotifier {
bool _mediaOk;
bool _inputOk;
// bool _needServerOpen;
bool _isPeerStart;
bool _isFileTransfer;
String _peerName;
String _peerID;
bool _mediaOk = false;
bool _inputOk = false;
bool _isPeerStart = false;
bool _isFileTransfer = false;
String _peerName = "";
String _peerID = "";
bool get mediaOk => _mediaOk;
bool get inputOk => _inputOk;
// bool get needServerOpen => _needServerOpen;
bool get isPeerStart => _isPeerStart;
bool get isFileTransfer => _isFileTransfer;
@ -614,18 +610,7 @@ class ServerModel with ChangeNotifier {
String get peerID => _peerID;
ServerModel() {
_mediaOk = false;
_inputOk = false;
_isPeerStart = false;
_peerName = "";
_peerID = "";
}
// setNeedServerOpen(bool v){
// _needServerOpen = v;
// notifyListeners();
// }
ServerModel();
changeStatue(String name, bool value) {
switch (name) {
@ -753,7 +738,7 @@ class FFI {
FFI.id = id;
}
static Map<String, dynamic> popEvent() {
static Map<String, dynamic>? popEvent() {
var s = getByName('event');
if (s == '') return null;
try {
@ -892,13 +877,13 @@ class Display {
}
class PeerInfo {
String version;
String username;
String hostname;
String platform;
bool sasEnabled;
int currentDisplay;
List<Display> displays;
String version = "";
String username = "";
String hostname = "";
String platform = "";
bool sasEnabled = false;
int currentDisplay = 0;
List<Display> displays = [];
}
void savePreference(String id, double xCursor, double yCursor, double xCanvas,
@ -914,7 +899,7 @@ void savePreference(String id, double xCursor, double yCursor, double xCanvas,
prefs.setString('peer' + id, json.encode(p));
}
Future<Map<String, dynamic>> getPreference(String id) async {
Future<Map<String, dynamic>?> getPreference(String id) async {
if (!isDesktop) return null;
SharedPreferences prefs = await SharedPreferences.getInstance();
var p = prefs.getString('peer' + id);

View File

@ -11,8 +11,8 @@ import 'common.dart';
class RgbaFrame extends Struct {
@Uint32()
int len;
Pointer<Uint8> data;
external int len;
external Pointer<Uint8> data;
}
typedef F2 = Pointer<Utf8> Function(Pointer<Utf8>, Pointer<Utf8>);
@ -21,24 +21,25 @@ typedef F4 = void Function(Pointer<RgbaFrame>);
typedef F5 = Pointer<RgbaFrame> Function();
class PlatformFFI {
static Pointer<RgbaFrame> _lastRgbaFrame;
static Pointer<RgbaFrame>? _lastRgbaFrame;
static String _dir = '';
static String _homeDir = '';
static F2 _getByName;
static F3 _setByName;
static F4 _freeRgba;
static F5 _getRgba;
static F2? _getByName;
static F3? _setByName;
static F4? _freeRgba;
static F5? _getRgba;
static void clearRgbaFrame() {
if (_lastRgbaFrame != null && _lastRgbaFrame != nullptr)
_freeRgba(_lastRgbaFrame);
if (_lastRgbaFrame != null &&
_lastRgbaFrame != nullptr &&
_freeRgba != null) _freeRgba!(_lastRgbaFrame!);
}
static Uint8List getRgba() {
static Uint8List? getRgba() {
if (_getRgba == null) return null;
_lastRgbaFrame = _getRgba();
_lastRgbaFrame = _getRgba!();
if (_lastRgbaFrame == null || _lastRgbaFrame == nullptr) return null;
final ref = _lastRgbaFrame.ref;
final ref = _lastRgbaFrame!.ref;
return Uint8List.sublistView(ref.data.asTypedList(ref.len));
}
@ -51,7 +52,7 @@ class PlatformFFI {
if (_getByName == null) return '';
var a = name.toNativeUtf8();
var b = arg.toNativeUtf8();
var p = _getByName(a, b);
var p = _getByName!(a, b);
assert(p != nullptr && p != null);
var res = p.toDartString();
calloc.free(p);
@ -64,7 +65,7 @@ class PlatformFFI {
if (_setByName == null) return;
var a = name.toNativeUtf8();
var b = value.toNativeUtf8();
_setByName(a, b);
_setByName!(a, b);
calloc.free(a);
calloc.free(b);
}
@ -110,6 +111,7 @@ class PlatformFFI {
static void startDesktopWebListener(
Function(Map<String, dynamic>) handleMouse) {}
static void stopDesktopWebListener() {}
static void setMethodCallHandler(FMethod callback) {

View File

@ -14,7 +14,7 @@ import 'model.dart';
final initText = '\1' * 1024;
class RemotePage extends StatefulWidget {
RemotePage({Key key, this.id}) : super(key: key);
RemotePage({Key? key, required this.id}) : super(key: key);
final String id;
@ -23,8 +23,8 @@ class RemotePage extends StatefulWidget {
}
class _RemotePageState extends State<RemotePage> {
Timer _interval;
Timer _timer;
Timer? _interval;
Timer? _timer;
bool _showBar = !isDesktop;
double _bottom = 0;
String _value = '';
@ -47,14 +47,14 @@ class _RemotePageState extends State<RemotePage> {
void initState() {
super.initState();
FFI.connect(widget.id);
WidgetsBinding.instance.addPostFrameCallback((_) {
WidgetsBinding.instance!.addPostFrameCallback((_) {
SystemChrome.setEnabledSystemUIOverlays([]);
showLoading(translate('Connecting...'), context);
_interval =
Timer.periodic(Duration(milliseconds: 30), (timer) => interval());
});
Wakelock.enable();
loadingCancelCallback = () => _interval.cancel();
loadingCancelCallback = () => _interval?.cancel();
_touchMode = FFI.getByName('peer_option', "touch-mode") != '';
}
@ -63,7 +63,7 @@ class _RemotePageState extends State<RemotePage> {
_focusNode.dispose();
FFI.close();
loadingCancelCallback = null;
_interval.cancel();
_interval?.cancel();
_timer?.cancel();
dismissLoading();
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
@ -354,8 +354,8 @@ class _RemotePageState extends State<RemotePage> {
onTapUp: (d) {
if (_drag || _scroll) return;
if (_touchMode) {
FFI.cursorModel.touch(
d.localPosition.dx, d.localPosition.dy, _right);
FFI.cursorModel
.touch(d.localPosition.dx, d.localPosition.dy, _right);
} else {
FFI.tap(_right);
}
@ -385,12 +385,12 @@ class _RemotePageState extends State<RemotePage> {
FFI.cursorModel.updatePan(d.delta.dx, d.delta.dy, _touchMode, _drag);
},
onTwoFingerScaleUpdate: (d) {
var scale = (d.scale -1) / 20 + 1;
FFI.canvasModel.updateScale(scale);
_scale = scale;
FFI.canvasModel.updateScale(d.scale/_scale);
_scale = d.scale;
},
onTwoFingerScaleEnd: (d)=>_scale = 1,
onTwoFingerVerticalDragUpdate: (d) {
FFI.scroll( - d.delta.dy / 20);
FFI.scroll(d.delta.dy / 2);
});
return GestureDetector(
onLongPress: () {
@ -491,9 +491,8 @@ class _RemotePageState extends State<RemotePage> {
paints.add(CursorPaint());
}
return MouseRegion(
cursor: keyboard
? SystemMouseCursors.none
: null, // still laggy, set cursor directly for web is better
cursor: keyboard ? SystemMouseCursors.none : MouseCursor.defer,
// TODO old null // still laggy, set cursor directly for web is better
onEnter: (event) {
print('enter');
FFI.listenToMouse(true);
@ -586,8 +585,8 @@ class _RemotePageState extends State<RemotePage> {
FFI.setByName('refresh');
} else if (value == 'paste') {
() async {
ClipboardData data = await Clipboard.getData(Clipboard.kTextPlain);
if (data.text != null) {
ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
if (data != null && data.text != null) {
FFI.setByName('input_string', '${data.text}');
}
}();
@ -618,8 +617,8 @@ class _RemotePageState extends State<RemotePage> {
return SizedBox();
}
final size = MediaQuery.of(context).size;
var wrap =
(String text, void Function() onPressed, [bool active, IconData icon]) {
var wrap = (String text, void Function() onPressed,
[bool? active, IconData? icon]) {
return TextButton(
style: TextButton.styleFrom(
minimumSize: Size(0, 0),
@ -811,13 +810,13 @@ class CursorPaint extends StatelessWidget {
class ImagePainter extends CustomPainter {
ImagePainter({
this.image,
this.x,
this.y,
this.scale,
required this.image,
required this.x,
required this.y,
required this.scale,
});
ui.Image image;
ui.Image? image;
double x;
double y;
double scale;
@ -826,7 +825,7 @@ class ImagePainter extends CustomPainter {
void paint(Canvas canvas, Size size) {
if (image == null) return;
canvas.scale(scale, scale);
canvas.drawImage(image, new Offset(x, y), new Paint());
canvas.drawImage(image!, new Offset(x, y), new Paint());
}
@override
@ -853,7 +852,9 @@ void enterPasswordDialog(String id, BuildContext context) {
),
value: remember,
onChanged: (v) {
setState(() => remember = v);
if (v != null) {
setState(() => remember = v);
}
},
),
]),
@ -918,7 +919,7 @@ CheckboxListTile getToggle(
}
RadioListTile<String> getRadio(String name, String toValue, String curValue,
void Function(String) onChange) {
void Function(String?) onChange) {
return RadioListTile<String>(
controlAffinity: ListTileControlAffinity.trailing,
title: Text(translate(name)),
@ -985,13 +986,15 @@ void showOptions(BuildContext context) {
more.add(getToggle(setState, 'privacy-mode', 'Privacy mode'));
}
}
var setQuality = (String value) {
var setQuality = (String? value) {
if(value == null) return;
setState(() {
quality = value;
FFI.setByName('image_quality', value);
});
};
var setViewStyle = (String value) {
var setViewStyle = (String? value) {
if(value == null) return;
setState(() {
viewStyle = value;
FFI.setByName(
@ -1000,7 +1003,7 @@ void showOptions(BuildContext context) {
});
};
return Tuple3(
null,
SizedBox.shrink(),
Column(
mainAxisSize: MainAxisSize.min,
children: displays +
@ -1012,7 +1015,7 @@ void showOptions(BuildContext context) {
getRadio('Stretch', 'stretch', viewStyle, setViewStyle),
Divider(color: MyTheme.border),
]
: {}) +
: []) +
<Widget>[
getRadio('Good image quality', 'best', quality, setQuality),
getRadio('Balanced', 'balanced', quality, setQuality),
@ -1023,7 +1026,7 @@ void showOptions(BuildContext context) {
setState, 'show-remote-cursor', 'Show remote cursor'),
] +
more),
null);
[]);
}, () async => true, true, 0);
}
@ -1047,6 +1050,7 @@ void showSetOSPassword(BuildContext context, bool login) {
),
value: autoLogin,
onChanged: (v) {
if(v==null) return;
setState(() => autoLogin = v);
},
),

View File

@ -101,7 +101,7 @@ class _ServerInfoState extends State<ServerInfo> {
labelStyle:
TextStyle(fontWeight: FontWeight.bold, color: MyTheme.accent50),
),
onSaved: (String value) {},
onSaved: (String? value) {},
),
TextFormField(
readOnly: true,
@ -123,7 +123,7 @@ class _ServerInfoState extends State<ServerInfo> {
_passwdShow = !_passwdShow;
});
})),
onSaved: (String value) {},
onSaved: (String? value) {},
),
],
));
@ -187,7 +187,7 @@ class _PermissionCheckerState extends State<PermissionChecker> {
}
}
BuildContext loginReqAlertCtx;
BuildContext? loginReqAlertCtx;
void showLoginReqAlert(BuildContext context, String peerID, String name) async {
await showDialog(
@ -238,7 +238,7 @@ void showLoginReqAlert(BuildContext context, String peerID, String name) async {
clearLoginReqAlert() {
if (loginReqAlertCtx != null) {
Navigator.of(loginReqAlertCtx).pop();
Navigator.of(loginReqAlertCtx!).pop();
FFI.serverModel.updateClientState();
}
}
@ -372,8 +372,10 @@ void toAndroidChannelInit() {
FFI.serverModel.updateClientState();
debugPrint(
"pre show loginAlert:${FFI.serverModel.isFileTransfer.toString()}");
showLoginReqAlert(
currentCtx, FFI.serverModel.peerID, FFI.serverModel.peerName);
if(currentCtx!=null){
showLoginReqAlert(
currentCtx!, FFI.serverModel.peerID, FFI.serverModel.peerName);
}
debugPrint("from jvm:try_start_without_auth done");
break;
}

View File

@ -105,7 +105,7 @@ Map<String, dynamic> getEvent(MouseEvent evt) {
out['buttons'] = evt
.buttons; // left button: 1, right button: 2, middle button: 4, 1 | 2 = 3 (left + right)
if (evt.buttons != 0) {
lastMouseDownButtons = evt.buttons;
lastMouseDownButtons = evt.buttons!;
} else {
out['buttons'] = lastMouseDownButtons;
}
@ -125,7 +125,7 @@ void handleKey(KeyboardEvent evt, bool down) {
name = evt.code;
} else {
name = evt.key;
if (name.toLowerCase() != name.toUpperCase() &&
if (name!=null && name.toLowerCase() != name.toUpperCase() &&
name == name.toUpperCase()) {
if (!evt.shiftKey) out['shift'] = 'true';
}

View File

@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.1.9+20
environment:
sdk: ">=2.7.0 <3.0.0"
sdk: ">=2.12.0 <3.0.0"
dependencies:
flutter:
@ -32,10 +32,11 @@ dependencies:
path_provider: ^2.0.2
external_path: ^1.0.1
provider: ^5.0.0
flutter_easyloading: # not Null safety 2.2.0
git:
url: git://github.com/open-trade/flutter_easyloading
#path: flutter_easyloading
flutter_easyloading: ^3.0.3
# flutter_easyloading: # not Null safety 2.2.0
# git:
# url: git://github.com/open-trade/flutter_easyloading
# #path: flutter_easyloading
tuple: ^2.0.0
wakelock: ^0.5.2
device_info: ^2.0.2