Generalize cake phone settings tile and make it reusable

Add Auto Renew settings page
This commit is contained in:
OmarHatem28 2022-07-06 05:51:19 +02:00
parent 6287afccef
commit a940eff679
7 changed files with 420 additions and 189 deletions

View file

@ -6,6 +6,7 @@ import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
import 'package:cake_wallet/src/screens/buy/pre_order_page.dart';
import 'package:cake_wallet/src/screens/cake_phone/cake_phone_auth_page.dart';
import 'package:cake_wallet/src/screens/cake_phone/phone_number_service/auto_renew_settings_page.dart';
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
@ -69,9 +70,9 @@ import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
import 'package:cake_wallet/src/screens/cake_phone/cake_phone_welcome_page.dart';
import 'package:cake_wallet/src/screens/cake_phone/cake_phone_verification_page.dart';
import 'package:cake_wallet/src/screens/cake_phone/cake_phone_products_page.dart';
import 'package:cake_wallet/src/screens/cake_phone/cake_phone_products/phone_number_product_page.dart';
import 'package:cake_wallet/src/screens/cake_phone/phone_number_service/phone_number_product_page.dart';
import 'package:cake_wallet/src/screens/cake_phone/active_services_page.dart';
import 'package:cake_wallet/src/screens/cake_phone/number_settings_page.dart';
import 'package:cake_wallet/src/screens/cake_phone/phone_number_service/number_settings_page.dart';
RouteSettings currentRouteSettings;
@ -385,6 +386,15 @@ Route<dynamic> createRoute(RouteSettings settings) {
),
);
case Routes.autoRenewSettings:
return MaterialPageRoute<AutoRenewSettingsPage>(
settings: RouteSettings(name: Routes.autoRenewSettings),
builder: (_) => AutoRenewSettingsPage(
phoneNumberService: settings.arguments as PhoneNumberService,
phonePlanViewModel: getIt.get<PhonePlanViewModel>(),
),
);
default:
return MaterialPageRoute<void>(
builder: (_) => Scaffold(body: Center(child: Text(S.current.router_no_route(settings.name)))));

View file

@ -67,4 +67,5 @@ class Routes {
static const phoneNumberProduct = '/phone_number_product';
static const cakePhoneActiveServices = '/cake_phone_active_services';
static const numberSettings = '/number_settings';
static const autoRenewSettings = '/auto_renew_settings';
}

View file

