mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-17 17:57:40 +00:00
201 lines
6.4 KiB
Dart
201 lines
6.4 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
|
|
|
class DraggableSwitchButton extends StatefulWidget {
|
|
const DraggableSwitchButton({
|
|
Key? key,
|
|
this.onItem,
|
|
this.offItem,
|
|
this.onValueChanged,
|
|
required this.isOn,
|
|
this.enabled = true,
|
|
this.controller,
|
|
}) : super(key: key);
|
|
|
|
final Widget? onItem;
|
|
final Widget? offItem;
|
|
final void Function(bool)? onValueChanged;
|
|
final bool isOn;
|
|
final bool enabled;
|
|
final DSBController? controller;
|
|
|
|
@override
|
|
DraggableSwitchButtonState createState() => DraggableSwitchButtonState();
|
|
}
|
|
|
|
class DraggableSwitchButtonState extends State<DraggableSwitchButton> {
|
|
late bool _isOn;
|
|
bool get isOn => _isOn;
|
|
|
|
late bool _enabled;
|
|
|
|
late ValueNotifier<double> valueListener;
|
|
|
|
final tapAnimationDuration = const Duration(milliseconds: 150);
|
|
bool _isDragging = false;
|
|
|
|
Color _colorBG(bool isOn, bool enabled, double alpha) {
|
|
if (enabled) {
|
|
return Color.alphaBlend(
|
|
Theme.of(context)
|
|
.extension<StackColors>()!
|
|
.switchBGOn
|
|
.withOpacity(alpha),
|
|
Theme.of(context).extension<StackColors>()!.switchBGOff,
|
|
);
|
|
}
|
|
return Theme.of(context).extension<StackColors>()!.switchBGDisabled;
|
|
}
|
|
|
|
Color _colorFG(bool isOn, bool enabled, double alpha) {
|
|
if (enabled) {
|
|
return Color.alphaBlend(
|
|
Theme.of(context)
|
|
.extension<StackColors>()!
|
|
.switchCircleOn
|
|
.withOpacity(alpha),
|
|
Theme.of(context).extension<StackColors>()!.switchCircleOff,
|
|
);
|
|
}
|
|
return Theme.of(context).extension<StackColors>()!.switchCircleDisabled;
|
|
}
|
|
|
|
@override
|
|
initState() {
|
|
_isOn = widget.isOn;
|
|
_enabled = widget.enabled;
|
|
valueListener = _isOn ? ValueNotifier(1.0) : ValueNotifier(0.0);
|
|
|
|
widget.controller?.activate = () {
|
|
_isOn = !_isOn;
|
|
// widget.onValueChanged?.call(_isOn);
|
|
valueListener.value = _isOn ? 1.0 : 0.0;
|
|
};
|
|
super.initState();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
valueListener.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
debugPrint("BUILD: $runtimeType");
|
|
|
|
return GestureDetector(
|
|
onTap: () {
|
|
_isOn = !_isOn;
|
|
widget.onValueChanged?.call(_isOn);
|
|
valueListener.value = _isOn ? 1.0 : 0.0;
|
|
},
|
|
child: LayoutBuilder(
|
|
builder: (context, constraint) {
|
|
return Stack(
|
|
children: [
|
|
AnimatedBuilder(
|
|
animation: valueListener,
|
|
builder: (context, child) {
|
|
return AnimatedContainer(
|
|
duration: tapAnimationDuration,
|
|
height: constraint.maxHeight,
|
|
width: constraint.maxWidth,
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(
|
|
constraint.maxHeight / 2,
|
|
),
|
|
color: _colorBG(_isOn, _enabled, valueListener.value),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
Builder(
|
|
builder: (context) {
|
|
final handle = GestureDetector(
|
|
key: const Key("draggableSwitchButtonSwitch"),
|
|
onHorizontalDragStart: (_) => _isDragging = true,
|
|
onHorizontalDragUpdate: (details) {
|
|
valueListener.value = (valueListener.value +
|
|
details.delta.dx / constraint.maxWidth)
|
|
.clamp(0.0, 1.0);
|
|
},
|
|
onHorizontalDragEnd: (details) {
|
|
bool oldValue = _isOn;
|
|
if (valueListener.value > 0.5) {
|
|
valueListener.value = 1.0;
|
|
_isOn = true;
|
|
} else {
|
|
valueListener.value = 0.0;
|
|
_isOn = false;
|
|
}
|
|
if (_isOn != oldValue) {
|
|
widget.onValueChanged?.call(_isOn);
|
|
}
|
|
_isDragging = false;
|
|
},
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(2),
|
|
child: AnimatedBuilder(
|
|
animation: valueListener,
|
|
builder: (context, child) {
|
|
return AnimatedContainer(
|
|
duration: tapAnimationDuration,
|
|
height: constraint.maxHeight - 4,
|
|
width: constraint.maxWidth / 2 - 4,
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(
|
|
constraint.maxHeight / 2,
|
|
),
|
|
color: _colorFG(
|
|
_isOn, _enabled, valueListener.value),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
return AnimatedBuilder(
|
|
animation: valueListener,
|
|
builder: (context, child) {
|
|
return AnimatedAlign(
|
|
duration:
|
|
_isDragging ? Duration.zero : tapAnimationDuration,
|
|
alignment: Alignment(valueListener.value * 2 - 1, 0.5),
|
|
child: child,
|
|
);
|
|
},
|
|
child: handle,
|
|
);
|
|
},
|
|
),
|
|
IgnorePointer(
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
children: [
|
|
SizedBox(
|
|
width: constraint.maxWidth / 2,
|
|
child: Center(
|
|
child: widget.onItem,
|
|
),
|
|
),
|
|
SizedBox(
|
|
width: constraint.maxWidth / 2,
|
|
child: Center(
|
|
child: widget.offItem,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class DSBController {
|
|
VoidCallback? activate;
|
|
}
|