email_entry_page.dart 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/gestures.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter/widgets.dart';
  5. import 'package:photos/core/configuration.dart';
  6. import 'package:photos/models/billing_plan.dart';
  7. import 'package:photos/services/billing_service.dart';
  8. import 'package:photos/services/user_service.dart';
  9. import 'package:photos/ui/common_elements.dart';
  10. import 'package:photos/ui/loading_widget.dart';
  11. import 'package:photos/ui/web_page.dart';
  12. import 'package:photos/utils/dialog_util.dart';
  13. import 'package:photos/utils/email_util.dart';
  14. class EmailEntryPage extends StatefulWidget {
  15. EmailEntryPage({Key key}) : super(key: key);
  16. @override
  17. _EmailEntryPageState createState() => _EmailEntryPageState();
  18. }
  19. class _EmailEntryPageState extends State<EmailEntryPage> {
  20. final _config = Configuration.instance;
  21. String _email;
  22. String _name;
  23. @override
  24. void initState() {
  25. _email = _config.getEmail();
  26. _name = _config.getName();
  27. super.initState();
  28. }
  29. @override
  30. Widget build(BuildContext context) {
  31. final appBar = AppBar(
  32. title: Text(
  33. "sign up",
  34. style: TextStyle(
  35. fontSize: 18,
  36. ),
  37. ),
  38. );
  39. return Scaffold(
  40. appBar: appBar,
  41. body: _getBody(appBar.preferredSize.height),
  42. );
  43. }
  44. Widget _getBody(final appBarSize) {
  45. final pageSize = MediaQuery.of(context).size.height;
  46. final notifySize = MediaQuery.of(context).padding.top;
  47. return SingleChildScrollView(
  48. child: Container(
  49. height: pageSize - (appBarSize + notifySize),
  50. padding: EdgeInsets.all(8),
  51. child: Column(
  52. crossAxisAlignment: CrossAxisAlignment.stretch,
  53. mainAxisAlignment: MainAxisAlignment.center,
  54. mainAxisSize: MainAxisSize.max,
  55. children: [
  56. Padding(
  57. padding: EdgeInsets.all(60),
  58. ),
  59. Padding(
  60. padding: const EdgeInsets.fromLTRB(32, 0, 32, 0),
  61. child: TextFormField(
  62. decoration: InputDecoration(
  63. hintText: 'name',
  64. hintStyle: TextStyle(
  65. color: Colors.white30,
  66. ),
  67. contentPadding: EdgeInsets.all(12),
  68. ),
  69. onChanged: (value) {
  70. setState(() {
  71. _name = value;
  72. });
  73. },
  74. autocorrect: false,
  75. keyboardType: TextInputType.text,
  76. textCapitalization: TextCapitalization.words,
  77. initialValue: _name,
  78. ),
  79. ),
  80. Padding(padding: EdgeInsets.all(8)),
  81. Padding(
  82. padding: const EdgeInsets.fromLTRB(32, 0, 32, 0),
  83. child: TextFormField(
  84. decoration: InputDecoration(
  85. hintText: 'email',
  86. hintStyle: TextStyle(
  87. color: Colors.white30,
  88. ),
  89. contentPadding: EdgeInsets.all(12),
  90. ),
  91. onChanged: (value) {
  92. setState(() {
  93. _email = value;
  94. });
  95. },
  96. autocorrect: false,
  97. keyboardType: TextInputType.emailAddress,
  98. initialValue: _email,
  99. ),
  100. ),
  101. Padding(padding: EdgeInsets.all(8)),
  102. Padding(
  103. padding: const EdgeInsets.all(12),
  104. child: RichText(
  105. text: TextSpan(
  106. children: [
  107. TextSpan(
  108. text: "by clicking sign up, I agree to the ",
  109. ),
  110. TextSpan(
  111. text: "terms of service",
  112. style: TextStyle(
  113. color: Colors.blue,
  114. fontFamily: 'Ubuntu',
  115. ),
  116. recognizer: TapGestureRecognizer()
  117. ..onTap = () {
  118. Navigator.of(context).push(
  119. MaterialPageRoute(
  120. builder: (BuildContext context) {
  121. return WebPage(
  122. "terms", "https://ente.io/terms");
  123. },
  124. ),
  125. );
  126. },
  127. ),
  128. TextSpan(text: " and "),
  129. TextSpan(
  130. text: "privacy policy",
  131. style: TextStyle(
  132. color: Colors.blue,
  133. fontFamily: 'Ubuntu',
  134. ),
  135. recognizer: TapGestureRecognizer()
  136. ..onTap = () {
  137. Navigator.of(context).push(
  138. MaterialPageRoute(
  139. builder: (BuildContext context) {
  140. return WebPage(
  141. "privacy", "https://ente.io/privacy");
  142. },
  143. ),
  144. );
  145. },
  146. ),
  147. ],
  148. style: TextStyle(
  149. height: 1.25,
  150. fontSize: 12,
  151. fontFamily: 'Ubuntu',
  152. color: Colors.white70,
  153. ),
  154. ),
  155. textAlign: TextAlign.center,
  156. ),
  157. ),
  158. Padding(padding: EdgeInsets.all(4)),
  159. Container(
  160. width: double.infinity,
  161. height: 64,
  162. padding: const EdgeInsets.fromLTRB(80, 0, 80, 0),
  163. child: button(
  164. "sign up",
  165. onPressed: _email != null && _name != null
  166. ? () {
  167. if (!isValidEmail(_email)) {
  168. showErrorDialog(context, "invalid email address",
  169. "please enter a valid email address.");
  170. return;
  171. }
  172. _config.setEmail(_email);
  173. _config.setName(_name);
  174. UserService.instance.getOtt(context, _email);
  175. }
  176. : null,
  177. fontSize: 18,
  178. ),
  179. ),
  180. Expanded(child: Container()),
  181. Align(
  182. alignment: Alignment.center,
  183. child: GestureDetector(
  184. onTap: () {
  185. showModalBottomSheet<void>(
  186. context: context,
  187. builder: (BuildContext context) {
  188. return PricingWidget();
  189. });
  190. },
  191. child: Container(
  192. padding: EdgeInsets.all(32),
  193. child: Row(
  194. mainAxisAlignment: MainAxisAlignment.center,
  195. crossAxisAlignment: CrossAxisAlignment.center,
  196. children: [
  197. Text(
  198. "pricing",
  199. ),
  200. Icon(Icons.arrow_drop_up),
  201. ],
  202. ),
  203. ),
  204. ),
  205. ),
  206. ],
  207. ),
  208. ),
  209. );
  210. }
  211. }
  212. class PricingWidget extends StatelessWidget {
  213. const PricingWidget({
  214. Key key,
  215. }) : super(key: key);
  216. @override
  217. Widget build(BuildContext context) {
  218. return FutureBuilder<List<BillingPlan>>(
  219. future: BillingService.instance.getBillingPlans(),
  220. builder: (BuildContext context, AsyncSnapshot snapshot) {
  221. if (snapshot.hasData) {
  222. return _buildPlans(context, snapshot.data);
  223. } else if (snapshot.hasError) {
  224. return Text("Oops, something went wrong.");
  225. } else {
  226. return loadWidget;
  227. }
  228. },
  229. );
  230. }
  231. Container _buildPlans(BuildContext context, List<BillingPlan> plans) {
  232. final planWidgets = List<BillingPlanWidget>();
  233. for (final plan in plans) {
  234. planWidgets.add(BillingPlanWidget(plan));
  235. }
  236. return Container(
  237. height: 280,
  238. child: Column(
  239. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  240. children: <Widget>[
  241. Text(
  242. "pricing",
  243. style: TextStyle(
  244. fontWeight: FontWeight.bold,
  245. fontSize: 18,
  246. ),
  247. ),
  248. Row(
  249. mainAxisAlignment: MainAxisAlignment.center,
  250. children: planWidgets,
  251. ),
  252. const Text("we offer a 14 day free trial"),
  253. GestureDetector(
  254. child: Row(
  255. mainAxisAlignment: MainAxisAlignment.center,
  256. crossAxisAlignment: CrossAxisAlignment.center,
  257. children: [
  258. Icon(
  259. Icons.close,
  260. size: 12,
  261. color: Colors.white38,
  262. ),
  263. Padding(padding: EdgeInsets.all(1)),
  264. Text(
  265. "close",
  266. style: TextStyle(
  267. color: Colors.white38,
  268. ),
  269. ),
  270. ],
  271. ),
  272. onTap: () => Navigator.pop(context),
  273. )
  274. ],
  275. ),
  276. );
  277. }
  278. }
  279. class BillingPlanWidget extends StatelessWidget {
  280. final BillingPlan plan;
  281. const BillingPlanWidget(
  282. this.plan, {
  283. Key key,
  284. }) : super(key: key);
  285. @override
  286. Widget build(BuildContext context) {
  287. return Card(
  288. shape: RoundedRectangleBorder(
  289. borderRadius: BorderRadius.circular(12.0),
  290. ),
  291. child: Container(
  292. width: 100,
  293. padding: EdgeInsets.fromLTRB(0, 20, 0, 20),
  294. child: Column(
  295. children: [
  296. Text(
  297. (plan.storageInMBs / 1024).round().toString() + " GB",
  298. style: TextStyle(
  299. fontWeight: FontWeight.bold,
  300. fontSize: 16,
  301. ),
  302. ),
  303. Padding(
  304. padding: EdgeInsets.all(4),
  305. ),
  306. Text(
  307. plan.price + " / " + plan.period,
  308. style: TextStyle(
  309. fontSize: 12,
  310. ),
  311. ),
  312. ],
  313. ),
  314. ),
  315. );
  316. }
  317. }