@ -0,0 +1,237 @@
import 'package:cake_wallet/buy/buy_amount.dart';
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/cake_phone_entities/phone_number_service.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/src/screens/cake_phone/widgets/cake_phone_settings_tile.dart';
import 'package:cake_wallet/src/screens/cake_phone/widgets/plan_card.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/view_model/cake_phone/phone_plan_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class AutoRenewSettingsPage extends BasePage {
AutoRenewSettingsPage({@required this.phoneNumberService, @required this.phonePlanViewModel});
final PhoneNumberService phoneNumberService;
final PhonePlanViewModel phonePlanViewModel;
@override
Widget body(BuildContext context) => AutoRenewSettingsBody(phoneNumberService, phonePlanViewModel);
@override
Widget middle(BuildContext context) {
return Text(
S.of(context).auto_renew_settings,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w700,
fontFamily: 'Lato',
color: Theme.of(context).primaryTextTheme.title.color),
);
}
}
class AutoRenewSettingsBody extends StatefulWidget {
AutoRenewSettingsBody(this.phoneNumberService, this.phonePlanViewModel);
final PhoneNumberService phoneNumberService;
final PhonePlanViewModel phonePlanViewModel;
@override
AutoRenewSettingsBodyState createState() => AutoRenewSettingsBodyState();
}
class AutoRenewSettingsBodyState extends State<AutoRenewSettingsBody> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 16),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.symmetric(vertical: 20),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Text(
"${S.of(context).auto_renew} ${S.of(context).term}",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
color: Theme.of(context).primaryTextTheme.title.color,
fontFamily: 'Lato',
),
),
),
Padding(
padding: const EdgeInsets.all(24).copyWith(top: 8),
child: Text(
S.of(context).auto_renew_term_description,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).accentTextTheme.subhead.color,
fontFamily: 'Lato',
),
),
),
Observer(builder: (_) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Row(
children: widget.phonePlanViewModel.servicePlans
.map((e) => PlanCard(
plan: e,
onTap: () {
if (widget.phonePlanViewModel.selectedPlan != e) {
widget.phonePlanViewModel.selectedPlan = e;
}
},
isSelected: widget.phonePlanViewModel.selectedPlan == e,
))
.toList(),
),
),
);
}),
const SizedBox(height: 40),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CakePhoneSettingsTile(
title: S.of(context).free_sms_email_forward,
value: Observer(builder: (_) {
return Text(
"${widget.phonePlanViewModel.selectedPlan.quantity}, " +
"${S.of(context).then} " +
"\$${(widget.phonePlanViewModel.rateInCents / 100).toStringAsFixed(2)} " +
"${S.of(context).per_message}",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Theme.of(context).primaryTextTheme.title.color,
),
);
}),
),
CakePhoneSettingsTile(
title: S.of(context).phone_number,
value: Text(
widget.phoneNumberService.phoneNumber,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Theme.of(context).primaryTextTheme.title.color,
),
),
),
],
),
),
],
),
bottomSectionPadding: EdgeInsets.only(bottom: 24, right: 24, left: 24),
bottomSection: Column(
children: <Widget>[
PrimaryButton(
onPressed: () {
},
text: "${S.of(context).disable} ${S.of(context).auto_renew}",
color: Theme.of(context).accentTextTheme.caption.backgroundColor,
textColor: Theme.of(context).primaryTextTheme.title.color,
),
const SizedBox(height: 8),
PrimaryButton(
onPressed: () {
},
text: "${S.of(context).update} ${S.of(context).auto_renew}",
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
),
],
),
),
);
}
Widget receiptRow(String title, Widget value) {
return Padding(
padding: const EdgeInsets.only(top: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme.subhead.color,
),
),
value,
],
),
);
}
Widget amountText(double amount) {
return Text(
"\$${amount.roundToDouble() == amount ? amount.round() : amount}",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: Theme.of(context).primaryTextTheme.title.color,
),
);
}
Widget cryptoAmount(double totalPrice) {
return FutureBuilder<BuyAmount>(
future: MoonPayBuyProvider(wallet: getIt.get<AppStore>().wallet)
.calculateAmount(totalPrice.toString(), FiatCurrency.usd.title),
builder: (context, AsyncSnapshot<BuyAmount> snapshot) {
double sourceAmount;
double destAmount;
if (snapshot.hasData) {
sourceAmount = snapshot.data.sourceAmount;
destAmount = snapshot.data.destAmount;
} else {
sourceAmount = 0.0;
destAmount = 0.0;
}
return Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
"${sourceAmount} ${getIt.get<AppStore>().wallet.currency.toString()}",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: Theme.of(context).primaryTextTheme.title.color,
),
),
Text(
"${destAmount} ${FiatCurrency.usd.title}",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme.subhead.color,
),
),
],
);
},
);
}
}

View file

@ -1,6 +1,6 @@
import 'package:cake_wallet/entities/cake_phone_entities/service_plan.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/src/screens/cake_phone/widgets/cake_phone_settings_tile.dart';
import 'package:cake_wallet/view_model/cake_phone/phone_plan_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
@ -65,7 +65,7 @@ class NumberSettingsBodyState extends State<NumberSettingsBody> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
numberSettingsTile(
CakePhoneSettingsTile(
title: S.of(context).phone_number,
value: Text(
widget.phoneNumberService.phoneNumber,
@ -76,33 +76,33 @@ class NumberSettingsBodyState extends State<NumberSettingsBody> {
),
),
),
numberSettingsTile(
CakePhoneSettingsTile(
title: S.of(context).auto_renew_settings,
value: InkWell(
onTap: () {},
child: Row(
children: [
Expanded(
child: Text(
"${S.of(context).renews_every} ${selectedPhoneNumberPlan?.duration ?? 0} ${S.of(context).month} " +
"${S.of(context).for_amount} \$${selectedPhoneNumberPlan?.price ?? 0}",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Theme.of(context).primaryTextTheme.title.color,
),
value: Row(
children: [
Expanded(
child: Text(
"${S.of(context).renews_every} ${selectedPhoneNumberPlan?.duration ?? 0} ${S.of(context).month} " +
"${S.of(context).for_amount} \$${selectedPhoneNumberPlan?.price ?? 0}",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Theme.of(context).primaryTextTheme.title.color,
),
),
Icon(
Icons.arrow_forward_ios,
color: Theme.of(context).primaryTextTheme.title.color,
size: 16,
),
],
),
),
Icon(
Icons.arrow_forward_ios,
color: Theme.of(context).primaryTextTheme.title.color,
size: 16,
),
],
),
onTap: () {
Navigator.pushNamed(context, Routes.autoRenewSettings, arguments: widget.phoneNumberService);
},
),
numberSettingsTile(
CakePhoneSettingsTile(
title: S.of(context).manually_add_balance,
value: InkWell(
onTap: () {},
@ -127,7 +127,7 @@ class NumberSettingsBodyState extends State<NumberSettingsBody> {
),
),
),
numberSettingsTile(
CakePhoneSettingsTile(
value: Row(
children: [
Expanded(
@ -155,32 +155,4 @@ class NumberSettingsBodyState extends State<NumberSettingsBody> {
),
);
}
Widget numberSettingsTile({String title, @required Widget value}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (title != null)
Text(
title,
style: TextStyle(
color: Theme.of(context).accentTextTheme.subhead.color,
),
),
Container(
padding: const EdgeInsets.only(top: 8, bottom: 16),
width: double.infinity,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).accentTextTheme.title.backgroundColor,
),
),
),
child: value,
),
const SizedBox(height: 24),
],
);
}
}

