2023-05-06 21:14:12 +00:00
import ' package:flutter/material.dart ' ;
import ' package:http/http.dart ' as http ;
2023-06-06 21:04:49 +00:00
import ' package:shared_preferences/shared_preferences.dart ' ;
import ' dart:developer ' as developer ;
2023-05-07 17:58:01 +00:00
import ' dart:convert ' ;
2023-06-06 21:04:49 +00:00
import ' package:flutter/services.dart ' ;
2023-05-07 17:58:01 +00:00
import ' package:tetra_stats/data_objects/tetrio.dart ' ;
2023-05-16 20:07:18 +00:00
import ' package:tetra_stats/services/tetrio_crud.dart ' ;
2023-05-20 20:41:01 +00:00
import ' package:tetra_stats/services/sqlite_db_controller.dart ' ;
2023-06-03 19:10:28 +00:00
import ' package:fl_chart/fl_chart.dart ' ;
2023-05-06 21:14:12 +00:00
2023-05-30 20:37:10 +00:00
extension StringExtension on String {
String capitalize ( ) {
2023-06-03 14:56:08 +00:00
return " ${ this [ 0 ] . toUpperCase ( ) } ${ substring ( 1 ) . toLowerCase ( ) } " ;
2023-05-30 20:37:10 +00:00
}
}
2023-06-03 14:56:08 +00:00
String _searchFor = " dan63047 " ;
2023-06-04 15:10:20 +00:00
Future < TetrioPlayer > ? me ;
2023-05-20 20:41:01 +00:00
DB db = DB ( ) ;
2023-06-06 21:04:49 +00:00
late TetrioService teto ;
late SharedPreferences prefs ;
2023-05-29 19:10:14 +00:00
const allowedHeightForPlayerIdInPixels = 40.0 ;
2023-05-25 19:21:56 +00:00
const allowedHeightForPlayerBioInPixels = 30.0 ;
const givenTextHeightByScreenPercentage = 0.3 ;
2023-05-07 17:58:01 +00:00
class MainView extends StatefulWidget {
const MainView ( { Key ? key } ) : super ( key: key ) ;
2023-05-06 21:14:12 +00:00
2023-05-29 19:10:14 +00:00
String get title = > " Tetra Stats: $ _searchFor " ;
2023-05-07 17:58:01 +00:00
@ override
2023-06-05 22:21:29 +00:00
State < MainView > createState ( ) = > _MainState ( ) ;
2023-05-07 17:58:01 +00:00
}
2023-06-06 21:04:49 +00:00
Future < void > copyToClipboard ( String text ) async {
await Clipboard . setData ( ClipboardData ( text: text ) ) ;
}
2023-05-29 19:10:14 +00:00
Future < TetrioPlayer > fetchTetrioPlayer ( String user ) async {
2023-06-03 19:10:28 +00:00
var url = Uri . https ( ' ch.tetr.io ' , ' api/users/ ${ user . toLowerCase ( ) . trim ( ) } ' ) ;
2023-05-29 19:10:14 +00:00
final response = await http . get ( url ) ;
2023-05-07 17:58:01 +00:00
2023-05-29 19:10:14 +00:00
if ( response . statusCode = = 200 ) {
2023-06-04 15:10:20 +00:00
if ( jsonDecode ( response . body ) [ ' success ' ] ) {
return TetrioPlayer . fromJson (
2023-06-06 21:04:49 +00:00
jsonDecode ( response . body ) [ ' data ' ] [ ' user ' ] , DateTime . fromMillisecondsSinceEpoch ( jsonDecode ( response . body ) [ ' cache ' ] [ ' cached_at ' ] , isUtc: true ) , true ) ;
2023-06-04 15:10:20 +00:00
} else {
2023-06-06 21:04:49 +00:00
developer . log ( " fetchTetrioPlayer User dosen't exist " , name: " main_view " , error: response . body ) ;
2023-06-04 15:10:20 +00:00
throw Exception ( " User doesn't exist " ) ;
}
2023-05-29 19:10:14 +00:00
} else {
2023-06-06 21:04:49 +00:00
developer . log ( " fetchTetrioPlayer Failed to fetch player " , name: " main_view " , error: response . statusCode ) ;
2023-05-29 19:10:14 +00:00
throw Exception ( ' Failed to fetch player ' ) ;
2023-05-07 17:58:01 +00:00
}
2023-05-29 19:10:14 +00:00
}
2023-05-11 16:08:42 +00:00
2023-06-05 22:21:29 +00:00
class _MainState extends State < MainView > with SingleTickerProviderStateMixin {
2023-06-03 14:56:08 +00:00
final bodyGlobalKey = GlobalKey ( ) ;
final List < Widget > myTabs = [
2023-06-03 19:10:28 +00:00
const Tab ( text: " Tetra League " ) ,
const Tab ( text: " 40 Lines " ) ,
const Tab ( text: " Blitz " ) ,
const Tab ( text: " Other " ) ,
2023-06-03 14:56:08 +00:00
] ;
2023-05-29 19:10:14 +00:00
bool _searchBoolean = false ;
2023-06-03 14:56:08 +00:00
late TabController _tabController ;
late ScrollController _scrollController ;
late bool fixedScroll ;
2023-05-29 19:10:14 +00:00
Widget _searchTextField ( ) {
return TextField (
2023-06-03 19:10:28 +00:00
maxLength: 25 ,
2023-06-06 21:04:49 +00:00
decoration: const InputDecoration ( counter: Offstage ( ) ) ,
2023-05-29 19:10:14 +00:00
style: const TextStyle (
shadows: < Shadow > [
Shadow (
offset: Offset ( 0.0 , 0.0 ) ,
blurRadius: 3.0 ,
color: Colors . black ,
) ,
Shadow (
offset: Offset ( 0.0 , 0.0 ) ,
blurRadius: 8.0 ,
color: Colors . black ,
) ,
] ,
) ,
2023-06-04 15:10:20 +00:00
onSubmitted: ( String value ) {
2023-06-05 22:21:29 +00:00
changePlayer ( value ) ;
2023-06-04 15:10:20 +00:00
} ,
2023-05-29 19:10:14 +00:00
) ;
2023-05-06 21:14:12 +00:00
}
2023-06-03 14:56:08 +00:00
@ override
void initState ( ) {
2023-06-06 21:04:49 +00:00
db . open ( ) ;
teto = TetrioService ( db ) ;
2023-06-03 14:56:08 +00:00
_scrollController = ScrollController ( ) ;
_tabController = TabController ( length: 4 , vsync: this ) ;
2023-06-06 21:04:49 +00:00
_getPreferences ( ) . then ( ( value ) = > changePlayer ( prefs . getString ( " player " ) ? ? " dan63047 " ) ) ;
2023-06-03 14:56:08 +00:00
super . initState ( ) ;
2023-06-06 21:04:49 +00:00
developer . log ( " Main view initialized " , name: " main_view " ) ;
2023-06-03 14:56:08 +00:00
}
@ override
void dispose ( ) {
_tabController . dispose ( ) ;
_scrollController . dispose ( ) ;
super . dispose ( ) ;
2023-06-06 21:04:49 +00:00
developer . log ( " Main view disposed " , name: " main_view " ) ;
}
Future < void > _getPreferences ( ) async {
prefs = await SharedPreferences . getInstance ( ) ;
2023-06-03 14:56:08 +00:00
}
2023-06-05 22:21:29 +00:00
void changePlayer ( String player ) {
setState ( ( ) {
2023-06-06 21:04:49 +00:00
_tabController . animateTo ( 0 , duration: const Duration ( milliseconds: 300 ) ) ;
2023-06-05 22:21:29 +00:00
_searchFor = player ;
me = fetchTetrioPlayer ( player ) ;
} ) ;
}
2023-06-06 21:04:49 +00:00
// _scrollListener() {
// if (fixedScroll) {
// _scrollController.jumpTo(0);
// }
// }
2023-06-03 14:56:08 +00:00
2023-06-06 21:04:49 +00:00
// _smoothScrollToTop() {
// _scrollController.animateTo(
// 0,
// duration: const Duration(microseconds: 300),
// curve: Curves.ease,
// );
// }
2023-06-03 14:56:08 +00:00
2023-05-06 21:14:12 +00:00
@ override
Widget build ( BuildContext context ) {
2023-05-07 17:58:01 +00:00
return Scaffold (
2023-06-05 22:21:29 +00:00
drawer: NavDrawer ( changePlayer ) ,
2023-05-29 19:10:14 +00:00
appBar: AppBar (
title: ! _searchBoolean
? Text (
widget . title ,
style: const TextStyle (
shadows: < Shadow > [
Shadow (
offset: Offset ( 0.0 , 0.0 ) ,
blurRadius: 3.0 ,
color: Colors . black ,
) ,
Shadow (
offset: Offset ( 0.0 , 0.0 ) ,
blurRadius: 8.0 ,
color: Colors . black ,
) ,
] ,
) ,
)
: _searchTextField ( ) ,
backgroundColor: Colors . black ,
actions: [
! _searchBoolean
? IconButton (
onPressed: ( ) {
setState ( ( ) {
_searchBoolean = true ;
} ) ;
} ,
icon: const Icon ( Icons . search ) ,
tooltip: " Search player " ,
)
: IconButton (
onPressed: ( ) {
setState ( ( ) {
_searchBoolean = false ;
} ) ;
} ,
icon: const Icon ( Icons . clear ) ,
tooltip: " Close search " ,
2023-05-25 19:21:56 +00:00
) ,
2023-05-29 19:10:14 +00:00
PopupMenuButton (
2023-06-05 22:21:29 +00:00
itemBuilder: ( BuildContext context ) = > < PopupMenuEntry > [
const PopupMenuItem (
value: " /compare " ,
2023-05-29 19:10:14 +00:00
child: Text ( ' Compare ' ) ,
2023-05-25 19:21:56 +00:00
) ,
2023-06-05 22:21:29 +00:00
const PopupMenuItem (
value: " /states " ,
2023-05-29 19:10:14 +00:00
child: Text ( ' States ' ) ,
2023-05-25 19:21:56 +00:00
) ,
2023-06-05 22:21:29 +00:00
const PopupMenuItem (
value: " /settings " ,
2023-05-29 19:10:14 +00:00
child: Text ( ' Settings ' ) ,
2023-05-25 19:21:56 +00:00
) ,
2023-05-29 19:10:14 +00:00
] ,
2023-06-05 22:21:29 +00:00
onSelected: ( value ) {
Navigator . pushNamed ( context , value ) ;
} ,
2023-05-25 19:21:56 +00:00
) ,
] ,
2023-05-06 21:14:12 +00:00
) ,
2023-05-29 19:10:14 +00:00
body: SafeArea (
child: FutureBuilder < TetrioPlayer > (
future: me ,
builder: ( context , snapshot ) {
2023-06-06 21:04:49 +00:00
developer . log ( " builder ( $ context ): $ snapshot " , name: " main_view " ) ;
2023-06-04 15:10:20 +00:00
if ( snapshot . connectionState = = ConnectionState . waiting ) {
return const Center (
child: CircularProgressIndicator (
color: Colors . white ,
) ) ;
}
2023-05-29 19:10:14 +00:00
if ( snapshot . hasData ) {
2023-06-03 14:56:08 +00:00
bool bigScreen = MediaQuery . of ( context ) . size . width > 768 ;
return NestedScrollView (
controller: _scrollController ,
headerSliverBuilder: ( context , value ) {
return [
2023-06-04 15:10:20 +00:00
SliverToBoxAdapter ( child: _UserThingy ( player: snapshot . data ! ) ) ,
2023-06-03 14:56:08 +00:00
SliverToBoxAdapter (
child: TabBar (
controller: _tabController ,
isScrollable: true ,
tabs: myTabs ,
2023-06-06 21:04:49 +00:00
onTap: ( int tabId ) {
setState ( ( ) {
developer . log ( " Tab changed to $ tabId " , name: " main_view " ) ;
} ) ;
2023-06-04 15:10:20 +00:00
} ,
2023-06-03 14:56:08 +00:00
) ,
) ,
] ;
} ,
body: TabBarView (
controller: _tabController ,
children: [
ListView . builder (
physics: const ClampingScrollPhysics ( ) ,
itemCount: 1 ,
itemBuilder: ( BuildContext context , int index ) {
return Column (
children: ( snapshot . data ! . tlSeason1 . gamesPlayed > 0 )
? [
2023-06-04 15:10:20 +00:00
Text ( " Tetra League " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
if ( snapshot . data ! . tlSeason1 . gamesPlayed > = 10 )
2023-06-03 19:10:28 +00:00
Wrap (
direction: Axis . horizontal ,
alignment: WrapAlignment . spaceAround ,
2023-06-04 15:10:20 +00:00
crossAxisAlignment: WrapCrossAlignment . center ,
2023-06-03 19:10:28 +00:00
clipBehavior: Clip . hardEdge ,
children: [
2023-06-04 15:10:20 +00:00
snapshot . data ! . userId = = " 5e32fc85ab319c2ab1beb07c "
2023-06-03 21:25:16 +00:00
? Image . asset (
" res/icons/kagari.png " ,
height: 128 ,
)
: Image . asset (
" res/tetrio_tl_alpha_ranks/ ${ snapshot . data ! . tlSeason1 . rank } .png " ,
height: 128 ,
) ,
2023-06-03 19:10:28 +00:00
Column (
children: [
2023-06-04 15:10:20 +00:00
Text ( " ${ snapshot . data ! . tlSeason1 . rating . toStringAsFixed ( 2 ) } TR " ,
style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
2023-06-03 19:10:28 +00:00
Text (
" Top ${ ( snapshot . data ! . tlSeason1 . percentile * 100 ) . toStringAsFixed ( 2 ) } % ( ${ snapshot . data ! . tlSeason1 . percentileRank . toUpperCase ( ) } ) • Top Rank: ${ snapshot . data ! . tlSeason1 . bestRank . toUpperCase ( ) } • Glicko: ${ snapshot . data ! . tlSeason1 . glicko ? . toStringAsFixed ( 2 ) } ± ${ snapshot . data ! . tlSeason1 . rd ? . toStringAsFixed ( 2 ) } ${ snapshot . data ! . tlSeason1 . decaying ? ' • Decaying ' : ' ' } " ,
textAlign: TextAlign . center ,
) ,
] ,
) ,
] ,
2023-06-03 14:56:08 +00:00
)
else
Row (
2023-06-04 15:10:20 +00:00
mainAxisAlignment: MainAxisAlignment . center ,
2023-06-03 14:56:08 +00:00
children: [
Column (
children: [
2023-06-04 15:10:20 +00:00
Text ( " ${ 10 - snapshot . data ! . tlSeason1 . gamesPlayed } games until being ranked " ,
2023-06-03 14:56:08 +00:00
softWrap: true ,
style: TextStyle (
2023-06-04 15:10:20 +00:00
fontFamily: " Eurostile Round Extended " ,
2023-06-03 14:56:08 +00:00
fontSize: bigScreen ? 42 : 28 ,
2023-06-04 15:10:20 +00:00
overflow: TextOverflow . visible ,
2023-06-03 14:56:08 +00:00
) ) ,
] ,
)
] ,
) ,
Padding (
2023-06-04 15:10:20 +00:00
padding: const EdgeInsets . fromLTRB ( 0 , 16 , 0 , 48 ) ,
2023-06-03 14:56:08 +00:00
child: Wrap (
direction: Axis . horizontal ,
alignment: WrapAlignment . center ,
spacing: 25 ,
2023-06-04 15:10:20 +00:00
crossAxisAlignment: WrapCrossAlignment . start ,
2023-06-03 14:56:08 +00:00
clipBehavior: Clip . hardEdge ,
children: [
2023-06-04 15:10:20 +00:00
if ( snapshot . data ! . tlSeason1 . apm ! = null )
2023-06-03 14:56:08 +00:00
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . apm ! ,
2023-06-03 14:56:08 +00:00
isScreenBig: bigScreen ,
fractionDigits: 2 ,
2023-06-04 15:10:20 +00:00
playerStatLabel: " Attack \n Per Minute " ) ,
if ( snapshot . data ! . tlSeason1 . pps ! = null )
2023-06-03 14:56:08 +00:00
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . pps ! ,
2023-06-03 14:56:08 +00:00
isScreenBig: bigScreen ,
fractionDigits: 2 ,
2023-06-04 15:10:20 +00:00
playerStatLabel: " Pieces \n Per Second " ) ,
if ( snapshot . data ! . tlSeason1 . apm ! = null )
2023-06-03 14:56:08 +00:00
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . vs ! ,
2023-06-03 14:56:08 +00:00
isScreenBig: bigScreen ,
fractionDigits: 2 ,
playerStatLabel: " Versus \n Score " ) ,
2023-06-04 15:10:20 +00:00
if ( snapshot . data ! . tlSeason1 . standing > 0 )
2023-06-03 14:56:08 +00:00
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . standing , isScreenBig: bigScreen , playerStatLabel: " Leaderboard \n placement " ) ,
if ( snapshot . data ! . tlSeason1 . standingLocal > 0 )
2023-06-03 14:56:08 +00:00
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . standingLocal ,
2023-06-03 14:56:08 +00:00
isScreenBig: bigScreen ,
2023-06-04 15:10:20 +00:00
playerStatLabel: " Country LB \n placement " ) ,
2023-06-03 14:56:08 +00:00
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . gamesPlayed , isScreenBig: bigScreen , playerStatLabel: " Games \n played " ) ,
_StatCellNum ( playerStat: snapshot . data ! . tlSeason1 . gamesWon , isScreenBig: bigScreen , playerStatLabel: " Games \n won " ) ,
2023-06-03 14:56:08 +00:00
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . winrate * 100 ,
2023-06-03 14:56:08 +00:00
isScreenBig: bigScreen ,
fractionDigits: 2 ,
2023-06-04 15:10:20 +00:00
playerStatLabel: " Winrate \n precentage " ) ,
2023-06-03 14:56:08 +00:00
] ,
) ,
) ,
2023-06-04 15:10:20 +00:00
if ( snapshot . data ! . tlSeason1 . nerdStats ! = null )
2023-06-03 14:56:08 +00:00
Column (
children: [
2023-06-04 15:10:20 +00:00
Text ( " Nerd Stats " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
2023-06-03 14:56:08 +00:00
Padding (
2023-06-04 15:10:20 +00:00
padding: const EdgeInsets . fromLTRB ( 0 , 16 , 0 , 48 ) ,
2023-06-03 14:56:08 +00:00
child: Wrap (
direction: Axis . horizontal ,
alignment: WrapAlignment . center ,
spacing: 25 ,
2023-06-04 15:10:20 +00:00
crossAxisAlignment: WrapCrossAlignment . start ,
2023-06-03 14:56:08 +00:00
clipBehavior: Clip . hardEdge ,
children: [
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . nerdStats ! . app ,
2023-06-03 14:56:08 +00:00
isScreenBig: bigScreen ,
fractionDigits: 3 ,
2023-06-04 15:10:20 +00:00
playerStatLabel: " Attack \n Per Piece " ) ,
2023-06-03 14:56:08 +00:00
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . nerdStats ! . vsapm ,
2023-06-03 14:56:08 +00:00
isScreenBig: bigScreen ,
fractionDigits: 3 ,
playerStatLabel: " VS/APM " ) ,
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . nerdStats ! . dss ,
2023-06-03 14:56:08 +00:00
isScreenBig: bigScreen ,
fractionDigits: 3 ,
2023-06-04 15:10:20 +00:00
playerStatLabel: " Downstack \n Per Second " ) ,
2023-06-03 14:56:08 +00:00
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . nerdStats ! . dsp ,
2023-06-03 14:56:08 +00:00
isScreenBig: bigScreen ,
fractionDigits: 3 ,
2023-06-04 15:10:20 +00:00
playerStatLabel: " Downstack \n Per Piece " ) ,
2023-06-03 19:10:28 +00:00
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . nerdStats ! . appdsp ,
2023-06-03 19:10:28 +00:00
isScreenBig: bigScreen ,
fractionDigits: 3 ,
2023-06-04 15:10:20 +00:00
playerStatLabel: " APP + DS/P " ) ,
2023-06-03 14:56:08 +00:00
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . nerdStats ! . cheese ,
2023-06-03 14:56:08 +00:00
isScreenBig: bigScreen ,
fractionDigits: 2 ,
2023-06-04 15:10:20 +00:00
playerStatLabel: " Cheese \n Index " ) ,
2023-06-03 14:56:08 +00:00
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . nerdStats ! . gbe ,
2023-06-03 14:56:08 +00:00
isScreenBig: bigScreen ,
fractionDigits: 3 ,
2023-06-04 15:10:20 +00:00
playerStatLabel: " Garbage \n Efficiency " ) ,
2023-06-03 14:56:08 +00:00
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . nerdStats ! . nyaapp ,
2023-06-03 14:56:08 +00:00
isScreenBig: bigScreen ,
fractionDigits: 3 ,
2023-06-04 15:10:20 +00:00
playerStatLabel: " Weighted \n APP " ) ,
2023-06-03 14:56:08 +00:00
_StatCellNum (
2023-06-04 15:10:20 +00:00
playerStat: snapshot . data ! . tlSeason1 . nerdStats ! . area ,
2023-06-03 14:56:08 +00:00
isScreenBig: bigScreen ,
fractionDigits: 1 ,
playerStatLabel: " Area " )
] ) ,
)
] ,
2023-06-03 19:10:28 +00:00
) ,
2023-06-04 15:10:20 +00:00
if ( snapshot . data ! . tlSeason1 . estTr ! = null )
Padding (
padding: const EdgeInsets . fromLTRB ( 0 , 16 , 0 , 48 ) ,
child: SizedBox (
width: bigScreen ? MediaQuery . of ( context ) . size . width * 0.4 : MediaQuery . of ( context ) . size . width * 0.85 ,
child: Column (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text (
" Est. of TR: " ,
style: TextStyle ( fontSize: 24 ) ,
) ,
Text (
snapshot . data ! . tlSeason1 . estTr ! . esttr . toStringAsFixed ( 2 ) ,
style: const TextStyle ( fontSize: 24 ) ,
) ,
] ,
) ,
if ( snapshot . data ! . tlSeason1 . rating > = 0 )
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text (
" Accuracy: " ,
style: TextStyle ( fontSize: 24 ) ,
) ,
Text (
snapshot . data ! . tlSeason1 . esttracc ! . toStringAsFixed ( 2 ) ,
style: const TextStyle ( fontSize: 24 ) ,
) ,
] ,
2023-06-03 19:10:28 +00:00
) ,
2023-06-04 15:10:20 +00:00
] ,
) ,
) ,
) ,
if ( snapshot . data ! . tlSeason1 . nerdStats ! = null )
Wrap (
direction: Axis . horizontal ,
alignment: WrapAlignment . spaceAround ,
spacing: 25 ,
crossAxisAlignment: WrapCrossAlignment . start ,
clipBehavior: Clip . hardEdge ,
children: [
Padding (
padding: const EdgeInsets . fromLTRB ( 20 , 0 , 20 , 48 ) ,
child: SizedBox (
height: 300 ,
width: 300 ,
child: RadarChart (
RadarChartData (
radarShape: RadarShape . polygon ,
tickCount: 4 ,
ticksTextStyle: const TextStyle ( color: Colors . transparent , fontSize: 10 ) ,
2023-06-06 21:04:49 +00:00
radarBorderData: const BorderSide ( color: Colors . transparent , width: 1 ) ,
gridBorderData: const BorderSide ( color: Colors . white24 , width: 1 ) ,
tickBorderData: const BorderSide ( color: Colors . transparent , width: 1 ) ,
2023-06-04 15:10:20 +00:00
getTitle: ( index , angle ) {
switch ( index ) {
case 0 :
return RadarChartTitle (
text: ' APM ' ,
angle: angle ,
) ;
case 1 :
return RadarChartTitle (
text: ' PPS ' ,
angle: angle ,
) ;
case 2 :
return RadarChartTitle ( text: ' VS ' , angle: angle ) ;
case 3 :
return RadarChartTitle ( text: ' APP ' , angle: angle + 180 ) ;
case 4 :
return RadarChartTitle ( text: ' DS/S ' , angle: angle + 180 ) ;
case 5 :
return RadarChartTitle ( text: ' DS/P ' , angle: angle + 180 ) ;
case 6 :
return RadarChartTitle ( text: ' APP+DS/P ' , angle: angle + 180 ) ;
case 7 :
return RadarChartTitle ( text: ' VS/APM ' , angle: angle + 180 ) ;
case 8 :
return RadarChartTitle ( text: ' Cheese ' , angle: angle ) ;
case 9 :
return RadarChartTitle ( text: ' Gb Eff. ' , angle: angle ) ;
default :
return const RadarChartTitle ( text: ' ' ) ;
}
} ,
dataSets: [
RadarDataSet (
dataEntries: [
RadarEntry ( value: snapshot . data ! . tlSeason1 . apm ! * 1 ) ,
RadarEntry ( value: snapshot . data ! . tlSeason1 . pps ! * 45 ) ,
RadarEntry ( value: snapshot . data ! . tlSeason1 . vs ! * 0.444 ) ,
RadarEntry ( value: snapshot . data ! . tlSeason1 . nerdStats ! . app * 185 ) ,
RadarEntry ( value: snapshot . data ! . tlSeason1 . nerdStats ! . dss * 175 ) ,
RadarEntry ( value: snapshot . data ! . tlSeason1 . nerdStats ! . dsp * 450 ) ,
RadarEntry ( value: snapshot . data ! . tlSeason1 . nerdStats ! . appdsp * 140 ) ,
RadarEntry ( value: snapshot . data ! . tlSeason1 . nerdStats ! . vsapm * 60 ) ,
RadarEntry ( value: snapshot . data ! . tlSeason1 . nerdStats ! . cheese * 1.25 ) ,
RadarEntry ( value: snapshot . data ! . tlSeason1 . nerdStats ! . gbe * 315 ) ,
] ,
) ,
RadarDataSet (
fillColor: Colors . transparent ,
borderColor: Colors . transparent ,
dataEntries: [
2023-06-06 21:04:49 +00:00
const RadarEntry ( value: 0 ) ,
const RadarEntry ( value: 0 ) ,
const RadarEntry ( value: 0 ) ,
const RadarEntry ( value: 0 ) ,
const RadarEntry ( value: 0 ) ,
const RadarEntry ( value: 0 ) ,
const RadarEntry ( value: 0 ) ,
const RadarEntry ( value: 0 ) ,
const RadarEntry ( value: 0 ) ,
const RadarEntry ( value: 0 ) ,
2023-06-04 15:10:20 +00:00
] ,
)
] ,
2023-06-03 19:10:28 +00:00
) ,
2023-06-04 15:10:20 +00:00
swapAnimationDuration: const Duration ( milliseconds: 150 ) , // Optional
swapAnimationCurve: Curves . linear , // Optional
) ,
2023-06-03 19:10:28 +00:00
) ,
2023-06-04 15:10:20 +00:00
) ,
Padding (
padding: const EdgeInsets . fromLTRB ( 20 , 0 , 20 , 48 ) ,
child: SizedBox (
height: 300 ,
width: 300 ,
child: RadarChart (
RadarChartData (
radarShape: RadarShape . polygon ,
tickCount: 4 ,
ticksTextStyle: const TextStyle ( color: Colors . transparent , fontSize: 10 ) ,
2023-06-06 21:04:49 +00:00
radarBorderData: const BorderSide ( color: Colors . transparent , width: 1 ) ,
gridBorderData: const BorderSide ( color: Colors . white24 , width: 1 ) ,
tickBorderData: const BorderSide ( color: Colors . transparent , width: 1 ) ,
2023-06-04 15:10:20 +00:00
getTitle: ( index , angle ) {
switch ( index ) {
case 0 :
return RadarChartTitle (
text: ' Opener ' ,
angle: angle ,
) ;
case 1 :
return RadarChartTitle (
text: ' Stride ' ,
angle: angle ,
) ;
case 2 :
return RadarChartTitle ( text: ' Inf Ds ' , angle: angle + 180 ) ;
case 3 :
return RadarChartTitle ( text: ' Plonk ' , angle: angle ) ;
default :
return const RadarChartTitle ( text: ' ' ) ;
}
} ,
dataSets: [
RadarDataSet (
dataEntries: [
RadarEntry ( value: snapshot . data ! . tlSeason1 . playstyle ! . opener ) ,
RadarEntry ( value: snapshot . data ! . tlSeason1 . playstyle ! . stride ) ,
RadarEntry ( value: snapshot . data ! . tlSeason1 . playstyle ! . infds ) ,
RadarEntry ( value: snapshot . data ! . tlSeason1 . playstyle ! . plonk ) ,
] ,
) ,
RadarDataSet (
fillColor: Colors . transparent ,
borderColor: Colors . transparent ,
dataEntries: [
2023-06-06 21:04:49 +00:00
const RadarEntry ( value: 0 ) ,
const RadarEntry ( value: 0 ) ,
const RadarEntry ( value: 0 ) ,
const RadarEntry ( value: 0 ) ,
2023-06-04 15:10:20 +00:00
] ,
) ,
RadarDataSet (
fillColor: Colors . transparent ,
borderColor: Colors . transparent ,
dataEntries: [
2023-06-06 21:04:49 +00:00
const RadarEntry ( value: 1 ) ,
const RadarEntry ( value: 1 ) ,
const RadarEntry ( value: 1 ) ,
const RadarEntry ( value: 1 ) ,
2023-06-04 15:10:20 +00:00
] ,
)
] ,
2023-06-03 19:10:28 +00:00
) ,
2023-06-04 15:10:20 +00:00
swapAnimationDuration: const Duration ( milliseconds: 150 ) , // Optional
swapAnimationCurve: Curves . linear , // Optional
) ,
2023-06-03 19:10:28 +00:00
) ,
2023-06-04 15:10:20 +00:00
) ,
] ,
)
]
: [
Text ( " That user never played Tetra League " ,
style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
] ,
) ;
} ,
) ,
ListView . builder (
physics: const ClampingScrollPhysics ( ) ,
itemCount: 1 ,
itemBuilder: ( BuildContext context , int index ) {
return Column (
children: ( snapshot . data ! . sprint . isNotEmpty )
? [
Text ( " 40 Lines " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
Text ( snapshot . data ! . sprint [ 0 ] ! . endContext ! . finalTime . toString ( ) ,
style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
if ( snapshot . data ! . sprint [ 0 ] ! . rank ! = null )
_StatCellNum (
playerStat: snapshot . data ! . sprint [ 0 ] ! . rank ! , playerStatLabel: " Leaderboard Placement " , isScreenBig: bigScreen ) ,
Padding (
padding: const EdgeInsets . fromLTRB ( 0 , 48 , 0 , 48 ) ,
child: Wrap (
direction: Axis . horizontal ,
alignment: WrapAlignment . spaceAround ,
crossAxisAlignment: WrapCrossAlignment . center ,
clipBehavior: Clip . hardEdge ,
spacing: 25 ,
children: [
_StatCellNum (
playerStat: snapshot . data ! . sprint [ 0 ] ! . endContext ! . piecesPlaced ,
playerStatLabel: " Pieces \n Placed " ,
isScreenBig: bigScreen ) ,
_StatCellNum (
playerStat: snapshot . data ! . sprint [ 0 ] ! . endContext ! . pps ,
playerStatLabel: " Pieces \n Per Second " ,
fractionDigits: 2 ,
isScreenBig: bigScreen ) ,
_StatCellNum (
playerStat: snapshot . data ! . sprint [ 0 ] ! . endContext ! . finesse . faults ,
playerStatLabel: " Finesse \n Faults " ,
isScreenBig: bigScreen ) ,
_StatCellNum (
playerStat: snapshot . data ! . sprint [ 0 ] ! . endContext ! . finessePercentage * 100 ,
playerStatLabel: " Finesse \n Percentage " ,
fractionDigits: 2 ,
isScreenBig: bigScreen ) ,
_StatCellNum (
playerStat: snapshot . data ! . sprint [ 0 ] ! . endContext ! . inputs ,
playerStatLabel: " Key \n Presses " ,
isScreenBig: bigScreen ) ,
_StatCellNum (
playerStat: snapshot . data ! . sprint [ 0 ] ! . endContext ! . kpp ,
playerStatLabel: " KP Per \n Piece " ,
fractionDigits: 2 ,
isScreenBig: bigScreen ) ,
_StatCellNum (
playerStat: snapshot . data ! . sprint [ 0 ] ! . endContext ! . kps ,
playerStatLabel: " KP Per \n Second " ,
fractionDigits: 2 ,
isScreenBig: bigScreen ) ,
2023-06-03 19:10:28 +00:00
] ,
) ,
) ,
2023-06-04 15:10:20 +00:00
Padding (
padding: const EdgeInsets . fromLTRB ( 0 , 16 , 0 , 48 ) ,
child: SizedBox (
width: bigScreen ? MediaQuery . of ( context ) . size . width * 0.4 : MediaQuery . of ( context ) . size . width * 0.85 ,
child: Column (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " All Clears: " , style: TextStyle ( fontSize: 24 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . clears . allClears . toString ( ) ,
style: const TextStyle ( fontSize: 24 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " Holds: " , style: TextStyle ( fontSize: 24 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . holds . toString ( ) ,
style: const TextStyle ( fontSize: 24 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " T-spins total: " , style: TextStyle ( fontSize: 24 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . tSpins . toString ( ) ,
style: const TextStyle ( fontSize: 24 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - T-spin zero: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . clears . tSpinZeros . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - T-spin singles: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . clears . tSpinSingles . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - T-spin doubles: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . clears . tSpinDoubles . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - T-spin triples: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . clears . tSpinTriples . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - T-spin mini zero: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . clears . tSpinMiniZeros . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - T-spin mini singles: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . clears . tSpinMiniSingles . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - T-spin mini doubles: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . clears . tSpinMiniDoubles . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " Line clears: " , style: TextStyle ( fontSize: 24 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . lines . toString ( ) ,
style: const TextStyle ( fontSize: 24 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - Singles: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . clears . singles . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - Doubles: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . clears . doubles . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - Triples: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . clears . triples . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
2023-06-03 19:10:28 +00:00
] ,
2023-06-03 21:25:16 +00:00
) ,
2023-06-04 15:10:20 +00:00
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - Quads: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . sprint [ 0 ] ! . endContext ! . clears . quads . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
2023-06-03 21:25:16 +00:00
] ,
2023-06-04 15:10:20 +00:00
) ,
2023-06-03 19:10:28 +00:00
] ,
) ,
) ,
) ,
2023-06-04 15:10:20 +00:00
]
: [
Text ( " That user never played 40 Lines " ,
style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) )
] ,
) ;
} ) ,
ListView . builder (
physics: const ClampingScrollPhysics ( ) ,
itemCount: 1 ,
itemBuilder: ( BuildContext context , int index ) {
return Column (
children: ( snapshot . data ! . blitz . isNotEmpty )
? [
Text ( " Blitz " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
Text ( snapshot . data ! . blitz [ 0 ] ! . endContext ! . score . toString ( ) ,
style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
if ( snapshot . data ! . blitz [ 0 ] ! . rank ! = null )
_StatCellNum (
playerStat: snapshot . data ! . blitz [ 0 ] ! . rank ! , playerStatLabel: " Leaderboard Placement " , isScreenBig: bigScreen ) ,
Padding (
padding: const EdgeInsets . fromLTRB ( 0 , 48 , 0 , 48 ) ,
child: Wrap (
direction: Axis . horizontal ,
alignment: WrapAlignment . spaceAround ,
crossAxisAlignment: WrapCrossAlignment . start ,
clipBehavior: Clip . hardEdge ,
spacing: 25 ,
children: [
_StatCellNum (
playerStat: snapshot . data ! . blitz [ 0 ] ! . endContext ! . level , playerStatLabel: " Level " , isScreenBig: bigScreen ) ,
_StatCellNum (
playerStat: snapshot . data ! . blitz [ 0 ] ! . endContext ! . spp ,
playerStatLabel: " Score \n Per Piece " ,
fractionDigits: 2 ,
isScreenBig: bigScreen ) ,
_StatCellNum (
playerStat: snapshot . data ! . blitz [ 0 ] ! . endContext ! . piecesPlaced ,
playerStatLabel: " Pieces \n Placed " ,
isScreenBig: bigScreen ) ,
_StatCellNum (
playerStat: snapshot . data ! . blitz [ 0 ] ! . endContext ! . pps ,
playerStatLabel: " Pieces \n Per Second " ,
fractionDigits: 2 ,
isScreenBig: bigScreen ) ,
_StatCellNum (
playerStat: snapshot . data ! . blitz [ 0 ] ! . endContext ! . finesse . faults ,
playerStatLabel: " Finesse \n Faults " ,
isScreenBig: bigScreen ) ,
_StatCellNum (
playerStat: snapshot . data ! . blitz [ 0 ] ! . endContext ! . finessePercentage * 100 ,
playerStatLabel: " Finesse \n Percentage " ,
fractionDigits: 2 ,
isScreenBig: bigScreen ) ,
_StatCellNum (
playerStat: snapshot . data ! . blitz [ 0 ] ! . endContext ! . inputs , playerStatLabel: " Key \n Presses " , isScreenBig: bigScreen ) ,
_StatCellNum (
playerStat: snapshot . data ! . blitz [ 0 ] ! . endContext ! . kpp ,
playerStatLabel: " KP Per \n Piece " ,
fractionDigits: 2 ,
isScreenBig: bigScreen ) ,
_StatCellNum (
playerStat: snapshot . data ! . blitz [ 0 ] ! . endContext ! . kps ,
playerStatLabel: " KP Per \n Second " ,
fractionDigits: 2 ,
isScreenBig: bigScreen ) ,
] ,
) ,
) ,
Padding (
padding: const EdgeInsets . fromLTRB ( 0 , 16 , 0 , 48 ) ,
child: SizedBox (
width: bigScreen ? MediaQuery . of ( context ) . size . width * 0.4 : MediaQuery . of ( context ) . size . width * 0.85 ,
child: Column (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " All Clears: " , style: TextStyle ( fontSize: 24 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . clears . allClears . toString ( ) ,
style: const TextStyle ( fontSize: 24 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " Holds: " , style: TextStyle ( fontSize: 24 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . holds . toString ( ) ,
style: const TextStyle ( fontSize: 24 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " T-spins total: " , style: TextStyle ( fontSize: 24 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . tSpins . toString ( ) ,
style: const TextStyle ( fontSize: 24 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - T-spin zero: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . clears . tSpinZeros . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - T-spin singles: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . clears . tSpinSingles . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - T-spin doubles: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . clears . tSpinDoubles . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - T-spin triples: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . clears . tSpinTriples . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - T-spin mini zero: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . clears . tSpinMiniZeros . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - T-spin mini singles: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . clears . tSpinMiniSingles . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - T-spin mini doubles: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . clears . tSpinMiniDoubles . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " Line clears: " , style: TextStyle ( fontSize: 24 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . lines . toString ( ) ,
style: const TextStyle ( fontSize: 24 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - Singles: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . clears . singles . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - Doubles: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . clears . doubles . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - Triples: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . clears . triples . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
const Text ( " - Quads: " , style: TextStyle ( fontSize: 18 ) ) ,
Text (
snapshot . data ! . blitz [ 0 ] ! . endContext ! . clears . quads . toString ( ) ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
] ,
) ,
) ,
) ,
]
: [
Text ( " That user never played Blitz " ,
style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) )
] ,
) ;
} ) ,
ListView . builder (
physics: const ClampingScrollPhysics ( ) ,
itemCount: 1 ,
itemBuilder: ( BuildContext context , int index ) {
return Column (
children: [
Text ( " Other info " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
if ( snapshot . data ! . zen ! = null )
Padding (
padding: const EdgeInsets . fromLTRB ( 0 , 48 , 0 , 48 ) ,
child: Column (
children: [
Text ( " Zen " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
Text ( " Level ${ snapshot . data ! . zen ! . level } " ,
style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
Text (
" Score ${ snapshot . data ! . zen ! . score } " ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
) ,
if ( snapshot . data ! . bio ! = null )
Padding (
padding: const EdgeInsets . fromLTRB ( 0 , 0 , 0 , 48 ) ,
child: Column (
children: [
Text ( " Bio " , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
Text (
snapshot . data ! . bio ! ,
style: const TextStyle ( fontSize: 18 ) ,
) ,
] ,
) ,
) ,
2023-06-03 19:10:28 +00:00
] ,
2023-06-04 15:10:20 +00:00
) ;
} )
2023-06-03 14:56:08 +00:00
] ,
) ,
2023-05-29 19:10:14 +00:00
) ;
} else if ( snapshot . hasError ) {
2023-06-06 21:04:49 +00:00
return Center (
child: Text ( ' ${ snapshot . error } ' , style: const TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: 42 ) , textAlign: TextAlign . center ) ) ;
2023-05-29 19:10:14 +00:00
}
2023-06-01 21:30:38 +00:00
return const Center (
child: CircularProgressIndicator (
color: Colors . white ,
) ) ;
2023-05-29 19:10:14 +00:00
} ,
) ,
) ,
2023-05-25 19:21:56 +00:00
) ;
}
}
2023-05-07 17:58:01 +00:00
2023-05-25 19:21:56 +00:00
class NavDrawer extends StatelessWidget {
2023-06-06 21:04:49 +00:00
final Function changePlayer ;
const NavDrawer ( this . changePlayer , { super . key } ) ;
2023-06-05 22:21:29 +00:00
2023-05-25 19:21:56 +00:00
@ override
Widget build ( BuildContext context ) {
return Drawer (
child: ListView (
padding: EdgeInsets . zero ,
children: < Widget > [
const DrawerHeader (
2023-05-29 19:10:14 +00:00
child: Text (
2023-06-04 15:10:20 +00:00
' Players you track ' ,
2023-05-29 19:10:14 +00:00
style: TextStyle ( color: Colors . white , fontSize: 25 ) ,
) ) ,
2023-05-25 19:21:56 +00:00
ListTile (
2023-06-06 21:04:49 +00:00
leading: const Icon ( Icons . home ) ,
title: Text ( prefs . getString ( " player " ) ? ? " dan63047 " ) ,
2023-06-04 15:10:20 +00:00
onTap: ( ) {
2023-06-06 21:04:49 +00:00
developer . log ( " Navigator changed player " , name: " main_view " ) ;
changePlayer ( prefs . getString ( " player " ) ? ? " dan63047 " ) ;
2023-06-04 15:10:20 +00:00
Navigator . of ( context ) . pop ( ) ;
} ,
2023-05-07 17:58:01 +00:00
) ,
] ,
2023-05-06 21:14:12 +00:00
) ,
) ;
}
2023-05-11 16:08:42 +00:00
}
2023-05-29 19:10:14 +00:00
2023-06-03 14:56:08 +00:00
class _StatCellNum extends StatelessWidget {
2023-06-04 15:10:20 +00:00
const _StatCellNum ( { required this . playerStat , required this . playerStatLabel , required this . isScreenBig , this . snackBar , this . fractionDigits } ) ;
2023-06-03 14:56:08 +00:00
final num playerStat ;
final String playerStatLabel ;
final bool isScreenBig ;
final String ? snackBar ;
final int ? fractionDigits ;
@ override
Widget build ( BuildContext context ) {
return Column (
children: [
Text (
2023-06-04 15:10:20 +00:00
fractionDigits ! = null ? playerStat . toStringAsFixed ( fractionDigits ! ) : playerStat . floor ( ) . toString ( ) ,
2023-06-03 14:56:08 +00:00
style: TextStyle (
fontFamily: " Eurostile Round Extended " ,
fontSize: isScreenBig ? 32 : 24 ,
) ,
) ,
snackBar = = null
? Text (
playerStatLabel ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: " Eurostile Round " ,
fontSize: 16 ,
) ,
)
: TextButton (
onPressed: ( ) {
2023-06-04 15:10:20 +00:00
ScaffoldMessenger . of ( context ) . showSnackBar ( SnackBar ( content: Text ( snackBar ! ) ) ) ;
2023-06-03 14:56:08 +00:00
} ,
2023-06-04 15:10:20 +00:00
style: ButtonStyle ( padding: MaterialStateProperty . all ( EdgeInsets . zero ) ) ,
2023-06-03 14:56:08 +00:00
child: Text (
playerStatLabel ,
textAlign: TextAlign . center ,
style: const TextStyle (
fontFamily: " Eurostile Round " ,
fontSize: 16 ,
) ,
) ) ,
] ,
) ;
}
}
2023-05-29 19:10:14 +00:00
class _UserThingy extends StatelessWidget {
final TetrioPlayer player ;
2023-06-06 21:04:49 +00:00
const _UserThingy ( { Key ? key , required this . player } ) : super ( key: key ) ;
2023-05-29 19:10:14 +00:00
@ override
Widget build ( BuildContext context ) {
return LayoutBuilder ( builder: ( context , constraints ) {
2023-06-01 21:30:38 +00:00
bool bigScreen = constraints . maxWidth > 768 ;
double bannerHeight = bigScreen ? 240 : 120 ;
double pfpHeight = bigScreen ? 64 : 32 ;
2023-05-29 19:10:14 +00:00
return Column (
children: [
Flex (
direction: Axis . vertical ,
mainAxisSize: MainAxisSize . min ,
mainAxisAlignment: MainAxisAlignment . center ,
children: [
2023-05-30 20:37:10 +00:00
Stack (
alignment: Alignment . topCenter ,
children: [
if ( player . bannerRevision ! = null )
Image . network (
" https://tetr.io/user-content/banners/ ${ player . userId } .jpg?rv= ${ player . bannerRevision } " ,
fit: BoxFit . cover ,
2023-06-01 21:30:38 +00:00
height: bannerHeight ,
2023-06-06 21:04:49 +00:00
errorBuilder: ( context , error , stackTrace ) {
developer . log ( " Error with building banner image " , name: " main_view " , error: error , stackTrace: stackTrace ) ;
return const Placeholder (
color: Colors . black ,
) ;
} ,
2023-05-30 20:37:10 +00:00
) ,
Container (
2023-06-04 15:10:20 +00:00
padding: EdgeInsets . fromLTRB ( 0 , player . bannerRevision ! = null ? bannerHeight / 1.4 : pfpHeight , 0 , 0 ) ,
2023-05-30 20:37:10 +00:00
child: ClipRRect (
borderRadius: BorderRadius . circular ( 1000 ) ,
2023-06-01 21:30:38 +00:00
child: player . role = = " banned "
? Image . asset (
" res/avatars/tetrio_banned.png " ,
fit: BoxFit . fitHeight ,
height: 128 ,
)
2023-06-06 21:04:49 +00:00
: player . avatarRevision ! = null
? Image . network ( " https://tetr.io/user-content/avatars/ ${ player . userId } .jpg?rv= ${ player . avatarRevision } " ,
fit: BoxFit . fitHeight , height: 128 , errorBuilder: ( context , error , stackTrace ) {
developer . log ( " Error with building profile picture " , name: " main_view " , error: error , stackTrace: stackTrace ) ;
return Image . asset (
" res/avatars/tetrio_anon.png " ,
fit: BoxFit . fitHeight ,
height: 128 ,
) ;
} )
: Image . asset (
" res/avatars/tetrio_anon.png " ,
fit: BoxFit . fitHeight ,
height: 128 ,
) ,
2023-05-30 20:37:10 +00:00
) ,
) ,
] ,
2023-05-29 19:10:14 +00:00
) ,
Flexible (
child: Column (
children: [
2023-06-04 15:10:20 +00:00
Text ( player . username , style: TextStyle ( fontFamily: " Eurostile Round Extended " , fontSize: bigScreen ? 42 : 28 ) ) ,
2023-06-06 21:04:49 +00:00
TextButton (
child: Text ( player . userId , style: const TextStyle ( fontFamily: " Eurostile Round Condensed " , fontSize: 14 ) ) ,
onPressed: ( ) {
copyToClipboard ( player . userId ) ;
ScaffoldMessenger . of ( context ) . showSnackBar ( const SnackBar ( content: Text ( " Copied to clipboard! " ) ) ) ;
} ) ,
2023-05-29 19:10:14 +00:00
] ,
) ,
) ,
] ,
) ,
2023-05-30 20:37:10 +00:00
( player . role ! = " banned " )
? Wrap (
direction: Axis . horizontal ,
alignment: WrapAlignment . center ,
spacing: 25 ,
crossAxisAlignment: WrapCrossAlignment . start ,
clipBehavior: Clip . hardEdge , // hard WHAT???
children: [
_StatCellNum (
playerStat: player . level ,
2023-06-01 21:30:38 +00:00
playerStatLabel: " XP Level " ,
isScreenBig: bigScreen ,
2023-06-04 15:10:20 +00:00
snackBar: " ${ player . xp . floor ( ) . toString ( ) } XP, ${ ( ( player . level - player . level . floor ( ) ) * 100 ) . toStringAsFixed ( 2 ) } % until next level " ,
2023-05-30 20:37:10 +00:00
) ,
if ( player . gameTime > = Duration . zero )
_StatCellNum (
2023-06-03 14:56:08 +00:00
playerStat: player . gameTime . inHours ,
2023-05-30 20:37:10 +00:00
playerStatLabel: " Hours \n Played " ,
2023-06-01 21:30:38 +00:00
isScreenBig: bigScreen ,
2023-05-30 20:37:10 +00:00
snackBar: player . gameTime . toString ( ) ,
) ,
2023-06-04 15:10:20 +00:00
if ( player . gamesPlayed > = 0 ) _StatCellNum ( playerStat: player . gamesPlayed , isScreenBig: bigScreen , playerStatLabel: " Online \n Games " ) ,
if ( player . gamesWon > = 0 ) _StatCellNum ( playerStat: player . gamesWon , isScreenBig: bigScreen , playerStatLabel: " Games \n Won " ) ,
if ( player . friendCount > 0 ) _StatCellNum ( playerStat: player . friendCount , isScreenBig: bigScreen , playerStatLabel: " Friends " ) ,
2023-05-30 20:37:10 +00:00
] ,
)
: Text (
" BANNED " ,
textAlign: TextAlign . center ,
2023-06-01 21:30:38 +00:00
style: TextStyle (
2023-05-30 20:37:10 +00:00
fontFamily: " Eurostile Round Extended " ,
fontWeight: FontWeight . w900 ,
color: Colors . red ,
2023-06-01 21:30:38 +00:00
fontSize: bigScreen ? 60 : 45 ,
2023-05-30 20:37:10 +00:00
) ,
) ,
2023-06-03 14:56:08 +00:00
if ( player . badstanding ! = null & & player . badstanding ! )
Text (
" BAD STANDING " ,
textAlign: TextAlign . center ,
style: TextStyle (
fontFamily: " Eurostile Round Extended " ,
fontWeight: FontWeight . w900 ,
color: Colors . red ,
fontSize: bigScreen ? 60 : 45 ,
) ,
) ,
2023-06-01 21:30:38 +00:00
Row (
mainAxisAlignment: MainAxisAlignment . center ,
children: [
2023-06-01 22:01:18 +00:00
Expanded (
child: Text (
2023-06-03 14:56:08 +00:00
" ${ player . country ! = null ? " $ {player.country?.toUpperCase() } • " : " " } $ { player . role . capitalize ( ) } account $ { player . registrationTime = = null ? " that was from very beginning " : ' created ${ player . registrationTime } ' } • $ { player . supporterTier = = 0 ? " Not a supporter " : " Supporter tier ${ player . supporterTier } " } " ,
2023-06-01 22:01:18 +00:00
textAlign: TextAlign . center ,
2023-06-01 21:30:38 +00:00
style: const TextStyle (
fontFamily: " Eurostile Round " ,
fontSize: 16 ,
) ) ,
2023-06-01 22:01:18 +00:00
)
2023-06-01 21:30:38 +00:00
] ,
) ,
2023-05-29 19:10:14 +00:00
Wrap (
direction: Axis . horizontal ,
alignment: WrapAlignment . center ,
spacing: 25 ,
crossAxisAlignment: WrapCrossAlignment . start ,
2023-05-30 20:37:10 +00:00
clipBehavior: Clip . hardEdge ,
2023-05-29 19:10:14 +00:00
children: [
2023-06-01 21:30:38 +00:00
for ( var badge in player . badges )
IconButton (
onPressed: ( ) = > showDialog < void > (
context: context ,
builder: ( BuildContext context ) {
return AlertDialog (
title: Text (
badge . label ,
2023-06-04 15:10:20 +00:00
style: const TextStyle ( fontFamily: " Eurostile Round Extended " ) ,
2023-06-01 21:30:38 +00:00
) ,
content: SingleChildScrollView (
child: ListBody (
children: [
Wrap (
direction: Axis . horizontal ,
alignment: WrapAlignment . center ,
2023-06-04 15:10:20 +00:00
crossAxisAlignment: WrapCrossAlignment . center ,
2023-06-01 21:30:38 +00:00
spacing: 25 ,
children: [
2023-06-04 15:10:20 +00:00
Image . asset ( " res/tetrio_badges/ ${ badge . badgeId } .png " ) ,
Text ( badge . ts ! = null ? " Obtained ${ badge . ts } " : " That badge was assigned manualy by TETR.IO admins " ) ,
2023-06-01 21:30:38 +00:00
] ,
)
] ,
) ,
) ,
actions: < Widget > [
TextButton (
child: const Text ( ' OK ' ) ,
onPressed: ( ) {
Navigator . of ( context ) . pop ( ) ;
} ,
) ,
] ,
) ;
} ,
) ,
tooltip: badge . label ,
icon: Image . asset (
" res/tetrio_badges/ ${ badge . badgeId } .png " ,
height: 64 ,
width: 64 ,
2023-06-06 21:04:49 +00:00
errorBuilder: ( context , error , stackTrace ) {
developer . log ( " Error with building $ badge " , name: " main_view " , error: error , stackTrace: stackTrace ) ;
return Image . asset ( " res/icons/kagari.png " , height: 64 , width: 64 ) ;
} ,
2023-06-01 21:30:38 +00:00
) )
2023-05-29 19:10:14 +00:00
] ,
2023-05-30 20:37:10 +00:00
) ,
2023-05-29 19:10:14 +00:00
] ,
) ;
} ) ;
}
}