Untitled
unknown
plain_text
a year ago
23 kB
8
Indexable
import 'dart:math';
import 'package:connectivity_widget/connectivity_widget.dart';
import 'package:ctexpeess/constants/dimensions.dart';
import 'package:ctexpeess/constants/routes.dart';
import 'package:ctexpeess/constants/styles.dart';
import 'package:ctexpeess/core/config/color.dart';
import 'package:ctexpeess/core/config/constant.dart';
import 'package:ctexpeess/core/utils/auth_app_bar.dart';
import 'package:ctexpeess/core/utils/date_time_formatter.dart';
import 'package:ctexpeess/core/utils/helper.dart';
import 'package:ctexpeess/core/widget/cus_elevated_button.dart';
import 'package:ctexpeess/core/widget/offline_snack.dart';
import 'package:ctexpeess/core/widget/pixel_perfect_wraper.dart';
import 'package:ctexpeess/feature/currency_converter/data/model/forex_rate.model.dart';
import 'package:ctexpeess/feature/currency_converter/presentation/view/forex_rate.cubit.dart';
import 'package:ctexpeess/feature/currency_converter/presentation/view/forex_rate.state.dart';
import 'package:ctexpeess/feature/currency_converter/view/widget/currency_bottom_sheet_widget.dart';
import 'package:ctexpeess/feature/currency_converter/view/widget/today_rate.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:ctexpeess/core/utils/bottom_sheet_container.dart';
import 'package:ctexpeess/core/widget/svg_builder.dart';
class CurrencyConverterPage extends StatefulWidget {
const CurrencyConverterPage({super.key});
@override
State<CurrencyConverterPage> createState() => _CurrencyConverterPageState();
}
class _CurrencyConverterPageState extends State<CurrencyConverterPage> {
// start of keys and controllers
final TextEditingController _toAmountTxController = TextEditingController();
final TextEditingController _fromAmountTxController = TextEditingController();
// end of keys and controller
// start of states
String _lastUpdatedAt = "";
final String _fromCurrency = 'AUD';
final String _fromCurrencyFlag = SvgAsset.austrila;
ForexRate? toCurrency;
// end of states
// start of functions
_handleSearchClicked(bool isFromCurrency) async {
final selectedCurrency = await showModalBottomSheet(
context: context,
useRootNavigator: true,
useSafeArea: true,
isScrollControlled: true,
builder: (BuildContext context) {
return BottomSheetContainer(
size: 650,
child: CurrencyBottomSheetWidget(
onCurrencySelected: (selectedCurrency) {
Navigator.pop(context, selectedCurrency);
},
),
);
},
);
if (selectedCurrency != null) {
setState(() {
setState(() {
toCurrency = selectedCurrency;
});
_updateToCountryCurrency();
});
}
}
_initCurrency() {
ForexRateState forex = context.read<ForexRateCubit>().state;
if (forex is ForexRateSuccess) {
final nprCurrency = forex.forexRateModel.data.firstWhere(
(currency) => currency.code == "NPR",
orElse: () => forex.forexRateModel.data.first,
);
setState(() {
toCurrency = nprCurrency;
});
_setLastUpdatedAt(forex.forexRateModel.data);
_updateToCountryCurrency();
}
}
_setLastUpdatedAt(List<ForexRate> currencies) {
if (currencies.isEmpty) return;
final latestCurrency = currencies.reduce((curr, next) {
final currDate = DateTime.parse(curr.updatedAt).toUtc();
final nextDate = DateTime.parse(next.updatedAt).toUtc();
return currDate.compareTo(nextDate) > 0 ? curr : next;
});
setState(() {
_lastUpdatedAt = ausDateTimeFormatter(
DateTime.parse(latestCurrency.updatedAt).toUtc().toLocal());
});
}
_getForexRate({bool force = false}) async {
context.read<ForexRateCubit>().getForexRate(force: true);
_initCurrency();
}
void _updateTextFieldValue(TextEditingController controller, String newValue,
{int? cursorPosition}) {
final previousCursorPosition = controller.selection.baseOffset;
final usePosition = cursorPosition ?? previousCursorPosition;
controller.value = TextEditingValue(
text: newValue,
selection: TextSelection.collapsed(
offset: usePosition > -1
? min(usePosition, newValue.length)
: newValue.length,
),
);
}
_updateFromCountryCurrency({String? value}) {
try {
String toValue = _toAmountTxController.text.trim();
if (toValue.isEmpty) {
_updateTextFieldValue(_fromAmountTxController, '');
return;
}
double? toAmount = _extractAmountFromFormatted(toValue);
if (toAmount == null || toAmount == 0) {
_updateTextFieldValue(
_fromAmountTxController, formatCurrency(1, symbol: ''));
return;
}
String formattedValue = formatCurrency(toAmount, symbol: '');
//
// if (formattedValue == toValue && value != null) {
// return;
// }
_updateTextFieldValue(_toAmountTxController, formattedValue);
if (toCurrency == null) {
_updateTextFieldValue(
_fromAmountTxController, formatCurrency(1, symbol: ''));
return;
}
double? rate = double.tryParse(toCurrency?.exchangeRate ?? '0');
if (rate == null || rate == 0) {
_updateTextFieldValue(
_fromAmountTxController, formatCurrency(0, symbol: ''));
return;
}
double fromAmount = toAmount / rate;
_updateTextFieldValue(
_fromAmountTxController, formatCurrency(fromAmount, symbol: ''));
} catch (e) {
_updateTextFieldValue(
_fromAmountTxController, formatCurrency(1, symbol: ''));
}
}
double? _extractAmountFromFormatted(String value) {
double? fromAmount = double.tryParse(value.replaceAll(',', ''));
return fromAmount;
}
_updateToCountryCurrency({String? value}) {
try {
String fromValue = _fromAmountTxController.text.trim();
if (fromValue.isEmpty) {
_updateTextFieldValue(_toAmountTxController, '');
return;
}
double? fromAmount = _extractAmountFromFormatted(fromValue);
if (fromAmount == null || fromAmount == 0) {
_fromAmountTxController.text = formatCurrency(1, symbol: '');
// _updateTextFieldValue(
// _toAmountTxController, formatCurrency(1, symbol: ''));
return;
}
String formattedValue = formatCurrency(fromAmount, symbol: '');
// if (formattedValue == fromValue && value != null) {
// return;
// }
_updateTextFieldValue(_fromAmountTxController, formattedValue);
if (toCurrency == null) {
_updateTextFieldValue(
_toAmountTxController, formatCurrency(1, symbol: ''));
return;
}
double? rate = double.tryParse(toCurrency?.exchangeRate ?? '0');
if (rate == null || rate == 0) {
_updateTextFieldValue(
_toAmountTxController, formatCurrency(0, symbol: ''));
return;
}
double toAmount = fromAmount * rate;
_updateTextFieldValue(
_toAmountTxController, formatCurrency(toAmount, symbol: ''));
} catch (e) {
_updateTextFieldValue(
_toAmountTxController, formatCurrency(1, symbol: ''));
}
}
// end of functions
@override
void initState() {
_fromAmountTxController.text = '1';
_getForexRate();
super.initState();
}
@override
void dispose() {
_toAmountTxController.dispose();
_fromAmountTxController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return BlocConsumer<ForexRateCubit, ForexRateState>(
listenWhen: (previous, current) => previous != current,
listener: (_, forex) {
if (forex is ForexRateSuccess &&
forex.forexRateModel.data.isNotEmpty) {
final nprCurrency = forex.forexRateModel.data.firstWhere(
(currency) => currency.code == "NPR",
orElse: () => forex.forexRateModel.data.first,
);
setState(() {
toCurrency = nprCurrency;
});
_setLastUpdatedAt(forex.forexRateModel.data);
_updateToCountryCurrency();
}
},
builder: (context, state) {
return PixelPerfectWrapper(
assetPath: ImageAsset.kCurrencyConverterPage,
child: Scaffold(
appBar: authAppBar(context,
title: "Currency Converter", showBackButton: true),
body: ConnectivityWidget(
onlineCallback: () => _getForexRate(force: true),
offlineBanner: const OfflineSnack(),
builder: (_, isOnline) =>
LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.minHeight,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.only(
left: Dimensions.HORIZONTAL_PADDING,
right: Dimensions.HORIZONTAL_PADDING,
top: Dimensions.P20),
child: Column(children: [
Text(
"Set your default currencies in the settings for quicker access.",
style: bodyCaptionStyle.copyWith(
color: CusColor.kBlack300),
),
const SizedBox(
height: Spacers.SPACER_20,
),
Container(
height: 48,
padding: const EdgeInsets.symmetric(
horizontal: Dimensions.P8),
decoration: BoxDecoration(
border: Border.all(color: CusColor.kBlack800),
borderRadius: BorderRadius.circular(
RadiusDimensions.ROUND_6),
),
child: Row(
children: [
SvgBuilder(
svgPath: _fromCurrencyFlag,
height: 24,
width: 24,
),
const SizedBox(
width: 10,
),
Text(_fromCurrency.toUpperCase(),
style: paragraphStyle.copyWith(
color: CusColor.kBlack100)),
const SizedBox(
width: Spacers.SPACER_24,
),
Expanded(
child: TextField(
controller: _fromAmountTxController,
textAlign: TextAlign.end,
keyboardType: TextInputType.number,
inputFormatters: const [],
decoration: const InputDecoration(
hintStyle: TextStyle(
fontSize: 16,
color: CusColor.kBlack),
border: InputBorder.none,
),
onChanged: (value) {
_updateToCountryCurrency(value: value);
},
),
)
],
),
),
const Padding(
padding: EdgeInsets.all(Dimensions.P8),
child: SvgBuilder(
svgPath: SvgAsset.kAIcDropDown, height: 24),
),
Skeletonizer(
enabled: state is ForexRateLoading,
child: Container(
height: 48,
padding: const EdgeInsets.symmetric(
horizontal: Dimensions.P8),
decoration: BoxDecoration(
border: Border.all(color: CusColor.kBlack800),
borderRadius: BorderRadius.circular(
RadiusDimensions.ROUND_6),
),
child: GestureDetector(
onTap: () => _handleSearchClicked(true),
child: Row(
children: [
SvgBuilder(
svgUrl: toCurrency?.photoUrl ?? '',
height: 24,
width: 24,
),
const SizedBox(
width: 10,
),
Text(toCurrency?.code ?? ''.toUpperCase(),
style: paragraphStyle.copyWith(
color: CusColor.kBlack100)),
const Icon(
Icons.arrow_drop_down,
color: CusColor.kBlack100,
),
const SizedBox(
width: Spacers.SPACER_24,
),
Expanded(
child: TextField(
controller: _toAmountTxController,
textAlign: TextAlign.end,
keyboardType: const TextInputType
.numberWithOptions(decimal: true),
inputFormatters: const [],
decoration: const InputDecoration(
hintText: '',
hintStyle: TextStyle(
fontSize: 16,
color: CusColor.kBlack),
border: InputBorder.none,
),
onChanged: (value) {
_updateFromCountryCurrency(
value: value);
},
),
),
],
),
),
),
),
const SizedBox(
height: Spacers.SPACER_12,
),
SizedBox(
width: double.infinity,
child: CusElevatedButton(
onPressed: () {
context.push(Routes.routeUrl(
Routes.TRANSFER_FRAGMENT));
},
text: 'Create Transfer',
disabled: state is ForexRateInitial ||
state is ForexRateLoading,
)),
const SizedBox(
height: Spacers.SPACER_12,
),
Container(
padding: const EdgeInsets.all(Dimensions.P8),
decoration: BoxDecoration(
color: CusColor.kY1000,
borderRadius: BorderRadius.circular(
RadiusDimensions.ROUND_4)),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Row(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Padding(
padding:
EdgeInsets.all(Dimensions.P2),
child: SvgBuilder(
width: 18,
height: 18,
color: CusColor.kB100,
svgPath: SvgAsset.infoCircleSolid,
),
),
const SizedBox(
width: Spacers.SPACER_8,
),
Flexible(
child: Text(
"Service fees will be shown after creating the transfer.",
maxLines: 2,
overflow: TextOverflow.ellipsis,
style:
paragraphSemiBoldStyle.copyWith(
color: CusColor.kB100),
),
)
],
),
),
],
),
),
const SizedBox(
height: Spacers.SPACER_24,
),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Today's Rate",
style: bodyBoldStyle.copyWith(
color: CusColor.kBlack100),
),
Skeletonizer(
enabled: state is! ForexRateSuccess,
child: Text(
"Last updated: $_lastUpdatedAt",
style: paragraphStyle.copyWith(
color: CusColor.kBlack300),
),
),
const SizedBox(
height: Spacers.SPACER_20,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Currency",
style: bodyMediumStyle.copyWith(
color: CusColor.kBlack100),
),
Text(
"Rate",
style: bodyMediumStyle.copyWith(
color: CusColor.kBlack100),
),
],
),
const SizedBox(
height: Spacers.SPACER_12,
),
SizedBox(
height: max(250, constraints.minHeight),
child: CurrencyListWidget(
onCurrencySelected: (selectedCurrency) {
setState(() {
toCurrency = selectedCurrency;
});
_updateToCountryCurrency();
},
),
),
],
),
)
]),
),
),
),
);
}),
),
),
);
});
}
}
Editor is loading...
Leave a Comment