landing_page_widget.dart 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import 'dart:io';
  2. import 'package:dots_indicator/dots_indicator.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:photos/core/configuration.dart';
  5. import 'package:photos/ente_theme_data.dart';
  6. import 'package:photos/ui/account/email_entry_page.dart';
  7. import 'package:photos/ui/account/login_page.dart';
  8. import 'package:photos/ui/account/password_entry_page.dart';
  9. import 'package:photos/ui/account/password_reentry_page.dart';
  10. import 'package:photos/ui/common/gradient_button.dart';
  11. import 'package:photos/ui/payment/subscription.dart';
  12. class LandingPageWidget extends StatefulWidget {
  13. const LandingPageWidget({Key key}) : super(key: key);
  14. @override
  15. State<LandingPageWidget> createState() => _LandingPageWidgetState();
  16. }
  17. class _LandingPageWidgetState extends State<LandingPageWidget> {
  18. double _featureIndex = 0;
  19. @override
  20. Widget build(BuildContext context) {
  21. return Scaffold(body: _getBody(), resizeToAvoidBottomInset: false);
  22. }
  23. Widget _getBody() {
  24. return Center(
  25. child: SingleChildScrollView(
  26. child: Column(
  27. children: [
  28. const Padding(padding: EdgeInsets.all(12)),
  29. const Text(
  30. "ente",
  31. style: TextStyle(
  32. fontWeight: FontWeight.bold,
  33. fontFamily: 'Montserrat',
  34. fontSize: 42,
  35. ),
  36. ),
  37. const Padding(
  38. padding: EdgeInsets.all(28),
  39. ),
  40. _getFeatureSlider(),
  41. const Padding(
  42. padding: EdgeInsets.all(12),
  43. ),
  44. DotsIndicator(
  45. dotsCount: 3,
  46. position: _featureIndex,
  47. decorator: DotsDecorator(
  48. activeColor:
  49. Theme.of(context).colorScheme.dotsIndicatorActiveColor,
  50. color: Theme.of(context).colorScheme.dotsIndicatorInactiveColor,
  51. activeShape: RoundedRectangleBorder(
  52. borderRadius: BorderRadius.circular(3),
  53. ),
  54. shape: RoundedRectangleBorder(
  55. borderRadius: BorderRadius.circular(3),
  56. ),
  57. size: const Size(100, 5),
  58. activeSize: const Size(100, 5),
  59. spacing: const EdgeInsets.all(3),
  60. ),
  61. ),
  62. const Padding(
  63. padding: EdgeInsets.all(28),
  64. ),
  65. _getSignUpButton(context),
  66. Container(
  67. width: double.infinity,
  68. padding: const EdgeInsets.fromLTRB(20, 12, 20, 28),
  69. child: Hero(
  70. tag: "log_in",
  71. child: ElevatedButton(
  72. style:
  73. Theme.of(context).colorScheme.optionalActionButtonStyle,
  74. onPressed: _navigateToSignInPage,
  75. child: const Text(
  76. "Existing user",
  77. style: TextStyle(
  78. color: Colors.black, // same for both themes
  79. ),
  80. ),
  81. ),
  82. ),
  83. ),
  84. const Padding(
  85. padding: EdgeInsets.all(20),
  86. ),
  87. ],
  88. ),
  89. ),
  90. );
  91. }
  92. Widget _getSignUpButton(BuildContext context) {
  93. return Container(
  94. width: double.infinity,
  95. padding: const EdgeInsets.symmetric(horizontal: 20),
  96. child: GradientButton(
  97. onTap: _navigateToSignUpPage,
  98. text: "New to ente",
  99. ),
  100. );
  101. }
  102. Widget _getFeatureSlider() {
  103. return ConstrainedBox(
  104. constraints: const BoxConstraints(maxHeight: 320),
  105. child: PageView(
  106. children: [
  107. const FeatureItemWidget(
  108. "assets/onboarding_lock.png",
  109. "Private backups",
  110. "for your memories",
  111. "End-to-end encrypted by default",
  112. ),
  113. const FeatureItemWidget(
  114. "assets/onboarding_safe.png",
  115. "Safely stored",
  116. "at a fallout shelter",
  117. "Designed to outlive",
  118. ),
  119. FeatureItemWidget(
  120. "assets/onboarding_lock.png",
  121. "Available",
  122. "everywhere",
  123. Platform.isAndroid
  124. ? "Android, iOS, Web, Desktop"
  125. : "iOS, Android, Web, Desktop",
  126. ),
  127. ],
  128. onPageChanged: (index) {
  129. setState(() {
  130. _featureIndex = double.parse(index.toString());
  131. });
  132. },
  133. ),
  134. );
  135. }
  136. void _navigateToSignUpPage() {
  137. Widget page;
  138. if (Configuration.instance.getEncryptedToken() == null) {
  139. page = const EmailEntryPage();
  140. } else {
  141. // No key
  142. if (Configuration.instance.getKeyAttributes() == null) {
  143. // Never had a key
  144. page = const PasswordEntryPage();
  145. } else if (Configuration.instance.getKey() == null) {
  146. // Yet to decrypt the key
  147. page = const PasswordReentryPage();
  148. } else {
  149. // All is well, user just has not subscribed
  150. page = getSubscriptionPage(isOnBoarding: true);
  151. }
  152. }
  153. Navigator.of(context).push(
  154. MaterialPageRoute(
  155. builder: (BuildContext context) {
  156. return page;
  157. },
  158. ),
  159. );
  160. }
  161. void _navigateToSignInPage() {
  162. Widget page;
  163. if (Configuration.instance.getEncryptedToken() == null) {
  164. page = const LoginPage();
  165. } else {
  166. // No key
  167. if (Configuration.instance.getKeyAttributes() == null) {
  168. // Never had a key
  169. page = const PasswordEntryPage();
  170. } else if (Configuration.instance.getKey() == null) {
  171. // Yet to decrypt the key
  172. page = const PasswordReentryPage();
  173. } else {
  174. // All is well, user just has not subscribed
  175. page = getSubscriptionPage(isOnBoarding: true);
  176. }
  177. }
  178. Navigator.of(context).push(
  179. MaterialPageRoute(
  180. builder: (BuildContext context) {
  181. return page;
  182. },
  183. ),
  184. );
  185. }
  186. }
  187. class FeatureItemWidget extends StatelessWidget {
  188. final String assetPath,
  189. featureTitleFirstLine,
  190. featureTitleSecondLine,
  191. subText;
  192. const FeatureItemWidget(
  193. this.assetPath,
  194. this.featureTitleFirstLine,
  195. this.featureTitleSecondLine,
  196. this.subText, {
  197. Key key,
  198. }) : super(key: key);
  199. @override
  200. Widget build(BuildContext context) {
  201. return Column(
  202. crossAxisAlignment: CrossAxisAlignment.stretch,
  203. children: [
  204. Image.asset(
  205. assetPath,
  206. height: 160,
  207. ),
  208. const Padding(padding: EdgeInsets.all(16)),
  209. Column(
  210. crossAxisAlignment: CrossAxisAlignment.center,
  211. mainAxisAlignment: MainAxisAlignment.start,
  212. children: [
  213. Text(
  214. featureTitleFirstLine,
  215. style: Theme.of(context).textTheme.headline5,
  216. ),
  217. const Padding(padding: EdgeInsets.all(2)),
  218. Text(
  219. featureTitleSecondLine,
  220. style: Theme.of(context).textTheme.headline5,
  221. ),
  222. const Padding(padding: EdgeInsets.all(12)),
  223. Text(
  224. subText,
  225. textAlign: TextAlign.center,
  226. style: TextStyle(
  227. color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5),
  228. fontSize: 16,
  229. ),
  230. ),
  231. ],
  232. ),
  233. ],
  234. );
  235. }
  236. }