login_view.dart 13 KB

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