login_view.dart 14 KB

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