View file

@ -3,6 +3,7 @@ import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/cake_phone/widgets/cake_phone_settings_tile.dart';
import 'package:cake_wallet/src/screens/cake_phone/widgets/plan_card.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
@ -53,8 +54,8 @@ class PhoneNumberProductBody extends StatefulWidget {
class PhoneNumberProductBodyState extends State<PhoneNumberProductBody> {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 16),
return Padding(
padding: const EdgeInsets.only(top: 16),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.symmetric(vertical: 20),
content: Column(
@ -110,23 +111,9 @@ class PhoneNumberProductBodyState extends State<PhoneNumberProductBody> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
S.of(context).free_sms_email_forward,
style: TextStyle(
color: Theme.of(context).accentTextTheme.subhead.color,
),
),
Container(
padding: const EdgeInsets.only(top: 8, bottom: 16),
width: double.infinity,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).accentTextTheme.title.backgroundColor,
),
),
),
child: Observer(builder: (_) {
CakePhoneSettingsTile(
title: S.of(context).free_sms_email_forward,
value: Observer(builder: (_) {
return Text(
"${widget.phonePlanViewModel.selectedPlan.quantity}, " +
"${S.of(context).then} " +
@ -140,109 +127,91 @@ class PhoneNumberProductBodyState extends State<PhoneNumberProductBody> {
);
}),
),
const SizedBox(height: 24),
Text(
S.of(context).phone_number_country,
style: TextStyle(
color: Theme.of(context).accentTextTheme.subhead.color,
),
),
Container(
padding: const EdgeInsets.only(top: 8, bottom: 16),
width: double.infinity,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).accentTextTheme.title.backgroundColor,
),
),
),
child: InkWell(
onTap: () {
showPopUp<void>(
context: context,
builder: (_) => Picker(
items: countryList,
displayItem: (dynamic country) {
final Country _country = country as Country;
return "${_country.name} (+${_country.phoneCode})";
},
selectedAtIndex: countryList.indexOf(widget.phonePlanViewModel.selectedCountry),
mainAxisAlignment: MainAxisAlignment.start,
onItemSelected: (Country country) {
widget.phonePlanViewModel.selectedCountry = country;
},
images: countryList
.map((e) => Image.asset(
CountryPickerUtils.getFlagImageAssetPath(e.isoCode),
height: 20.0,
width: 36.0,
fit: BoxFit.fill,
package: "country_pickers",
))
.toList(),
isSeparated: false,
hintText: S.of(context).search_country,
matchingCriteria: (dynamic country, String searchText) {
final Country _country = country as Country;
searchText = searchText.toLowerCase();
return _country.name.toLowerCase().contains(searchText) ||
_country.phoneCode.contains(searchText) ||
_country.isoCode.toLowerCase().contains(searchText) ||
_country.iso3Code.toLowerCase().contains(searchText);
},
CakePhoneSettingsTile(
title: S.of(context).phone_number_country,
value: Observer(builder: (_) {
return Row(
children: [
Image.asset(
CountryPickerUtils.getFlagImageAssetPath(widget.phonePlanViewModel.selectedCountry.isoCode),
height: 20.0,
width: 36.0,
fit: BoxFit.fill,
package: "country_pickers",
),
);
},
child: Observer(builder: (_) {
return Row(
children: [
Image.asset(
CountryPickerUtils.getFlagImageAssetPath(
widget.phonePlanViewModel.selectedCountry.isoCode),
height: 20.0,
width: 36.0,
fit: BoxFit.fill,
package: "country_pickers",
),
Expanded(
child: Row(
children: [
Flexible(
child: Padding(
padding: const EdgeInsets.only(left: 8, right: 6),
child: Text(
widget.phonePlanViewModel.selectedCountry.name,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Theme.of(context).primaryTextTheme.title.color,
),
Expanded(
child: Row(
children: [
Flexible(
child: Padding(
padding: const EdgeInsets.only(left: 8, right: 6),
child: Text(
widget.phonePlanViewModel.selectedCountry.name,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Theme.of(context).primaryTextTheme.title.color,
),
),
),
Text(
"(+${widget.phonePlanViewModel.selectedCountry.phoneCode})",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Theme.of(context).primaryTextTheme.title.color,
),
),
Text(
"(+${widget.phonePlanViewModel.selectedCountry.phoneCode})",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
color: Theme.of(context).primaryTextTheme.title.color,
),
],
),
),
],
),
Icon(
Icons.arrow_forward_ios,
color: Theme.of(context).primaryTextTheme.title.color,
size: 16,
),
],
);
}),
),
),
Icon(
Icons.arrow_forward_ios,
color: Theme.of(context).primaryTextTheme.title.color,
size: 16,
),
],
);
}),
onTap: () {
showPopUp<void>(
context: context,
builder: (_) => Picker(
items: countryList,
displayItem: (dynamic country) {
final Country _country = country as Country;
return "${_country.name} (+${_country.phoneCode})";
},
selectedAtIndex: countryList.indexOf(widget.phonePlanViewModel.selectedCountry),
mainAxisAlignment: MainAxisAlignment.start,
onItemSelected: (Country country) {
widget.phonePlanViewModel.selectedCountry = country;
},
images: countryList
.map((e) => Image.asset(
CountryPickerUtils.getFlagImageAssetPath(e.isoCode),
height: 20.0,
width: 36.0,
fit: BoxFit.fill,
package: "country_pickers",
))
.toList(),
isSeparated: false,
hintText: S.of(context).search_country,
matchingCriteria: (dynamic country, String searchText) {
final Country _country = country as Country;
searchText = searchText.toLowerCase();
return _country.name.toLowerCase().contains(searchText) ||
_country.phoneCode.contains(searchText) ||
_country.isoCode.toLowerCase().contains(searchText) ||
_country.iso3Code.toLowerCase().contains(searchText);
},
),
);
},
),
const SizedBox(height: 49),
const SizedBox(height: 25),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -261,15 +230,7 @@ class PhoneNumberProductBodyState extends State<PhoneNumberProductBody> {
children: [
GestureDetector(
onTap: () => widget.phonePlanViewModel.removeAdditionalSms(),
child: Container(
padding: const EdgeInsets.all(8),
margin: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: Theme.of(context).accentTextTheme.body2.color,
shape: BoxShape.circle,
),
child: Icon(Icons.remove, color: Colors.white, size: 15),
),
child: icon(Icons.remove),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
@ -286,15 +247,7 @@ class PhoneNumberProductBodyState extends State<PhoneNumberProductBody> {
),
GestureDetector(
onTap: () => widget.phonePlanViewModel.addAdditionalSms(),
child: Container(
padding: const EdgeInsets.all(8),
margin: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: Theme.of(context).accentTextTheme.body2.color,
shape: BoxShape.circle,
),
child: Icon(Icons.add, color: Colors.white, size: 15),
),
child: icon(Icons.add),
),
],
),
@ -408,6 +361,18 @@ class PhoneNumberProductBodyState extends State<PhoneNumberProductBody> {
);
}
Widget icon(IconData icon) {
return Container(
padding: const EdgeInsets.all(8),
margin: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: Theme.of(context).accentTextTheme.body2.color,
shape: BoxShape.circle,
),
child: Icon(icon, color: Colors.white, size: 15),
);
}
Widget receiptRow(String title, Widget value) {
return Padding(
padding: const EdgeInsets.only(top: 24),

View file

@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
class CakePhoneSettingsTile extends StatelessWidget {
const CakePhoneSettingsTile({Key key, @required this.value, this.title, this.onTap}) : super(key: key);
final String title;
final Widget value;
final Function() onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (title != null)
Text(
title,
style: TextStyle(
color: Theme.of(context).accentTextTheme.subhead.color,
),
),
Container(
padding: const EdgeInsets.only(top: 8, bottom: 16),
width: double.infinity,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).accentTextTheme.title.backgroundColor,
),
),
),
child: value,
),
const SizedBox(height: 24),
],
),
);
}
}

View file

@ -581,5 +581,10 @@
"renews_every": "Renews every",
"for_amount": "for",
"manually_add_balance": "Manually Add Balance",
"block_incoming_sms": "Block incoming SMS"
"block_incoming_sms": "Block incoming SMS",
"auto_renew": "Auto-Renew",
"term": "Term",
"disable": "Disable",
"update": "Update",
"auto_renew_term_description": "Optionally auto-renew your phone number if a sufficient balance is available. We will attempt to pay the balance a day before the service is set to expire."
}