123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- import 'dart:async';
- import 'dart:io';
- import 'package:animated_text_kit/animated_text_kit.dart';
- import 'package:audio_session/audio_session.dart';
- import 'package:dotted_line/dotted_line.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:get_it/get_it.dart';
- import 'package:just_audio/just_audio.dart';
- import 'package:sport/provider/bluetooth.dart';
- import 'package:sport/services/app_subscription_state.dart';
- import 'package:sport/utils/DateFormat.dart';
- import 'package:sport/widgets/button_cancel.dart';
- import 'package:sport/widgets/dialog/game_alert_dialog.dart';
- import 'package:sport/widgets/image.dart';
- import 'package:video_player/video_player.dart';
- /**
- * @TODO 一、服务器接口
- * 1、数据详情接口
- * 2、运动提交接口、保存的内容、所需要回显的位置及具体内容需提前规则
- * 3、排行榜(修改)升序
- *
- * 二、算法
- * 1、目前只使用了在三轮车游戏中使用的开合跳,来测试demo
- * 2、具体需要支持哪些动作?识别动作算法需重新编辑
- *
- * 三、运动的配置
- * 1、动作的次数,难度选择等
- * 2、运动过程中是否要添加其它提示语音、引导语音等
- */
- class SportDetail extends StatefulWidget {
- final int type;
- const SportDetail({Key? key, required this.type}) : super(key: key);
- @override
- State<StatefulWidget> createState() => _State();
- }
- class _State extends State<SportDetail> with SubscriptionState {
- late VideoPlayerController _videoPlayerController;
- late AudioPlayer _audioPlayer;
- Timer? _timer;
- final List<String> groupLabel = ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十"];
- List<Mission> missions = List.generate(5, (i) => Mission());
- int _index = 0;
- int _missionIndex = 0;
- int _restIndex = -1;
- Duration _duration = Duration.zero;
- late Bluetooth _bluetooth;
- final GlobalKey dialogKey = GlobalKey();
- bool _startui = false;
- var _buttonIndex = 0;
- @override
- void initState() {
- super.initState();
- _videoPlayerController = VideoPlayerController.network("https://static.ouj.com/hiyd_cms/file/4bb98b9c7cb0441cbea0a96ff73bae93.mp4");
- _videoPlayerController.setLooping(true);
- _videoPlayerController.initialize().then((value) {
- setState(() {
- // print("111111111111111111111111111 ${_videoPlayerController.value.duration}");
- _duration = _videoPlayerController.value.duration;
- setState(() {
- _startui = true;
- });
- // _startMission();
- });
- });
- _videoPlayerController.addListener(() {
- // print("11111111111111111111 ${_videoPlayerController.value.isPlaying} ${_videoPlayerController.value}");
- // if(_videoPlayerController.value.position == _videoPlayerController.value.duration){
- // _videoPlayerController.seekTo(Duration.zero);
- // _videoPlayerController.play();
- // }
- });
- _audioPlayer = AudioPlayer(handleInterruptions: false);
- AudioSession.instance.then((audioSession) async {
- await audioSession.configure(AudioSessionConfiguration.music().copyWith(avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.mixWithOthers | AVAudioSessionCategoryOptions.duckOthers, avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.notifyOthersOnDeactivation, androidAudioFocusGainType: AndroidAudioFocusGainType.gainTransientMayDuck));
- addSubscription(_audioPlayer.playerStateStream.listen((event) {
- if (event.processingState == ProcessingState.completed) {}
- if (Platform.isAndroid) {
- audioSession.setActive(event.processingState == ProcessingState.ready);
- }
- }));
- });
- WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
- SystemChrome.setPreferredOrientations(Platform.isIOS ? [DeviceOrientation.landscapeRight] : [DeviceOrientation.landscapeLeft]);
- SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
- });
- _bluetooth = GetIt.I<Bluetooth>();
- _bluetooth.gameInit(6);
- _bluetooth.setupGameMode4h5(true);
- addSubscription(_bluetooth.sdkMotionStream.listen((event) {
- List<int> result = event;
- for (var i in result) {
- if (i == 14) {
- setState(() {
- var mission = missions[_missionIndex];
- _index = mission.count += 1;
- _playAudio(["number/${_index}"]);
- if (_index == mission.target) {
- if (_missionIndex < missions.length - 1) {
- _startRest();
- } else {
- _finishMission();
- }
- }
- });
- break;
- }
- }
- }));
- }
- @override
- void dispose() {
- SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
- SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.bottom, SystemUiOverlay.top]);
- _timer?.cancel();
- _videoPlayerController.dispose();
- _audioPlayer.dispose();
- _bluetooth.setupGameMode4h5(false);
- super.dispose();
- }
- _startMission() {
- if (_missionIndex >= missions.length) {
- Navigator.pop(context);
- return;
- }
- _index = 0;
- setState(() {
- _restIndex = -1;
- _startui = false;
- });
- _timer?.cancel();
- _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
- setState(() {
- missions[_missionIndex].time += 1;
- // var mission = missions[_missionIndex];
- // _index = mission.count += 1;
- // if (_index == mission.target) {
- // timer.cancel();
- // if (_missionIndex < missions.length - 1) {
- // _startRest();
- // } else {
- // _finishMission();
- // }
- // }
- });
- });
- _videoPlayerController.play();
- }
- _startRest() {
- _timer?.cancel();
- setState(() {
- _restIndex = 0;
- });
- _videoPlayerController.pause();
- _videoPlayerController.seekTo(Duration.zero);
- _timer = Timer.periodic(Duration(seconds: 1), (timer) {
- ++_restIndex;
- if (_restIndex > 10) {
- timer.cancel();
- setState(() {
- _missionIndex++;
- _startMission();
- });
- } else {
- setState(() {});
- }
- });
- }
- _finishMission() {
- showDialog(
- context: context,
- builder: (context) => CustomGameAlertDialog(
- title: '是否退出教程',
- key: dialogKey,
- ok: () => Navigator.of(context).pop(true),
- custom: Center(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Container(
- decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(11.0)),
- padding: const EdgeInsets.all(20.0),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Column(
- children: [
- Text(
- "恭喜你已完成训练",
- style: Theme.of(context).textTheme.subtitle1?.copyWith(color: Theme.of(context).colorScheme.secondary, fontSize: 20.0),
- ),
- Text(
- "完成动作",
- style: Theme.of(context).textTheme.subtitle1,
- ),
- Text(
- "运动消耗",
- style: Theme.of(context).textTheme.subtitle1,
- ),
- Text(
- "运动时长",
- style: Theme.of(context).textTheme.subtitle1,
- )
- ],
- ),
- SizedBox(height: 140, child:
- DottedLine( direction: Axis.vertical,lineLength: double.infinity, dashColor: Theme.of(context).dividerColor,),),
- Column(children: [
- Text(
- "排行榜",
- style: Theme.of(context).textTheme.subtitle1,
- )
- ],),
- ],
- ),
- ),
- const SizedBox(
- height: 24,
- ),
- Center(
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: <Widget>[
- _buttonIndex == 0
- ? CancelButton(
- backgroundColor: Theme.of(context).colorScheme.secondary,
- height: 35,
- width: 135,
- textColor: Colors.white,
- callback: () {
- Navigator.of(context).pop(false);
- },
- content: "再来一次")
- : CancelButton(
- height: 35,
- width: 135,
- callback: () {
- Navigator.of(context).pop(false);
- },
- content: "再来一次"),
- const SizedBox(
- width: 16,
- ),
- _buttonIndex == 1 ? CancelButton(backgroundColor: Theme.of(context).colorScheme.secondary, height: 35, width: 135, textColor: Colors.white, callback: () => Navigator.of(context).pop(true), content: "结束运动") : CancelButton(height: 35, width: 135, callback: () => Navigator.of(context).pop(true), content: "结束运动")
- ],
- ),
- ),
- ],
- ),
- ),
- ),
- useSafeArea: false,
- ).then((value) {
- if (value == true) {
- Navigator.of(context).pop();
- }else{
- setState(() {
- _index = 0;
- _missionIndex = 0;
- _restIndex = -1;
- missions.clear();
- missions = List.generate(5, (i) => Mission());
- _startMission();
- });
- }
- });
- }
- _playAudio(List<String> audios) {
- _audioPlayer.setAudioSource(ConcatenatingAudioSource(children: audios.map((e) => AudioSource.uri(Uri.parse("asset:///assets/audio/$e.mp3"))).toList())).then((value) => _audioPlayer.play());
- }
- @override
- Widget build(BuildContext context) {
- return WillPopScope(
- onWillPop: () async {
- return false;
- },
- child: Scaffold(
- backgroundColor: Colors.black,
- body: Center(
- child: AspectRatio(
- aspectRatio: _videoPlayerController.value.aspectRatio,
- child: Stack(
- fit: StackFit.expand,
- children: [
- VideoPlayer(_videoPlayerController),
- Align(
- alignment: Alignment.bottomCenter,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- for (var i = 0; i < missions.length; i++)
- Expanded(
- child: Container(
- padding: i > 0 ? const EdgeInsets.only(left: 6) : EdgeInsets.zero,
- child: Column(
- children: [
- Padding(
- padding: const EdgeInsets.all(6.0),
- child: Text(
- "第${groupLabel[i]}组",
- style: Theme.of(context).textTheme.subtitle1?.copyWith(color: Colors.white),
- ),
- ),
- LinearProgressIndicator(
- value: _missionIndex > i
- ? 1.0
- : _missionIndex == i && missions[i].count > 0
- ? missions[i].count / missions[i].target
- : 0.0,
- backgroundColor: Colors.grey,
- ),
- ],
- mainAxisSize: MainAxisSize.min,
- ),
- ),
- )
- ],
- ),
- ),
- Positioned(
- top: 20,
- right: 0,
- child: Container(
- decoration: BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(50.0), bottomLeft: Radius.circular(50.0)), color: Colors.black.withOpacity(.5)),
- child: InkWell(
- onTap: () {
- Navigator.pop(context);
- },
- child: Padding(
- padding: const EdgeInsets.fromLTRB(12.0, 10.0, 12.0, 10.0),
- child: Row(
- children: [
- arrowBackShoe(),
- Text(
- "左踮脚 · 返回",
- style: Theme.of(context).textTheme.subtitle1?.copyWith(color: Colors.white),
- )
- ],
- ),
- ),
- ),
- )),
- Positioned(
- top: 30,
- child: SafeArea(
- child: Container(
- constraints: BoxConstraints(maxWidth: 185),
- padding: const EdgeInsets.all(27.0),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Container(
- decoration: BoxDecoration(borderRadius: BorderRadius.circular(8.0), color: Colors.black.withOpacity(.5)),
- padding: const EdgeInsets.all(16.0),
- child: Center(
- child: Text(
- "开合跳",
- style: Theme.of(context).textTheme.headline4?.copyWith(fontSize: 18.0),
- ),
- ),
- ),
- Container(
- width: double.infinity,
- decoration: BoxDecoration(borderRadius: BorderRadius.circular(8.0), color: Colors.black.withOpacity(.5)),
- margin: const EdgeInsets.only(top: 13.0),
- padding: const EdgeInsets.all(16.0),
- child: Column(
- children: [
- Padding(
- padding: const EdgeInsets.only(bottom: 8.0),
- child: Text(
- "第${groupLabel[_missionIndex]}组",
- style: Theme.of(context).textTheme.subtitle1?.copyWith(color: Colors.white),
- ),
- ),
- Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text(
- "${missions[_missionIndex].count.toString().padLeft(2, "0")}",
- style: Theme.of(context).textTheme.headline4?.copyWith(fontSize: 30.0, color: Theme.of(context).colorScheme.secondary),
- ),
- Text(
- " / ",
- style: Theme.of(context).textTheme.headline4?.copyWith(fontSize: 30.0),
- ),
- Text(
- "${missions[_missionIndex].target}",
- style: Theme.of(context).textTheme.headline4?.copyWith(
- fontSize: 30.0,
- ),
- ),
- ],
- ),
- Padding(
- padding: const EdgeInsets.only(top: 8.0),
- child: Text(DateFormat.toVideoTime(missions[_missionIndex].time), style: Theme.of(context).textTheme.subtitle1?.copyWith(fontSize: 18.0, color: Colors.white)),
- )
- ],
- )),
- ],
- ),
- ),
- ),
- ),
- if (_startui == true)
- Container(
- color: Colors.black54,
- child: Center(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- SizedBox(
- height: 120,
- child: DefaultTextStyle(
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 120.0, fontFamily: "DIN", color: Colors.white),
- child: AnimatedTextKit(
- totalRepeatCount: 1,
- pause: Duration.zero,
- onFinished: () => _startMission(),
- animatedTexts: [
- for (var i = 5; i > 0; i--) ScaleAnimatedText('$i', duration: const Duration(milliseconds: 1000)),
- ScaleAnimatedText('GO', duration: const Duration(milliseconds: 1000)),
- ],
- onTap: () {},
- ),
- ),
- ),
- Padding(
- padding: const EdgeInsets.only(top: 27.0, bottom: 8.0),
- child: Text("运动即将开始", style: Theme.of(context).textTheme.headline4?.copyWith(fontSize: 18.0)),
- ),
- Text("请做好拉伸准备,避免肌肉拉伤", style: Theme.of(context).textTheme.subtitle1?.copyWith(color: Colors.white)),
- ],
- ),
- ),
- ),
- if (_restIndex > -1)
- Container(
- color: Colors.black54,
- child: Center(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- SizedBox(
- width: 100.0,
- height: 100.0,
- child: Stack(
- fit: StackFit.expand,
- children: [
- Center(
- child: Padding(
- padding: const EdgeInsets.only(top: 10.0),
- child: Text(
- "${10 - _restIndex}",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 60.0, fontFamily: "DIN", color: Theme.of(context).colorScheme.secondary),
- ),
- ),
- ),
- CircularProgressIndicator(
- value: _restIndex / 10.0,
- strokeWidth: 6,
- backgroundColor: Theme.of(context).colorScheme.secondary,
- valueColor: AlwaysStoppedAnimation(
- Color(0xff82785c),
- ))
- ],
- ),
- ),
- Padding(
- padding: const EdgeInsets.only(top: 27.0, bottom: 8.0),
- child: Text("第${groupLabel[_missionIndex + 1]}组运动即将开始", style: Theme.of(context).textTheme.headline4?.copyWith(fontSize: 18.0)),
- ),
- Text("请做好充分休息,让肌肉放松下", style: Theme.of(context).textTheme.subtitle1?.copyWith(color: Colors.white)),
- ],
- ),
- ),
- ),
- ],
- ),
- ),
- ),
- ),
- );
- }
- }
- class Mission {
- int state = 0;
- int count = 0;
- int target = 10;
- int time = 0;
- }
|