login_view.dart 14 KB

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