import 'dart:io'; import 'dart:math'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:get_it/get_it.dart'; import 'package:sport/application.dart'; import 'package:sport/bean/game.dart'; import 'package:sport/pages/game/game_detail.dart'; import 'package:sport/pages/game/game_guide.dart'; import 'package:sport/pages/game/game_info.dart'; import 'package:sport/provider/bluetooth.dart'; import 'package:sport/provider/game_model.dart'; import 'package:sport/router/navigator_util.dart'; import 'package:sport/services/api/inject_api.dart'; import 'package:sport/services/app_subscription_state.dart'; import 'package:sport/services/inject_route_aware.dart'; import 'package:sport/widgets/appbar.dart'; import 'package:sport/widgets/dialog/ble_wait_dialog.dart'; import 'package:sport/widgets/image.dart'; import 'package:umeng_common_sdk/umeng_common_sdk.dart'; import 'package:wakelock/wakelock.dart'; class SportListPage extends StatefulWidget { final int index; final int runUserCount; final List games; SportListPage({this.index = 0, this.runUserCount = 0, required this.games}); @override State createState() => _PageState(); } class _PageState extends State with InjectApi, SubscriptionState, InjectRouteAware, WidgetsBindingObserver { List _games = []; late PageController _pageController; late Bluetooth _bluetooth; late GameModel _gameModel; late Function() gameModeListener; GameInfoData? game; int gameIndex = 0; bool gameMode = false; bool gamePlaying = false; @override void initState() { super.initState(); WidgetsBinding.instance?.addObserver(this); _gameModel = GetIt.I(); _gameModel.noCheck = true; _bluetooth = GetIt.I(); _bluetooth.h5gameRNotifier.addListener(gameModeListener = () { gameMode = _bluetooth.h5gameRNotifier.value; }); addSubscription(_bluetooth.sdkCmdStream.listen((event) { if (gameMode && !gamePlaying) { int cmd = event; if (cmd > -1) { print("sdk -- cmd $cmd"); if (cmd == 3) { _previous(); } if (cmd == 4) { _next(); } if (cmd == 6) { _back(); _bluetooth.vibrate(200, leftOrRight: 1); } if (cmd == 5) { _ok(); _bluetooth.vibrate(200, leftOrRight: 2); } } } })); WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { _checkInstall(); }); } @override void dispose() { WidgetsBinding.instance?.removeObserver(this); _gameModel.noCheck = false; _bluetooth.setupGameMode4h5(false); _bluetooth.h5gameRNotifier.removeListener(gameModeListener); gameMode = false; Wakelock.disable(); super.dispose(); // horizontal.value = false; // horizontal.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); switch (state) { case AppLifecycleState.resumed: if (game?.h5 == 0) { _bluetooth.gameInit(game?.id ?? 0); gamePlaying = false; if(gameMode == true) showWaitDialog(context); } Stream.periodic(const Duration(seconds: 2)).take(5).forEach((element) { if (mounted) { _bluetooth.setupGameMode4h5(gameMode); } }); break; case AppLifecycleState.paused: // if (mounted) { // _bluetooth.setupGameMode4h5(false); // } break; default: break; } } @override void didPop() { gamePlaying = false; } @override void didPopNext() { gamePlaying = false; } @override void didPushNext() { gamePlaying = true; } _checkInstall() async { List games = widget.games.isNotEmpty == true ? List.from(widget.games) : (await api.getGameAll()).results; for (var i = 0; i < games.length; i++) { games[i].isLocal = await isInstalled(games[i]) == 0; } games.sort((a, b) => a.isLocal == true ? -1 : 1); setState(() { _games = games; }); } _back() { Navigator.pop(context, false); } _ok() { if (_games.isNotEmpty == true && gamePlaying != true && appLifecycleState == AppLifecycleState.resumed) { gamePlaying = true; // _bluetooth.setupGameMode4h5(gameMode = true); _gameModel.gameCount++; game = _games[gameIndex]; startGame(context, game, launch4GameCenter: true).then((value) => gamePlaying = false); UmengCommonSdk.onEvent("game_start_sport_list", {"id": "${game?.id}"}); } } _previous() { setState(() { gameIndex = --gameIndex % _games.length; _switchPage(gameIndex); }); } _next() { setState(() { gameIndex = ++gameIndex % _games.length; _switchPage(gameIndex); }); } _switchPage(int index) { if (!mounted) return; int page = _pageController.page?.toInt() ?? 0; int currentPage = index ~/ 10; if (currentPage != page) { _pageController.animateToPage(currentPage, duration: Duration(milliseconds: 500), curve: Curves.linear); } } @override Widget build(BuildContext context) { final _padding = const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0); List games = _games; return OrientationBuilder( builder: (BuildContext context, Orientation orientation) { final bool landscape = orientation == Orientation.landscape; return Scaffold( backgroundColor: landscape ? Colors.black : Colors.black.withOpacity(0.9), appBar: AppBar( automaticallyImplyLeading: false, centerTitle: false, titleSpacing: landscape ? 50 : 16, systemOverlayStyle: SystemUiOverlayStyle.light, backgroundColor: Colors.transparent, title: Row( children: [ Text( "运动列表", style: titleStyle.copyWith(color: Colors.white), ), const SizedBox( width: 20.0, ), TextButton( onPressed: () { if (landscape) { SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); _bluetooth.setupGameMode4h5(gameMode = false); } else { SystemChrome.setPreferredOrientations(Platform.isIOS ? [DeviceOrientation.landscapeRight] : [DeviceOrientation.landscapeLeft]); _bluetooth.setupGameMode4h5(gameMode = true); } }, child: Row( children: [ Row(children: [ Image.asset( landscape ? "lib/assets/img/icon_pop_vertical_screen.png" : "lib/assets/img/icon_pop_horizontal_screen.png", height: 20.0, ), const SizedBox( width: 8.0, ), Text( landscape ? "竖屏切换" : "横屏切换", style: Theme.of(context).textTheme.headline6!.copyWith(color: Colors.white), ) ]), ], )), ], ), actions: [ landscape ? InkWell( onTap: _back, child: Padding( padding: const EdgeInsets.fromLTRB(8.0,5.0,12.0, 5.0), child: Row( children: [ arrowBackShoe(), Text( "左踮脚 · 返回", style: Theme.of(context).textTheme.subtitle1?.copyWith(color: Colors.white), ) ], ), ), ):IconButton( icon: Image.asset( "lib/assets/img/btn_close_white.png", height: 20.0, ), onPressed: () =>_back()) ], ), body: landscape ? Column( children: [ Expanded( child: PageView.builder( controller: _pageController = PageController(initialPage: gameIndex ~/ 10), scrollDirection: Axis.vertical, itemCount: ((games.length - 1) ~/ 10) + 1, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 30.0, vertical: 12.0), child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { double width = constraints.biggest.width; double height = constraints.biggest.height; double mainAxisSpacing = 12.0; double crossAxisSpacing = 10.0; int crossAxisCount = 5; double boxWidth = width / 5 - crossAxisSpacing / 4; double boxHeight = (height / 2.0) - mainAxisSpacing; return AlignedGridView.count( crossAxisCount: crossAxisCount, shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemCount: min(10, games.length - index * 10), crossAxisSpacing: 10.0, mainAxisSpacing: mainAxisSpacing, itemBuilder: (context, i) { int _index = index * 10 + i; GameInfoData e = games[_index]; return InkWell( onTap: () { setState(() { gameIndex = _index; _ok(); }); }, child: Container( width: boxWidth, height: boxHeight, alignment: Alignment.center, child: Column( mainAxisSize: MainAxisSize.min, children: [ AnimatedContainer( duration: Duration(milliseconds: 300), width: min(boxWidth, boxHeight) * (_index == gameIndex ? .7 : .6), height: min(boxWidth, boxHeight) * (_index == gameIndex ? .7 : .6), decoration: BoxDecoration( image: DecorationImage( image: CachedNetworkImageProvider( "${e.cover}", ), fit: BoxFit.cover), borderRadius: BorderRadius.circular(10.0), border: Border.all(width: 4, color: _index == gameIndex ? Theme.of(context).colorScheme.secondary : Colors.transparent)), ), const SizedBox( height: 8.0, ), Text( "${e.name}", maxLines: 1, style: Theme.of(context).textTheme.subtitle1!.copyWith(color: _index == gameIndex ? Theme.of(context).colorScheme.secondary : Colors.white), ) ], ), ), ); }); }, ), ); }), ), Container( color: Colors.white.withOpacity(.1), padding: const EdgeInsets.symmetric(vertical: 8.0), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Row( children: [ Row(children: [ Image.asset( "lib/assets/img/gamepop_leftarrow_notes.png", height: 20.0, ), const SizedBox( width: 8.0, ), Text( "左踏 · 上一个", style: Theme.of(context).textTheme.headline6!, ) ]), ], ), Row( children: [ Row(children: [ Image.asset( "lib/assets/img/gamepop_rihgtarrow_notes.png", height: 20.0, ), const SizedBox( width: 8.0, ), Text( "右踏 · 下一个", style: Theme.of(context).textTheme.headline6!, ) ]), ], ), Row( children: [ Row(children: [ Image.asset( "lib/assets/img/gamepop_icon_right_notes.png", height: 20.0, ), const SizedBox( width: 8.0, ), Text( "右踮脚 · 确认", style: Theme.of(context).textTheme.headline6!, ) ]), ], ), ], ), ), ) ], ) : CustomScrollView( slivers: [ SliverToBoxAdapter( child: InkWell( onTap: () { NavigatorUtil.goPage( context, (context) => GameGuide( game: GameInfoData(), base: true, launch4GameCenter: true, detailGuide: true, )); }, child: Container( margin: _padding, decoration: BoxDecoration( borderRadius: BorderRadius.circular(10.0), color: Colors.white, ), padding: const EdgeInsets.symmetric(horizontal: 14.0, vertical: 15.0), child: Row( children: [ Image.asset( "lib/assets/img/gamepop_icon_right_notes_black.png", height: 20.0, ), Expanded( child: Container( margin: const EdgeInsets.only(left: 12.0), child: Text( "游戏通用操作教程", style: Theme.of(context).textTheme.subtitle1, ), ), ), arrowRight() ], )), ), ), for (var e in games) SliverToBoxAdapter( child: Padding( padding: _padding, child: GestureDetector( onTap: () => NavigatorUtil.goPage(context, (context) => GameDetailsPage(e)), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(10.0), color: Colors.white, ), padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 12.0), child: Row( children: [ ClipRRect( borderRadius: BorderRadius.circular(6.0), child: CachedNetworkImage( width: 60.0, height: 60.0, fit: BoxFit.cover, imageUrl: "${e.cover}", ), ), const SizedBox( width: 12.0, ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( e.name ?? "", style: Theme.of(context).textTheme.headline3, ), const SizedBox( height: 8, ), Row( children: [ Text("${e.playMinute}分钟", style: Theme.of(context).textTheme.bodyText1), Container( decoration: BoxDecoration(shape: BoxShape.circle, color: const Color(0xff999999)), margin: const EdgeInsets.symmetric(horizontal: 4), width: 2, height: 2, ), Text("${difficulty[(e.difficulty ?? 0) ~/ 40.0]}", style: Theme.of(context).textTheme.bodyText1), ], ), ], ), ), Container( width: 110.0, child: StartButtonWidget( e, (v) { setState(() { gameIndex = games.indexOf(e); _ok(); }); }, height: 35.0, textStyle: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white), ), ) ], ), ), )), ) ], ), ); }, ); } }