login_view.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. import 'package:flutter/gestures.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:get/get.dart';
  4. import 'package:rive/rive.dart';
  5. import 'package:trackoffical_app/generated/assets.dart';
  6. import 'package:trackoffical_app/screen.dart';
  7. // import 'package:trackoffical_app/view/login/sign_up2_view.dart';
  8. import 'package:trackoffical_app/view/web_view.dart';
  9. import '../../styles/color_schemes.g.dart';
  10. import '../../utils.dart';
  11. import 'login_controller.dart';
  12. import 'common.dart';
  13. import 'package:vector_math/vector_math.dart' as vec;
  14. import 'package:common_pub/prelude.dart';
  15. final _duration = 2.seconds;
  16. class AnimationPage extends StatefulWidget {
  17. const AnimationPage({super.key});
  18. @override
  19. State<StatefulWidget> createState() {
  20. return _AnimationPageState();
  21. }
  22. }
  23. class _AnimationPageState extends State<AnimationPage>
  24. with SingleTickerProviderStateMixin {
  25. late Animation<double> animation;
  26. late AnimationController animationController;
  27. @override
  28. void initState() {
  29. super.initState();
  30. animationController = AnimationController(duration: _duration, vsync: this);
  31. animation = Tween<double>(begin: 0, end: 1).animate(animationController);
  32. animationController.forward();
  33. }
  34. @override
  35. void dispose() {
  36. animationController.dispose();
  37. super.dispose();
  38. }
  39. @override
  40. Widget build(BuildContext context) {
  41. SizeFit.screenInit(context);
  42. return wPageLeft();
  43. }
  44. Widget wPageLeft() {
  45. return Container(
  46. decoration: const BoxDecoration(
  47. image: DecorationImage(
  48. image: AssetImage(Assets.commonPageBg), fit: BoxFit.cover)),
  49. child: Column(
  50. children: [
  51. Expanded(
  52. flex: 1,
  53. child: Container(
  54. // color: const Color(0xffffcb00),
  55. padding: const EdgeInsets.only(bottom: 32),
  56. alignment: Alignment.bottomCenter,
  57. child: Stack(
  58. alignment: Alignment.center,
  59. children: [
  60. Image.asset(
  61. Assets.imagesImCompassNoMap,
  62. height: 15.0.wp,
  63. fit: BoxFit.fitHeight,
  64. ),
  65. SizedBox(
  66. height: 10.0.wp,
  67. child: const RiveAnimation.asset(
  68. 'assets/images/am_app_start_arrow.riv',
  69. fit: BoxFit.fitHeight,
  70. ))
  71. ],
  72. ),
  73. )),
  74. Expanded(
  75. flex: 1,
  76. child: Column(
  77. // mainAxisAlignment: MainAxisAlignment.start,
  78. children: [
  79. AnimatedText(animation: animation),
  80. Expanded(
  81. child: Stack(
  82. alignment: Alignment.center,
  83. children: [
  84. Center(
  85. child: AnimatedColors(
  86. animation: animation,
  87. ),
  88. ),
  89. // Positioned(
  90. // bottom: 2.6.wp,
  91. // child:
  92. // Image.asset(Assets.imagesIcLogo, height: 5.0.wp))
  93. ],
  94. ))
  95. ],
  96. )),
  97. ],
  98. ),
  99. );
  100. }
  101. }
  102. class AnimatedText extends AnimatedWidget {
  103. const AnimatedText({super.key, required Animation<double> animation})
  104. : super(listenable: animation);
  105. @override
  106. Widget build(BuildContext context) {
  107. final animation = listenable as Animation<double>;
  108. var style = (context.textTheme.titleLarge ?? const TextStyle())
  109. .copyWith(height: 0.14.wp, color: Colors.white, fontSize: 2.2.wp);
  110. return Opacity(
  111. opacity: animation.value,
  112. child: Column(
  113. mainAxisSize: MainAxisSize.min,
  114. crossAxisAlignment: CrossAxisAlignment.center,
  115. children: [
  116. SizedBox(
  117. height: 0.9.wp,
  118. ),
  119. Text('确定方向 ', style: style),
  120. Text(' 发现你自己的路!', style: style),
  121. Text('Orienting, Discover Your Own Way!',
  122. style: style.copyWith(
  123. color: const Color(0xffade0ff),
  124. fontWeight: FontWeight.bold,
  125. fontSize: 1.4.wp)),
  126. ],
  127. ));
  128. }
  129. }
  130. class AnimatedColors extends AnimatedWidget {
  131. const AnimatedColors({super.key, required Animation<double> animation})
  132. : super(listenable: animation);
  133. @override
  134. Widget build(BuildContext context) {
  135. final animation = listenable as Animation<double>;
  136. var style = (context.textTheme.titleLarge ?? const TextStyle())
  137. .copyWith(color: Colors.white, fontSize: 8.33.wp);
  138. var children = <Widget>[];
  139. for (var i = 0; i < hrPColors.length; i++) {
  140. final color = hrPColors[i];
  141. var opacity = 0.0;
  142. if (i < animation.value * hrPColors.length) {
  143. opacity = 1;
  144. }
  145. children.add(Opacity(
  146. opacity: opacity,
  147. child: Container(
  148. width: 1.02.wp,
  149. decoration: BoxDecoration(
  150. color: color,
  151. borderRadius: BorderRadius.circular(2.0.wp),
  152. border: Border.all(color: Colors.white.withAlpha(100))),
  153. )));
  154. }
  155. return Column(
  156. mainAxisSize: MainAxisSize.min,
  157. crossAxisAlignment: CrossAxisAlignment.center,
  158. children: [
  159. SizedBox(height: 5.36.wp),
  160. SizedBox(
  161. width: 7.6.wp,
  162. height: 2.5.wp,
  163. child: Row(
  164. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  165. children: children,
  166. )),
  167. // Text('支持心率带检测身体数据',
  168. // style: style.copyWith(
  169. // color: const Color(0xffffcb00), fontSize: 3.3.wp)),
  170. ],
  171. );
  172. }
  173. }
  174. class LoginView extends GetView<LoginController> {
  175. const LoginView({super.key});
  176. static Bindings loginBind() {
  177. return BindingsBuilder(() {
  178. Get.lazyPut<LoginController>(() => LoginController());
  179. // Get.put(LoginController());
  180. });
  181. }
  182. @override
  183. Widget build(BuildContext context) {
  184. return Scaffold(
  185. // appBar: AppTopBar(title: '登录账号', hasBackText: true, height: context.height*0.3),
  186. backgroundColor: Colors.white,
  187. body: Container(
  188. height: double.infinity,
  189. decoration: const BoxDecoration(color: Colors.white),
  190. alignment: Alignment.center,
  191. clipBehavior: Clip.hardEdge,
  192. child: Row(
  193. children: [
  194. const Expanded(
  195. flex: 2,
  196. child: AnimationPage(),
  197. ),
  198. Expanded(
  199. flex: 3,
  200. child: wPageRight(context),
  201. )
  202. ],
  203. ),
  204. ),
  205. );
  206. }
  207. Widget wPageRight(BuildContext context) {
  208. return Container(
  209. margin: EdgeInsets.fromLTRB(6.wp, 5.6.wp, 6.wp, 2.wp),
  210. padding: const EdgeInsets.all(10.0),
  211. decoration: const BoxDecoration(
  212. // color: Color(0xffffcb00),
  213. image: DecorationImage(
  214. image: AssetImage(Assets.loginRightBg),
  215. alignment: Alignment.topCenter,
  216. fit: BoxFit.fitWidth),
  217. ),
  218. child: Column(
  219. mainAxisAlignment: MainAxisAlignment.center,
  220. crossAxisAlignment: CrossAxisAlignment.center,
  221. children: [
  222. Text(
  223. '定向运动活动现场监控系统',
  224. textAlign: TextAlign.center,
  225. style: TextStyle(fontSize: 22.0.rpx, fontWeight: FontWeight.bold),
  226. ),
  227. SizedBox(
  228. height: context.height * 0.45,
  229. child: wInput()
  230. ),
  231. SizedBox(
  232. height: 3.wp,
  233. child: Image.asset(Assets.loginXfldx)
  234. ),
  235. // Expanded(flex: 10,child: wExtra()),
  236. ],
  237. ));
  238. }
  239. InputDecoration wInputDecoration(hintText) {
  240. return InputDecoration(
  241. // isCollapsed: true,
  242. contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
  243. hintText: hintText,
  244. // hintStyle: TextStyle(fontSize: 12.0.rpx),
  245. focusedBorder: OutlineInputBorder(
  246. borderRadius: BorderRadius.circular(4.0),
  247. borderSide: const BorderSide(
  248. color: Colors.blue,
  249. width: 1.0,
  250. ),
  251. ),
  252. enabledBorder: OutlineInputBorder(
  253. borderRadius: BorderRadius.circular(4.0),
  254. borderSide: const BorderSide(
  255. color: Color(0xffaaaaaa),
  256. width: 1.0,
  257. ),
  258. ),
  259. );
  260. }
  261. Widget wInput() {
  262. return Container(
  263. margin: EdgeInsets.fromLTRB(6.wp, 3.wp, 6.wp, 2.wp),
  264. padding: EdgeInsets.fromLTRB(6.wp, 3.6.wp, 6.wp, 2.wp),
  265. decoration: BoxDecoration(
  266. color: const Color(0xffffffff),
  267. borderRadius: BorderRadius.circular(1.0.wp),
  268. boxShadow: const [BoxShadow(color: Color(0x38000000), offset: Offset(0.0, 3.0), blurRadius: 6)],
  269. ),
  270. child: Column(
  271. children: [
  272. // const Spacer(flex: 20),
  273. TextFormField(
  274. decoration: wInputDecoration('请输入手机号'),
  275. validator: (String? value) {
  276. if (value == null || !value.isPhoneNumber) {
  277. return '请输入正确的手机号';
  278. }
  279. return null;
  280. },
  281. onChanged: (value) {
  282. controller.phone.value = value;
  283. },
  284. keyboardType: TextInputType.phone),
  285. const Spacer(flex: 50),
  286. Stack(
  287. alignment: Alignment.centerRight,
  288. children: [
  289. TextFormField(
  290. decoration: wInputDecoration('请输入验证码'),
  291. onChanged: (value) {
  292. controller.password.value = value;
  293. },
  294. keyboardType: TextInputType.number,
  295. ),
  296. Padding(
  297. padding: const EdgeInsets.only(right: 4),
  298. child: SizedBox(
  299. width: 84,
  300. height: 37,
  301. child: Obx(() => GetCodeButton(
  302. codeRetryLeft: controller.codeRetryLeft.value,
  303. onPressed: () => controller.onGetCode(),
  304. )),
  305. ),
  306. ),
  307. ],
  308. ),
  309. // _TextContract(),
  310. const Spacer(flex: 80),
  311. button(
  312. '登 录',
  313. () => controller.onSignIn(),
  314. ),
  315. const Spacer(flex: 10),
  316. ],
  317. ));
  318. }
  319. Widget wExtra() {
  320. return Padding(
  321. padding: EdgeInsets.only(left: 41.67.rpx, right: 41.67.rpx),
  322. child: Column(
  323. crossAxisAlignment: CrossAxisAlignment.center,
  324. children: [
  325. Row(
  326. crossAxisAlignment: CrossAxisAlignment.center,
  327. children: [
  328. divider(),
  329. Padding(
  330. padding: EdgeInsets.only(left: 22.9.rpx, right: 22.9.rpx),
  331. child: Text(
  332. '其他登录方式',
  333. style: TextStyle(fontSize: 25.0.rpx),
  334. )),
  335. divider(),
  336. ],
  337. ),
  338. SizedBox(height: 76.5.rpx),
  339. Row(),
  340. const Spacer(),
  341. Text.rich(TextSpan(
  342. text: '还不是我们的会员?',
  343. style: TextStyle(fontSize: 33.33.rpx, color: Colors.black),
  344. children: [
  345. TextSpan(
  346. text: '点击注册',
  347. style: const TextStyle(color: Color(0xffffb40b)),
  348. recognizer: TapGestureRecognizer()
  349. ..onTap = () {
  350. // SignUpView.show();
  351. },
  352. ),
  353. ])),
  354. SizedBox(height: 76.5.rpx),
  355. ],
  356. ));
  357. }
  358. Widget divider() {
  359. return Expanded(
  360. child: Container(
  361. height: 2.0.rpx,
  362. color: Colors.black,
  363. ));
  364. }
  365. }
  366. class _TextContract extends GetView<LoginController> {
  367. @override
  368. Widget build(BuildContext context) {
  369. const color = Color(0xffffb40b);
  370. return Row(
  371. children: [
  372. Obx(() => Checkbox(
  373. value: controller.isAgreeContract.value,
  374. onChanged: (v) => controller.isAgreeContract.value = (v ?? false),
  375. )),
  376. Expanded(
  377. child: Text.rich(TextSpan(
  378. text: '我已阅读并同意',
  379. style: const TextStyle(fontSize: 11, color: Color(0xFF999999)),
  380. children: [
  381. TextSpan(
  382. text: '《用户协议》',
  383. style: const TextStyle(color: color),
  384. recognizer: TapGestureRecognizer()
  385. ..onTap = () {
  386. Get.to(() => WebView(
  387. url: 'https://www.beswell.com/ot_agreement.html'));
  388. },
  389. ),
  390. const TextSpan(text: '和'),
  391. TextSpan(
  392. text: '《隐私政策》',
  393. style: const TextStyle(color: color),
  394. recognizer: TapGestureRecognizer()
  395. ..onTap = () {
  396. Get.to(() =>
  397. WebView(url: 'https://beswell.com/privacy-ot.html'));
  398. },
  399. ),
  400. ])))
  401. ],
  402. );
  403. }
  404. }
  405. void main() {
  406. Get.put(LoginController());
  407. runPreview(LoginView());
  408. }