stack_wallet/lib/widgets/custom_buttons/draggable_switch_button.dart
2024-05-27 18:01:41 -06:00

215 lines
6.6 KiB
Dart

/*
* This file is part of Stack Wallet.
*
* Copyright (c) 2023 Cypher Stack
* All Rights Reserved.
* The code is distributed under GPLv3 license, see LICENSE file for details.
* Generated by Cypher Stack on 2023-05-26
*
*/
import 'package:flutter/material.dart';
import '../../themes/stack_colors.dart';
class DraggableSwitchButton extends StatefulWidget {
const DraggableSwitchButton({
super.key,
this.onItem,
this.offItem,
this.onValueChanged,
required this.isOn,
this.enabled = true,
this.controller,
});
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) {
final 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;
}