login_view.dart 13 KB

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