login_view.dart 14 KB

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