import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:trackoffical_app/generated/assets.dart'; import 'package:trackoffical_app/logger.dart'; import 'package:trackoffical_app/screen.dart'; import 'package:trackoffical_app/service/all_init.dart'; import 'package:trackoffical_app/service/app.dart'; import 'package:trackoffical_app/utils.dart'; import 'package:rive/rive.dart'; import 'package:vector_math/vector_math.dart' as vec; final _duration = 2.seconds; class AppInitPageController extends GetxController { @override void onReady() async { final initFuture = initAllServicesAndThenCallback(); final minWaitFuture = _duration.delay(); final l = await Future.wait([initFuture, minWaitFuture]); final firstPage = l[0]; firstPage(); } void setScreenInfo(BuildContext context) { var msg = '获取屏幕信息\n'; msg += 'width: ${context.width}, height: ${context.height}\n'; var pixelRatio = context.devicePixelRatio; msg += 'pixelRatio: $pixelRatio\n'; App.to.devicePixelRatio = pixelRatio; App.to.screenSize = Size(context.width, context.height); SizeFit.screenInit(context); debug(msg); } } class AppInitPage extends GetView { const AppInitPage({super.key}); static Bindings bindings() { return BindingsBuilder(() { Get.put(AppInitPageController()); }); } @override Widget build(BuildContext context) { controller.setScreenInfo(context); return const AnimationPage(); } } class AnimationPage extends StatefulWidget { const AnimationPage({super.key}); @override State createState() { return _AnimationPageState(); } } class _AnimationPageState extends State with SingleTickerProviderStateMixin { late Animation animation; late AnimationController controller; @override void initState() { super.initState(); controller = AnimationController(duration: _duration, vsync: this); animation = Tween(begin: 0, end: 1).animate(controller); controller.forward(); } @override void dispose() { controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { SizeFit.screenInit(context); return Scaffold( body: Container( decoration: const BoxDecoration( image: DecorationImage( image: AssetImage(Assets.imagesBkAppInit), fit: BoxFit.cover)), child: Column( children: [ Expanded( flex: 1, child: Container( // color: const Color(0xffffcb00), alignment: Alignment.bottomCenter, child: Stack( alignment: Alignment.center, children: [ Image.asset( Assets.imagesImCompassNoMap, height: 25.3.wp, fit: BoxFit.fitHeight, ), // AnimatedCompass(animation: animation), SizedBox( height: 18.0.wp, child: const RiveAnimation.asset( 'assets/images/am_app_start_arrow.riv', fit: BoxFit.fitHeight, )) ], ), )), Expanded( flex: 1, child: Column( children: [ AnimatedText(animation: animation), Expanded( child: Stack( alignment: Alignment.center, children: [ Center( child: AnimatedColors( animation: animation, ), ), Positioned( bottom: 2.6.wp, child: Image.asset(Assets.imagesIcLogo, height: 5.0.wp)) ], )) ], )), ], ), ), ); } } class AnimatedCompass extends AnimatedWidget { const AnimatedCompass({super.key, required Animation animation}) : super(listenable: animation); @override Widget build(BuildContext context) { final animation = listenable as Animation; return Transform.rotate( angle: vec.radians(animation.value * 360), alignment: Alignment.center, child: Image.asset( Assets.imagesImCompassArrow, height: 44.0.wp, fit: BoxFit.fitHeight, ), ); } } class AnimatedText extends AnimatedWidget { const AnimatedText({super.key, required Animation animation}) : super(listenable: animation); @override Widget build(BuildContext context) { final animation = listenable as Animation; var style = (context.textTheme.titleLarge ?? const TextStyle()) .copyWith(color: Colors.white, fontSize: 3.9.wp); return Opacity( opacity: animation.value, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox( height: 3.9.wp, ), Text('确定方向 ', style: style), Text(' 发现你自己的路!', style: style.copyWith(fontSize: 3.9.wp)), Text('Orienting, Discover Your Own Way!', style: style.copyWith( color: const Color(0xffade0ff), fontSize: 2.5.wp)), ], )); } } class AnimatedColors extends AnimatedWidget { const AnimatedColors({super.key, required Animation animation}) : super(listenable: animation); @override Widget build(BuildContext context) { final animation = listenable as Animation; var style = (context.textTheme.titleLarge ?? const TextStyle()) .copyWith(color: Colors.white, fontSize: 8.33.wp); var children = []; for (var i = 0; i < hrPColors.length; i++) { final color = hrPColors[i]; var opacity = 0.0; if (i < animation.value * hrPColors.length) { opacity = 1; } children.add(Opacity( opacity: opacity, child: Container( width: 1.2.wp, decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(2.0.wp), border: Border.all(color: Colors.white.withAlpha(100))), ))); } return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox( width: 10.wp, height: 3.9.wp, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: children, )), SizedBox(height: 6.66.wp), // Text('支持心率带检测身体数据', // style: style.copyWith( // color: const Color(0xffffcb00), fontSize: 3.3.wp)), ], ); } } void main() { runPreview(const AnimationPage()); }