From 57f8ada2b070577999be15261df4bb1ccd432c40 Mon Sep 17 00:00:00 2001 From: OmarHatem28 Date: Fri, 1 Jul 2022 19:15:07 +0200 Subject: [PATCH] Add initial UI for phone number product page --- lib/entities/service_plan.dart | 21 ++ lib/router.dart | 6 + lib/routes.dart | 1 + .../cake_phone/cake_phone_auth_page.dart | 2 +- .../phone_number_product_page.dart | 201 ++++++++++++++++++ .../cake_phone/cake_phone_products_page.dart | 77 +++---- .../cake_phone_verification_page.dart | 2 +- res/values/strings_en.arb | 11 +- 8 files changed, 282 insertions(+), 39 deletions(-) create mode 100644 lib/entities/service_plan.dart create mode 100644 lib/src/screens/cake_phone/cake_phone_products/phone_number_product_page.dart diff --git a/lib/entities/service_plan.dart b/lib/entities/service_plan.dart new file mode 100644 index 000000000..0c1a0cc73 --- /dev/null +++ b/lib/entities/service_plan.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class ServicePlan { + const ServicePlan({ + @required this.id, + @required this.duration, + @required this.price, + @required this.quantity, + }); + + final String id; + final int duration; + final int price; + final int quantity; + + @override + bool operator ==(Object other) => other is ServicePlan && other.id == id; + + @override + int get hashCode => id.hashCode; +} diff --git a/lib/router.dart b/lib/router.dart index 14c5fd526..4172d76ea 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -67,6 +67,7 @@ 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'; RouteSettings currentRouteSettings; @@ -427,6 +428,11 @@ Route createRoute(RouteSettings settings) { builder: (_) => CakePhoneProductsPage(), ); + case Routes.phoneNumberProduct: + return MaterialPageRoute( + builder: (_) => PhoneNumberProductPage(), + ); + default: return MaterialPageRoute( builder: (_) => Scaffold( diff --git a/lib/routes.dart b/lib/routes.dart index 08d78483d..a0324080f 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -64,4 +64,5 @@ class Routes { static const cakePhoneAuth = '/cake_phone_auth'; static const cakePhoneVerification = '/cake_phone_verification'; static const cakePhoneProducts = '/cake_phone_products'; + static const phoneNumberProduct = '/phone_number_product'; } \ No newline at end of file diff --git a/lib/src/screens/cake_phone/cake_phone_auth_page.dart b/lib/src/screens/cake_phone/cake_phone_auth_page.dart index 9b36ee72a..099312679 100644 --- a/lib/src/screens/cake_phone/cake_phone_auth_page.dart +++ b/lib/src/screens/cake_phone/cake_phone_auth_page.dart @@ -23,7 +23,7 @@ class CakePhoneAuthPage extends BasePage { fontSize: 22, fontWeight: FontWeight.w600, fontFamily: 'Lato', - color: titleColor ?? Theme.of(context).primaryTextTheme.title.color), + color: Theme.of(context).primaryTextTheme.title.color), ); } } diff --git a/lib/src/screens/cake_phone/cake_phone_products/phone_number_product_page.dart b/lib/src/screens/cake_phone/cake_phone_products/phone_number_product_page.dart new file mode 100644 index 000000000..a8be63ef5 --- /dev/null +++ b/lib/src/screens/cake_phone/cake_phone_products/phone_number_product_page.dart @@ -0,0 +1,201 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/entities/service_plan.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'; + +class PhoneNumberProductPage extends BasePage { + PhoneNumberProductPage(); + + @override + Widget body(BuildContext context) => PhoneNumberProductBody(); + + @override + Widget middle(BuildContext context) { + return Text( + S.of(context).phone_number, + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w600, + fontFamily: 'Lato', + color: Theme.of(context).primaryTextTheme.title.color), + ); + } +} + +class PhoneNumberProductBody extends StatefulWidget { + PhoneNumberProductBody(); + + @override + PhoneNumberProductBodyState createState() => PhoneNumberProductBodyState(); +} + +class PhoneNumberProductBodyState extends State { + final List dummyPlans = [ + ServicePlan(id: "1", duration: 1, price: 20, quantity: 30), + ServicePlan(id: "2", duration: 3, price: 10, quantity: 60), + ServicePlan(id: "3", duration: 6, price: 9, quantity: 120), + ServicePlan(id: "4", duration: 12, price: 5, quantity: 200), + ]; + + final int rateInCents = 20; + + ServicePlan selectedPlan; + + @override + void initState() { + super.initState(); + + selectedPlan = dummyPlans.first; + } + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(top: 16), + child: ScrollableWithBottomSection( + contentPadding: EdgeInsets.symmetric(horizontal: 24, vertical: 20), + content: Column( + children: [ + Text( + S.of(context).initial_service_term, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + color: Theme.of(context).primaryTextTheme.title.color, + fontFamily: 'Lato', + ), + ), + Padding( + padding: const EdgeInsets.only(top: 8, bottom: 24), + child: Text( + S.of(context).phone_number_promotion_text, + style: TextStyle( + fontSize: 12, + color: Theme.of(context).accentTextTheme.subhead.color, + fontFamily: 'Lato', + ), + ), + ), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: dummyPlans.map((e) => planCard(e)).toList(), + ), + ), + const SizedBox(height: 40), + 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), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Theme.of(context).accentTextTheme.title.backgroundColor, + ))), + child: Text( + "${selectedPlan.quantity}, " + + "${S.of(context).then} " + + "\$${(rateInCents / 100).toStringAsFixed(2)} " + + "${S.of(context).per_message}", + style: TextStyle( + color: Theme.of(context).accentTextTheme.caption.backgroundColor, + ), + ), + ), + ], + ), + bottomSectionPadding: EdgeInsets.only(bottom: 24, right: 24, left: 24), + bottomSection: Column( + children: [ + RichText( + text: TextSpan( + children: [ + TextSpan(text: "${S.of(context).due_today} "), + TextSpan( + text: "\$35.00 ", + style: TextStyle(fontWeight: FontWeight.w700), + ), + ], + style: TextStyle( + fontSize: 15, + color: Theme.of(context).accentTextTheme.subhead.color, + fontFamily: 'Lato', + ), + ), + ), + const SizedBox(height: 24), + PrimaryButton( + onPressed: () {}, + text: S.of(context).pay_with_cake_phone, + color: Theme.of(context).accentTextTheme.caption.backgroundColor, + textColor: Theme.of(context).primaryTextTheme.title.color, + ), + const SizedBox(height: 8), + PrimaryButton( + onPressed: () {}, + text: S.of(context).pay_with_xmr, + color: Theme.of(context).accentTextTheme.body2.color, + textColor: Colors.white, + ), + ], + ), + ), + ); + } + + Widget planCard(ServicePlan e) { + return GestureDetector( + onTap: () { + if (e != selectedPlan) { + selectedPlan = e; + setState(() {}); + } + }, + child: Container( + margin: EdgeInsets.symmetric(horizontal: 2), + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + gradient: selectedPlan == e + ? LinearGradient( + colors: [ + Theme.of(context).primaryTextTheme.subhead.color, + Theme.of(context).primaryTextTheme.subhead.decorationColor, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ) + : null, + color: selectedPlan == e ? null : Theme.of(context).primaryTextTheme.display3.decorationColor, + ), + child: Column( + children: [ + Text( + "\$${e.price}/${S.of(context).month}", + style: TextStyle( + fontWeight: FontWeight.w500, + color: selectedPlan == e ? Colors.white : Theme.of(context).primaryTextTheme.title.color, + ), + ), + Text( + "${e.duration} ${S.of(context).month}", + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.w500, + color: selectedPlan == e ? Colors.white : Theme.of(context).accentTextTheme.subhead.color, + fontFamily: 'Lato', + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/screens/cake_phone/cake_phone_products_page.dart b/lib/src/screens/cake_phone/cake_phone_products_page.dart index a97f79b06..8851ba640 100644 --- a/lib/src/screens/cake_phone/cake_phone_products_page.dart +++ b/lib/src/screens/cake_phone/cake_phone_products_page.dart @@ -18,7 +18,7 @@ class CakePhoneProductsPage extends BasePage { fontSize: 22, fontWeight: FontWeight.w600, fontFamily: 'Lato', - color: titleColor ?? Theme.of(context).primaryTextTheme.title.color), + color: Theme.of(context).primaryTextTheme.title.color), ); } } @@ -48,47 +48,52 @@ class CakePhoneProductsBodyState extends State { fontFamily: 'Lato', ), ), - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(24)), - gradient: LinearGradient( - colors: [ - Theme.of(context).primaryTextTheme.subhead.color, - Theme.of(context).primaryTextTheme.subhead.decorationColor, - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, + GestureDetector( + onTap: () { + Navigator.pushNamed(context, Routes.phoneNumberProduct); + }, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(24)), + gradient: LinearGradient( + colors: [ + Theme.of(context).primaryTextTheme.subhead.color, + Theme.of(context).primaryTextTheme.subhead.decorationColor, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), ), - ), - padding: const EdgeInsets.all(24), - margin: const EdgeInsets.only(top: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 16), - child: Text( - S.of(context).phone_number, - style: TextStyle( - fontSize: 32, - fontWeight: FontWeight.w700, - color: Colors.white, + padding: const EdgeInsets.all(24), + margin: const EdgeInsets.only(top: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: Text( + S.of(context).phone_number, + style: TextStyle( + fontSize: 32, + fontWeight: FontWeight.w700, + color: Colors.white, + ), ), ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 60), - child: Text( - S.of(context).phone_number_product_description, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Colors.white, + Padding( + padding: const EdgeInsets.only(bottom: 60), + child: Text( + S.of(context).phone_number_product_description, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Colors.white, + ), ), ), - ), - ], + ], + ), ), ), ], diff --git a/lib/src/screens/cake_phone/cake_phone_verification_page.dart b/lib/src/screens/cake_phone/cake_phone_verification_page.dart index 7f2907ac2..c20ee2589 100644 --- a/lib/src/screens/cake_phone/cake_phone_verification_page.dart +++ b/lib/src/screens/cake_phone/cake_phone_verification_page.dart @@ -23,7 +23,7 @@ class CakePhoneVerificationPage extends BasePage { fontSize: 22, fontWeight: FontWeight.w600, fontFamily: 'Lato', - color: titleColor ?? Theme.of(context).primaryTextTheme.title.color), + color: Theme.of(context).primaryTextTheme.title.color), ); } } diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 4910f0470..daa5cd7c2 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -555,5 +555,14 @@ "get_phone_number": "Get Phone Number", "choose_phone_products": "Choose from the following available options:", "phone_number": "Phone Number", - "phone_number_product_description": "SMS messages are forwarded to your email address. Includes 20 free SMS messages for each month of service with the option to pay for more." + "phone_number_product_description": "SMS messages are forwarded to your email address. Includes 20 free SMS messages for each month of service with the option to pay for more.", + "initial_service_term": "Initial Service Term", + "phone_number_promotion_text": "You can add more time later. Save big on longer terms!", + "pay_with_cake_phone": "Pay with CakePhone Balance", + "pay_with_xmr": "Pay with XMR", + "due_today": "Due today:", + "free_sms_email_forward": "Free SMS Email Forwards", + "month": "month", + "then": "then", + "per_message": "per message" }