exchange rate type toggle animated effect

This commit is contained in:
julian 2022-11-29 09:42:46 -06:00
parent 2ec8c6067b
commit f04018dbe6
3 changed files with 513 additions and 175 deletions

View file

@ -1359,8 +1359,11 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
SizedBox(
height: isDesktop ? 20 : 12,
),
RateTypeToggle(
onChanged: onRateTypeChanged,
SizedBox(
height: 60,
child: RateTypeToggle(
onChanged: onRateTypeChanged,
),
),
if (ref.read(exchangeFormStateProvider).fromAmount != null &&
ref.read(exchangeFormStateProvider).fromAmount != Decimal.zero)

View file

@ -1,15 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/toggle.dart';
import '../../../utilities/constants.dart';
class RateTypeToggle extends ConsumerWidget {
const RateTypeToggle({
@ -28,177 +26,203 @@ class RateTypeToggle extends ConsumerWidget {
.select((value) => value.exchangeRateType)) ==
ExchangeRateType.estimated;
return RoundedContainer(
padding: const EdgeInsets.all(0),
color: isDesktop
return Toggle(
onValueChanged: (value) {
if (!estimated) {
ref.read(prefsChangeNotifierProvider).exchangeRateType =
ExchangeRateType.estimated;
onChanged?.call(ExchangeRateType.estimated);
} else {
onChanged?.call(ExchangeRateType.fixed);
}
},
isOn: !estimated,
onColor: Theme.of(context).extension<StackColors>()!.textFieldDefaultBG,
offColor: isDesktop
? Theme.of(context).extension<StackColors>()!.buttonBackSecondary
: Theme.of(context).extension<StackColors>()!.popupBG,
child: Row(
children: [
Expanded(
child: ConditionalParent(
condition: isDesktop,
builder: (child) => MouseRegion(
cursor: estimated
? SystemMouseCursors.basic
: SystemMouseCursors.click,
child: child,
),
child: GestureDetector(
onTap: () {
if (!estimated) {
ref.read(prefsChangeNotifierProvider).exchangeRateType =
ExchangeRateType.estimated;
onChanged?.call(ExchangeRateType.estimated);
}
},
child: RoundedContainer(
padding: isDesktop
? const EdgeInsets.all(17)
: const EdgeInsets.all(12),
color: estimated
? Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG
: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.svg.lockOpen,
width: 12,
height: 14,
color: isDesktop
? estimated
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary
: estimated
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
const SizedBox(
width: 5,
),
Text(
"Estimate rate",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: estimated
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
)
: STextStyles.smallMed12(context).copyWith(
color: estimated
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
),
),
),
Expanded(
child: ConditionalParent(
condition: isDesktop,
builder: (child) => MouseRegion(
cursor: !estimated
? SystemMouseCursors.basic
: SystemMouseCursors.click,
child: child,
),
child: GestureDetector(
onTap: () {
if (estimated) {
ref.read(prefsChangeNotifierProvider).exchangeRateType =
ExchangeRateType.fixed;
onChanged?.call(ExchangeRateType.fixed);
}
},
child: RoundedContainer(
padding: isDesktop
? const EdgeInsets.all(17)
: const EdgeInsets.all(12),
color: !estimated
? Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG
: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.svg.lock,
width: 12,
height: 14,
color: isDesktop
? !estimated
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary
: !estimated
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
const SizedBox(
width: 5,
),
Text(
"Fixed rate",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: !estimated
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
)
: STextStyles.smallMed12(context).copyWith(
color: !estimated
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
),
),
),
],
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onIcon: Assets.svg.lockOpen,
onText: "Estimate rate",
offIcon: Assets.svg.lock,
offText: "Fixed rate",
);
//
// return RoundedContainer(
// padding: const EdgeInsets.all(0),
// color: isDesktop
// ? Theme.of(context).extension<StackColors>()!.buttonBackSecondary
// : Theme.of(context).extension<StackColors>()!.popupBG,
// child: Row(
// children: [
// Expanded(
// child: ConditionalParent(
// condition: isDesktop,
// builder: (child) => MouseRegion(
// cursor: estimated
// ? SystemMouseCursors.basic
// : SystemMouseCursors.click,
// child: child,
// ),
// child: GestureDetector(
// onTap: () {
// if (!estimated) {
// ref.read(prefsChangeNotifierProvider).exchangeRateType =
// ExchangeRateType.estimated;
// onChanged?.call(ExchangeRateType.estimated);
// }
// },
// child: RoundedContainer(
// padding: isDesktop
// ? const EdgeInsets.all(17)
// : const EdgeInsets.all(12),
// color: estimated
// ? Theme.of(context)
// .extension<StackColors>()!
// .textFieldDefaultBG
// : Colors.transparent,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// SvgPicture.asset(
// Assets.svg.lockOpen,
// width: 12,
// height: 14,
// color: isDesktop
// ? estimated
// ? Theme.of(context)
// .extension<StackColors>()!
// .accentColorBlue
// : Theme.of(context)
// .extension<StackColors>()!
// .buttonTextSecondary
// : estimated
// ? Theme.of(context)
// .extension<StackColors>()!
// .textDark
// : Theme.of(context)
// .extension<StackColors>()!
// .textSubtitle1,
// ),
// const SizedBox(
// width: 5,
// ),
// Text(
// "Estimate rate",
// style: isDesktop
// ? STextStyles.desktopTextExtraExtraSmall(context)
// .copyWith(
// color: estimated
// ? Theme.of(context)
// .extension<StackColors>()!
// .accentColorBlue
// : Theme.of(context)
// .extension<StackColors>()!
// .buttonTextSecondary,
// )
// : STextStyles.smallMed12(context).copyWith(
// color: estimated
// ? Theme.of(context)
// .extension<StackColors>()!
// .textDark
// : Theme.of(context)
// .extension<StackColors>()!
// .textSubtitle1,
// ),
// ),
// ],
// ),
// ),
// ),
// ),
// ),
// Expanded(
// child: ConditionalParent(
// condition: isDesktop,
// builder: (child) => MouseRegion(
// cursor: !estimated
// ? SystemMouseCursors.basic
// : SystemMouseCursors.click,
// child: child,
// ),
// child: GestureDetector(
// onTap: () {
// if (estimated) {
// ref.read(prefsChangeNotifierProvider).exchangeRateType =
// ExchangeRateType.fixed;
// onChanged?.call(ExchangeRateType.fixed);
// }
// },
// child: RoundedContainer(
// padding: isDesktop
// ? const EdgeInsets.all(17)
// : const EdgeInsets.all(12),
// color: !estimated
// ? Theme.of(context)
// .extension<StackColors>()!
// .textFieldDefaultBG
// : Colors.transparent,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// SvgPicture.asset(
// Assets.svg.lock,
// width: 12,
// height: 14,
// color: isDesktop
// ? !estimated
// ? Theme.of(context)
// .extension<StackColors>()!
// .accentColorBlue
// : Theme.of(context)
// .extension<StackColors>()!
// .buttonTextSecondary
// : !estimated
// ? Theme.of(context)
// .extension<StackColors>()!
// .textDark
// : Theme.of(context)
// .extension<StackColors>()!
// .textSubtitle1,
// ),
// const SizedBox(
// width: 5,
// ),
// Text(
// "Fixed rate",
// style: isDesktop
// ? STextStyles.desktopTextExtraExtraSmall(context)
// .copyWith(
// color: !estimated
// ? Theme.of(context)
// .extension<StackColors>()!
// .accentColorBlue
// : Theme.of(context)
// .extension<StackColors>()!
// .buttonTextSecondary,
// )
// : STextStyles.smallMed12(context).copyWith(
// color: !estimated
// ? Theme.of(context)
// .extension<StackColors>()!
// .textDark
// : Theme.of(context)
// .extension<StackColors>()!
// .textSubtitle1,
// ),
// ),
// ],
// ),
// ),
// ),
// ),
// ),
// ],
// ),
// );
}
}

311
lib/widgets/toggle.dart Normal file
View file

@ -0,0 +1,311 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
class Toggle extends StatefulWidget {
const Toggle({
Key? key,
this.onIcon,
this.onText,
this.offIcon,
this.offText,
this.onValueChanged,
required this.isOn,
// this.enabled = true,
this.controller,
required this.onColor,
required this.offColor,
this.decoration,
}) : super(key: key);
final String? onIcon;
final String? onText;
final String? offIcon;
final String? offText;
final void Function(bool)? onValueChanged;
final bool isOn;
// final bool enabled;
final DSBController? controller;
final Color onColor;
final Color offColor;
final BoxDecoration? decoration;
@override
ToggleState createState() => ToggleState();
}
class ToggleState extends State<Toggle> {
late final BoxDecoration? decoration;
late final Color onColor;
late final Color offColor;
late final DSBController? controller;
final bool isDesktop = Util.isDesktop;
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, double alpha) {
// return Color.alphaBlend(
// onColor.withOpacity(alpha),
// offColor,
// );
// }
// Color _colorFG(bool isOn, double alpha) {
// return Color.alphaBlend(
// onColor.withOpacity(alpha),
// offColor,
// );
// }
@override
initState() {
onColor = widget.onColor;
offColor = widget.offColor;
decoration = widget.decoration;
controller = widget.controller;
_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;
setState(() {});
},
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: decoration?.copyWith(
color: offColor,
),
);
},
),
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);
controller?.isOn = _isOn;
setState(() {});
}
_isDragging = false;
},
child: AnimatedBuilder(
animation: valueListener,
builder: (context, child) {
return AnimatedContainer(
duration: tapAnimationDuration,
height: constraint.maxHeight,
width: constraint.maxWidth / 2,
decoration: decoration?.copyWith(
color:
onColor, //_colorFG(_isOn, 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: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
widget.onIcon ?? "",
width: 12,
height: 14,
color: isDesktop
? !_isOn
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary
: !_isOn
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
const SizedBox(
width: 5,
),
Text(
widget.onText ?? "",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: !_isOn
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
)
: STextStyles.smallMed12(context).copyWith(
color: !_isOn
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
),
SizedBox(
width: constraint.maxWidth / 2,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
widget.offIcon ?? "",
width: 12,
height: 14,
color: isDesktop
? _isOn
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary
: _isOn
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
const SizedBox(
width: 5,
),
Text(
widget.offText ?? "",
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(
context)
.copyWith(
color: _isOn
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
)
: STextStyles.smallMed12(context).copyWith(
color: _isOn
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
],
),
),
),
],
),
),
],
);
},
),
);
}
}
class DSBController {
VoidCallback? activate;
bool? isOn;
}