Smooth graph + unfinished reimplementation of tl progress bar

Also small UI changes
This commit is contained in:
dan63047 2024-04-21 01:37:31 +03:00
parent 3b16822c1f
commit 5c6c502a57
12 changed files with 768 additions and 554 deletions

View File

@ -17,6 +17,9 @@ const double appdspWeight = 140;
const double vsapmWeight = 60; const double vsapmWeight = 60;
const double cheeseWeight = 1.25; const double cheeseWeight = 1.25;
const double gbeWeight = 315; const double gbeWeight = 315;
const List<String> ranks = [
"d", "d+", "c-", "c", "c+", "b-", "b", "b+", "a-", "a", "a+", "s-", "s", "s+", "ss", "u", "x"
];
const Map<String, double> rankCutoffs = { const Map<String, double> rankCutoffs = {
"x": 0.01, "x": 0.01,
"u": 0.05, "u": 0.05,

View File

@ -4,9 +4,9 @@
/// To regenerate, run: `dart run slang` /// To regenerate, run: `dart run slang`
/// ///
/// Locales: 2 /// Locales: 2
/// Strings: 1120 (560 per locale) /// Strings: 1122 (561 per locale)
/// ///
/// Built on 2024-03-24 at 14:28 UTC /// Built on 2024-04-11 at 22:23 UTC
// coverage:ignore-file // coverage:ignore-file
// ignore_for_file: type=lint // ignore_for_file: type=lint
@ -216,6 +216,7 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
String verdictGeneral({required Object n, required Object verdict, required Object rank}) => '${n} ${verdict} than ${rank} rank average'; String verdictGeneral({required Object n, required Object verdict, required Object rank}) => '${n} ${verdict} than ${rank} rank average';
String get verdictBetter => 'better'; String get verdictBetter => 'better';
String get verdictWorse => 'worse'; String get verdictWorse => 'worse';
String get smooth => 'Smooth';
String gamesUntilRanked({required Object left}) => '${left} games until being ranked'; String gamesUntilRanked({required Object left}) => '${left} games until being ranked';
String get nerdStats => 'Nerd Stats'; String get nerdStats => 'Nerd Stats';
String get playersYouTrack => 'Players you track'; String get playersYouTrack => 'Players you track';
@ -870,6 +871,7 @@ class _StringsRu implements Translations {
@override String verdictGeneral({required Object verdict, required Object rank, required Object n}) => '${verdict} среднего ${rank} ранга на ${n}'; @override String verdictGeneral({required Object verdict, required Object rank, required Object n}) => '${verdict} среднего ${rank} ранга на ${n}';
@override String get verdictBetter => 'Лучше'; @override String get verdictBetter => 'Лучше';
@override String get verdictWorse => 'Хуже'; @override String get verdictWorse => 'Хуже';
@override String get smooth => 'Гладкий';
@override String gamesUntilRanked({required Object left}) => '${left} матчей до получения рейтинга'; @override String gamesUntilRanked({required Object left}) => '${left} матчей до получения рейтинга';
@override String get nerdStats => 'Для задротов'; @override String get nerdStats => 'Для задротов';
@override String get playersYouTrack => 'Отслеживаемые игроки'; @override String get playersYouTrack => 'Отслеживаемые игроки';
@ -1516,6 +1518,7 @@ extension on Translations {
case 'verdictGeneral': return ({required Object n, required Object verdict, required Object rank}) => '${n} ${verdict} than ${rank} rank average'; case 'verdictGeneral': return ({required Object n, required Object verdict, required Object rank}) => '${n} ${verdict} than ${rank} rank average';
case 'verdictBetter': return 'better'; case 'verdictBetter': return 'better';
case 'verdictWorse': return 'worse'; case 'verdictWorse': return 'worse';
case 'smooth': return 'Smooth';
case 'gamesUntilRanked': return ({required Object left}) => '${left} games until being ranked'; case 'gamesUntilRanked': return ({required Object left}) => '${left} games until being ranked';
case 'nerdStats': return 'Nerd Stats'; case 'nerdStats': return 'Nerd Stats';
case 'playersYouTrack': return 'Players you track'; case 'playersYouTrack': return 'Players you track';
@ -2096,6 +2099,7 @@ extension on _StringsRu {
case 'verdictGeneral': return ({required Object verdict, required Object rank, required Object n}) => '${verdict} среднего ${rank} ранга на ${n}'; case 'verdictGeneral': return ({required Object verdict, required Object rank, required Object n}) => '${verdict} среднего ${rank} ранга на ${n}';
case 'verdictBetter': return 'Лучше'; case 'verdictBetter': return 'Лучше';
case 'verdictWorse': return 'Хуже'; case 'verdictWorse': return 'Хуже';
case 'smooth': return 'Гладкий';
case 'gamesUntilRanked': return ({required Object left}) => '${left} матчей до получения рейтинга'; case 'gamesUntilRanked': return ({required Object left}) => '${left} матчей до получения рейтинга';
case 'nerdStats': return 'Для задротов'; case 'nerdStats': return 'Для задротов';
case 'playersYouTrack': return 'Отслеживаемые игроки'; case 'playersYouTrack': return 'Отслеживаемые игроки';

View File

@ -820,6 +820,7 @@ class CompareThingy extends StatelessWidget {
colors: const [Colors.green, Colors.transparent], colors: const [Colors.green, Colors.transparent],
begin: Alignment.centerLeft, begin: Alignment.centerLeft,
end: Alignment.centerRight, end: Alignment.centerRight,
transform: GradientRotation(0.6),
stops: [ stops: [
0.0, 0.0,
higherIsBetter higherIsBetter
@ -830,7 +831,8 @@ class CompareThingy extends StatelessWidget {
? 0.6 ? 0.6
: 0 : 0
], ],
)), )
),
child: Text( child: Text(
(prefix ?? "") + f.format(greenSide) + (postfix ?? ""), (prefix ?? "") + f.format(greenSide) + (postfix ?? ""),
style: const TextStyle( style: const TextStyle(
@ -838,7 +840,12 @@ class CompareThingy extends StatelessWidget {
shadows: <Shadow>[ shadows: <Shadow>[
Shadow( Shadow(
offset: Offset(0.0, 0.0), offset: Offset(0.0, 0.0),
blurRadius: 3.0, blurRadius: 1.0,
color: Colors.black,
),
Shadow(
offset: Offset(0.0, 0.0),
blurRadius: 2.0,
color: Colors.black, color: Colors.black,
), ),
Shadow( Shadow(
@ -874,6 +881,7 @@ class CompareThingy extends StatelessWidget {
colors: const [Colors.red, Colors.transparent], colors: const [Colors.red, Colors.transparent],
begin: Alignment.centerRight, begin: Alignment.centerRight,
end: Alignment.centerLeft, end: Alignment.centerLeft,
transform: GradientRotation(-0.6),
stops: [ stops: [
0.0, 0.0,
higherIsBetter higherIsBetter

View File

@ -1,5 +1,6 @@
// ignore_for_file: type_literal_in_constant_pattern // ignore_for_file: type_literal_in_constant_pattern
import 'dart:ffi';
import 'dart:io'; import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
@ -35,6 +36,7 @@ import 'package:go_router/go_router.dart';
final TetrioService teto = TetrioService(); // thing, that manadge our local DB final TetrioService teto = TetrioService(); // thing, that manadge our local DB
int _chartsIndex = 0; int _chartsIndex = 0;
bool _gamesPlayedInsteadOfDateAndTime = false; bool _gamesPlayedInsteadOfDateAndTime = false;
bool _smooth = false;
List _historyShortTitles = ["TR", "Glicko", "RD", "APM", "PPS", "VS", "APP", "DS/S", "DS/P", "APP + DS/P", "VS/APM", "Cheese", "GbE", "wAPP", "Area", "eTR", "±eTR", "Opener", "Plonk", "Inf. DS", "Stride"]; List _historyShortTitles = ["TR", "Glicko", "RD", "APM", "PPS", "VS", "APP", "DS/S", "DS/P", "APP + DS/P", "VS/APM", "Cheese", "GbE", "wAPP", "Area", "eTR", "±eTR", "Opener", "Plonk", "Inf. DS", "Stride"];
late ScrollController _scrollController; late ScrollController _scrollController;
final NumberFormat _timeInSec = NumberFormat("#,###.###s.", LocaleSettings.currentLocale.languageCode); final NumberFormat _timeInSec = NumberFormat("#,###.###s.", LocaleSettings.currentLocale.languageCode);
@ -81,11 +83,15 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
TetrioPlayersLeaderboard? everyone; TetrioPlayersLeaderboard? everyone;
PlayerLeaderboardPosition? meAmongEveryone; PlayerLeaderboardPosition? meAmongEveryone;
TetraLeagueAlpha? rankAverages; TetraLeagueAlpha? rankAverages;
double? thatRankCutoff;
double? nextRankCutoff;
String _searchFor = "6098518e3d5155e6ec429cdc"; // who we looking for String _searchFor = "6098518e3d5155e6ec429cdc"; // who we looking for
String _titleNickname = "dan63047"; String _titleNickname = "";
/// Each dropdown menu item contains list of dots for the graph /// Each dropdown menu item contains list of dots for the graph
var chartsData = <DropdownMenuItem<List<FlSpot>>>[]; List<DropdownMenuItem<List<FlSpot>>> chartsData = [];
var chartsDataGamesPlayed = <DropdownMenuItem<List<FlSpot>>>[]; List<DropdownMenuItem<List<FlSpot>>>? smoothChartsData;
List<DropdownMenuItem<List<FlSpot>>> chartsDataGamesPlayed = [];
List<DropdownMenuItem<List<FlSpot>>>? smoothChartsDataGamesPlayed;
//var tableData = <TableRow>[]; //var tableData = <TableRow>[];
final bodyGlobalKey = GlobalKey(); final bodyGlobalKey = GlobalKey();
bool _showSearchBar = false; bool _showSearchBar = false;
@ -108,7 +114,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
changePlayer(widget.player!); // it's gonna be user input changePlayer(widget.player!); // it's gonna be user input
}else{ }else{
_getPreferences() // otherwise, checking for preferences _getPreferences() // otherwise, checking for preferences
.then((value) => changePlayer(prefs.getString("player") ?? "dan63047")); // no preferences - loading me .then((value) => changePlayer(prefs.getString("player") ?? "6098518e3d5155e6ec429cdc")); // no preferences - loading me
} }
super.initState(); super.initState();
} }
@ -185,6 +191,11 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
everyone ??= await teto.fetchTLLeaderboard(); everyone ??= await teto.fetchTLLeaderboard();
meAmongEveryone = await compute(everyone!.getLeaderboardPosition, me); meAmongEveryone = await compute(everyone!.getLeaderboardPosition, me);
if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!); if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!);
if (me.tlSeason1.rank != "z") {
thatRankCutoff = everyone!.cutoffs[me.tlSeason1.rank];
nextRankCutoff = everyone!.cutoffs[ranks.indexOf(me.tlSeason1.rank)+1];
nextRankCutoff = nextRankCutoff??25000;
}
} }
if (everyone != null && me.tlSeason1.gamesPlayed > 9) rankAverages = everyone?.averages[me.tlSeason1.percentileRank]?[0]; if (everyone != null && me.tlSeason1.gamesPlayed > 9) rankAverages = everyone?.averages[me.tlSeason1.percentileRank]?[0];
@ -307,6 +318,42 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) FlSpot(tl.gamesPlayed.toDouble(), tl.playstyle!.infds)], child: const Text("Inf. DS")), DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) FlSpot(tl.gamesPlayed.toDouble(), tl.playstyle!.infds)], child: const Text("Inf. DS")),
DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) FlSpot(tl.gamesPlayed.toDouble(), tl.playstyle!.stride)], child: const Text("Stride")), DropdownMenuItem(value: [for (var tl in uniqueTL) if (tl.playstyle != null) FlSpot(tl.gamesPlayed.toDouble(), tl.playstyle!.stride)], child: const Text("Stride")),
]; ];
if (chartsData[0].value!.length > 200){
smoothChartsData = [];
smoothChartsDataGamesPlayed = [];
for (var chart in chartsData) {
int valuesPerDot = (chart.value!.length / 200).floor();
int lastDotEntries = chart.value!.length - (valuesPerDot * 199);
List<FlSpot> spots = [];
for (int i=0; i < 200; i++){
double avgX = 0, avgY = 0;
for (int k = i * valuesPerDot; k < (i == 199 ? chart.value!.length : i * valuesPerDot + valuesPerDot); k++) {
avgX += chart.value![k].x;
avgY += chart.value![k].y;
}
avgX /= i == 199 ? lastDotEntries : valuesPerDot;
avgY /= i == 199 ? lastDotEntries : valuesPerDot;
spots.add(FlSpot(avgX, avgY));
}
smoothChartsData!.add(DropdownMenuItem(value: spots, child: chart.child));
}
for (var chart in chartsDataGamesPlayed) {
int valuesPerDot = (chart.value!.length / 200).floor();
int lastDotEntries = chart.value!.length - (valuesPerDot * 199);
List<FlSpot> spots = [];
for (int i=0; i < 200; i++){
double avgX = 0, avgY = 0;
for (int k = i * valuesPerDot; k < (i == 199 ? chart.value!.length : i * valuesPerDot + valuesPerDot); k++) {
avgX += chart.value![k].x;
avgY += chart.value![k].y;
}
avgX /= i == 199 ? lastDotEntries : valuesPerDot;
avgY /= i == 199 ? lastDotEntries : valuesPerDot;
spots.add(FlSpot(avgX, avgY));
}
smoothChartsDataGamesPlayed!.add(DropdownMenuItem(value: spots, child: chart.child));
}
}
}else{ }else{
compareWith = null; compareWith = null;
chartsData = []; chartsData = [];
@ -465,6 +512,10 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
topTR: snapshot.data![7], topTR: snapshot.data![7],
bot: snapshot.data![0].role == "bot", bot: snapshot.data![0].role == "bot",
guest: snapshot.data![0].role == "anon", guest: snapshot.data![0].role == "anon",
thatRankCutoff: thatRankCutoff,
thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null,
nextRankCutoff: nextRankCutoff,
nextRankTarget: snapshot.data![0].tlSeason1.rank != "z" || snapshot.data![0].tlSeason1.rank != "x" ? rankTargets[ranks.indexOf(snapshot.data![0].tlSeason1.rank)+1] : null,
averages: rankAverages, averages: rankAverages,
lbPositions: meAmongEveryone lbPositions: meAmongEveryone
), ),
@ -474,7 +525,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
child: _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, oldMathcesHere: _TLHistoryWasFetched, separateScrollController: true,) child: _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, oldMathcesHere: _TLHistoryWasFetched, separateScrollController: true,)
), ),
],), ],),
_History(chartsData: chartsData, chartsDataGamesPlayed: chartsDataGamesPlayed, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0), _History(chartsData: chartsData, chartsDataGamesPlayed: chartsDataGamesPlayed, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, smoothChartsData: smoothChartsData, smoothChartsDataGamesPlayed: smoothChartsDataGamesPlayed),
_TwoRecordsThingy(sprint: snapshot.data![1]['sprint'], blitz: snapshot.data![1]['blitz'], rank: snapshot.data![0].tlSeason1.percentileRank,), _TwoRecordsThingy(sprint: snapshot.data![1]['sprint'], blitz: snapshot.data![1]['blitz'], rank: snapshot.data![0].tlSeason1.percentileRank,),
_OtherThingy(zen: snapshot.data![1]['zen'], bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![6],) _OtherThingy(zen: snapshot.data![1]['zen'], bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![6],)
] : [ ] : [
@ -489,7 +540,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
lbPositions: meAmongEveryone lbPositions: meAmongEveryone
), ),
_TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, oldMathcesHere: _TLHistoryWasFetched), _TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3], wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, oldMathcesHere: _TLHistoryWasFetched),
_History(chartsData: chartsData, chartsDataGamesPlayed: chartsDataGamesPlayed, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0), _History(chartsData: chartsData, chartsDataGamesPlayed: chartsDataGamesPlayed, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![0].tlSeason1.gamesPlayed > 0, smoothChartsData: smoothChartsData, smoothChartsDataGamesPlayed: smoothChartsDataGamesPlayed),
_RecordThingy(record: snapshot.data![1]['sprint'], rank: snapshot.data![0].tlSeason1.percentileRank), _RecordThingy(record: snapshot.data![1]['sprint'], rank: snapshot.data![0].tlSeason1.percentileRank),
_RecordThingy(record: snapshot.data![1]['blitz'], rank: snapshot.data![0].tlSeason1.percentileRank), _RecordThingy(record: snapshot.data![1]['blitz'], rank: snapshot.data![0].tlSeason1.percentileRank),
_OtherThingy(zen: snapshot.data![1]['zen'], bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![6],) _OtherThingy(zen: snapshot.data![1]['zen'], bio: snapshot.data![0].bio, distinguishment: snapshot.data![0].distinguishment, newsletter: snapshot.data![6],)
@ -592,7 +643,7 @@ class _NavDrawerState extends State<NavDrawer> {
homePlayerNickname = id; homePlayerNickname = id;
} }
} else { } else {
homePlayerNickname = "dan63047"; homePlayerNickname = "dan63";
} }
setState(() {}); setState(() {});
} }
@ -624,7 +675,7 @@ class _NavDrawerState extends State<NavDrawer> {
leading: const Icon(Icons.home), leading: const Icon(Icons.home),
title: Text(homePlayerNickname), title: Text(homePlayerNickname),
onTap: () { onTap: () {
widget.changePlayer(prefs.getString("player") ?? "dan63047"); // changes player on main view to the one from preferences widget.changePlayer(prefs.getString("player") ?? "6098518e3d5155e6ec429cdc"); // changes player on main view to the one from preferences
Navigator.of(context).pop(); // and then NavDrawer closes itself. Navigator.of(context).pop(); // and then NavDrawer closes itself.
}, },
), ),
@ -753,7 +804,9 @@ class _TLRecords extends StatelessWidget {
class _History extends StatelessWidget{ class _History extends StatelessWidget{
final List<DropdownMenuItem<List<FlSpot>>> chartsData; final List<DropdownMenuItem<List<FlSpot>>> chartsData;
final List<DropdownMenuItem<List<FlSpot>>>? smoothChartsData;
final List<DropdownMenuItem<List<FlSpot>>> chartsDataGamesPlayed; final List<DropdownMenuItem<List<FlSpot>>> chartsDataGamesPlayed;
final List<DropdownMenuItem<List<FlSpot>>>? smoothChartsDataGamesPlayed;
final String userID; final String userID;
final Function update; final Function update;
final Function changePlayer; final Function changePlayer;
@ -761,7 +814,7 @@ class _History extends StatelessWidget{
/// Widget, that can show history of some stat of the player on the graph. /// Widget, that can show history of some stat of the player on the graph.
/// Requires player [states], which is list of states and function [update], which rebuild widgets /// Requires player [states], which is list of states and function [update], which rebuild widgets
const _History({required this.chartsData, required this.chartsDataGamesPlayed, required this.userID, required this.changePlayer, required this.update, required this.wasActiveInTL}); const _History({required this.chartsData, required this.chartsDataGamesPlayed, required this.userID, required this.changePlayer, required this.update, required this.wasActiveInTL, this.smoothChartsData, this.smoothChartsDataGamesPlayed});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -776,6 +829,8 @@ class _History extends StatelessWidget{
)); ));
} }
bool bigScreen = MediaQuery.of(context).size.width > 768; bool bigScreen = MediaQuery.of(context).size.width > 768;
var selectedGraph = _gamesPlayedInsteadOfDateAndTime ? chartsDataGamesPlayed[_chartsIndex].value! : chartsData[_chartsIndex].value!;
var smoothSelectedGraph = _gamesPlayedInsteadOfDateAndTime ? (smoothChartsDataGamesPlayed?[_chartsIndex].value) : (smoothChartsData?[_chartsIndex].value);
return SingleChildScrollView( return SingleChildScrollView(
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
child: SingleChildScrollView( child: SingleChildScrollView(
@ -786,6 +841,7 @@ class _History extends StatelessWidget{
children: [ children: [
Wrap( Wrap(
spacing: 20, spacing: 20,
crossAxisAlignment: WrapCrossAlignment.center,
children: [ children: [
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -815,9 +871,21 @@ class _History extends StatelessWidget{
), ),
], ],
), ),
if (smoothSelectedGraph != null) Row(
mainAxisSize: MainAxisSize.min,
children: [
Checkbox(value: _smooth,
checkColor: Colors.black,
onChanged: ((value) {
_smooth = value!;
update();
})),
Text(t.smooth, style: const TextStyle(color: Colors.white, fontSize: 22))
],
)
], ],
), ),
if(chartsData[_chartsIndex].value!.length > 1) _HistoryChartThigy(data: _gamesPlayedInsteadOfDateAndTime ? chartsDataGamesPlayed[_chartsIndex].value! : chartsData[_chartsIndex].value!, yAxisTitle: _historyShortTitles[_chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(), xFormat: NumberFormat.compact()) if(chartsData[_chartsIndex].value!.length > 1) _HistoryChartThigy(data: selectedGraph, smoothData: smoothSelectedGraph, smooth: _smooth, yAxisTitle: _historyShortTitles[_chartsIndex], bigScreen: bigScreen, leftSpace: bigScreen? 80 : 45, yFormat: bigScreen? f2 : NumberFormat.compact(), xFormat: NumberFormat.compact())
else if (chartsData[_chartsIndex].value!.length <= 1) Center(child: Column( else if (chartsData[_chartsIndex].value!.length <= 1) Center(child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@ -835,6 +903,8 @@ class _History extends StatelessWidget{
class _HistoryChartThigy extends StatefulWidget{ class _HistoryChartThigy extends StatefulWidget{
final List<FlSpot> data; final List<FlSpot> data;
final List<FlSpot>? smoothData;
final bool smooth;
final String yAxisTitle; final String yAxisTitle;
final bool bigScreen; final bool bigScreen;
final double leftSpace; final double leftSpace;
@ -844,7 +914,7 @@ class _HistoryChartThigy extends StatefulWidget{
/// Implements graph for the _History widget. Requires [data] which is a list of dots for the graph. [yAxisTitle] used to keep track of changes. /// Implements graph for the _History widget. Requires [data] which is a list of dots for the graph. [yAxisTitle] used to keep track of changes.
/// [bigScreen] tells if screen wide enough, [leftSpace] sets size, reserved for titles on the left from the graph and [yFormat] sets number format /// [bigScreen] tells if screen wide enough, [leftSpace] sets size, reserved for titles on the left from the graph and [yFormat] sets number format
/// for left titles /// for left titles
const _HistoryChartThigy({required this.data, required this.yAxisTitle, required this.bigScreen, required this.leftSpace, required this.yFormat, this.xFormat}); const _HistoryChartThigy({required this.data, this.smoothData, required this.smooth, required this.yAxisTitle, required this.bigScreen, required this.leftSpace, required this.yFormat, this.xFormat});
@override @override
State<_HistoryChartThigy> createState() => _HistoryChartThigyState(); State<_HistoryChartThigy> createState() => _HistoryChartThigyState();
@ -1050,8 +1120,26 @@ class _HistoryChartThigyState extends State<_HistoryChartThigy> {
children: [ children: [
LineChart( LineChart(
key: graphKey, key: graphKey,
curve: Curves.elasticInOut,
LineChartData( LineChartData(
lineBarsData: [LineChartBarData(spots: widget.data)], lineBarsData: [
LineChartBarData(
show: !_smooth,
spots: widget.data,
dotData: FlDotData(show: false),
isCurved: true,
curveSmoothness: 0.35,
preventCurveOverShooting: true
),
if (widget.smoothData != null) LineChartBarData(
show: _smooth,
spots: widget.smoothData!,
dotData: FlDotData(show: false),
isCurved: true,
curveSmoothness: 0.35,
preventCurveOverShooting: true,
)
],
clipData: const FlClipData.all(), clipData: const FlClipData.all(),
borderData: FlBorderData(show: false), borderData: FlBorderData(show: false),
gridData: FlGridData(verticalInterval: xInterval), gridData: FlGridData(verticalInterval: xInterval),
@ -1116,29 +1204,6 @@ class _HistoryChartThigyState extends State<_HistoryChartThigy> {
} }
} }
// class _HistoryTableThingy extends StatelessWidget{
// final List<TableRow> tableData;
// const _HistoryTableThingy(this.tableData);
// // :tf:
// @override
// Widget build(BuildContext context) {
// return LayoutBuilder(builder: (context, constraints){
// return Table(
// defaultColumnWidth: FixedColumnWidth(75),
// columnWidths: {
// 0: FixedColumnWidth(170),
// 1: FixedColumnWidth(100),
// 2: FixedColumnWidth(90),
// 18: FixedColumnWidth(100),
// 19: FixedColumnWidth(90),
// },
// children: tableData,
// );
// });
// }
// }
class _TwoRecordsThingy extends StatelessWidget { class _TwoRecordsThingy extends StatelessWidget {
final RecordSingle? sprint; final RecordSingle? sprint;
final RecordSingle? blitz; final RecordSingle? blitz;

View File

@ -15,6 +15,7 @@ var _chartsShortTitlesDropdowns = <DropdownMenuItem>[for (MapEntry e in chartsSh
Stats _chartsX = Stats.tr; Stats _chartsX = Stats.tr;
Stats _chartsY = Stats.apm; Stats _chartsY = Stats.apm;
List<DropdownMenuItem> _itemStats = [for (MapEntry e in chartsShortTitles.entries) DropdownMenuItem(value: e.key, child: Text(e.value))]; List<DropdownMenuItem> _itemStats = [for (MapEntry e in chartsShortTitles.entries) DropdownMenuItem(value: e.key, child: Text(e.value))];
List<_MyScatterSpot> _spots = [];
Stats _sortBy = Stats.tr; Stats _sortBy = Stats.tr;
late List<TetrioPlayerFromLeaderboard> they; late List<TetrioPlayerFromLeaderboard> they;
bool _reversed = false; bool _reversed = false;
@ -63,6 +64,7 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
super.initState(); super.initState();
previousAxisTitles = _chartsX.toString()+_chartsY.toString(); previousAxisTitles = _chartsX.toString()+_chartsY.toString();
they = TetrioPlayersLeaderboard("lol", []).getStatRanking(widget.rank[1]["entries"]!, _sortBy, reversed: _reversed, country: _country); they = TetrioPlayersLeaderboard("lol", []).getStatRanking(widget.rank[1]["entries"]!, _sortBy, reversed: _reversed, country: _country);
createSpots();
recalculateBoundaries(); recalculateBoundaries();
resetScale(); resetScale();
} }
@ -102,6 +104,19 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
}).getStatByEnum(_chartsY).toDouble(); }).getStatByEnum(_chartsY).toDouble();
} }
void createSpots(){
_spots = [
for (TetrioPlayerFromLeaderboard entry in widget.rank[1]["entries"])
if (entry.apm != 0.0 && entry.vs != 0.0) // prevents from ScatterChart "Offset argument contained a NaN value." exception
_MyScatterSpot(
entry.getStatByEnum(_chartsX).toDouble(),
entry.getStatByEnum(_chartsY).toDouble(),
entry.userId,
entry.username,
dotPainter: FlDotCirclePainter(color: rankColors[entry.rank]??Colors.white, radius: 3))
];
}
void resetScale(){ void resetScale(){
maxX = actualMaxX; maxX = actualMaxX;
minX = actualMinX; minX = actualMinX;
@ -161,6 +176,7 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
double graphStartX = padding.left; double graphStartX = padding.left;
double graphEndX = MediaQuery.sizeOf(context).width - padding.right; double graphEndX = MediaQuery.sizeOf(context).width - padding.right;
if (previousAxisTitles != _chartsX.toString()+_chartsY.toString()){ if (previousAxisTitles != _chartsX.toString()+_chartsY.toString()){
createSpots();
recalculateBoundaries(); recalculateBoundaries();
resetScale(); resetScale();
previousAxisTitles = _chartsX.toString()+_chartsY.toString(); previousAxisTitles = _chartsX.toString()+_chartsY.toString();
@ -325,16 +341,7 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
minY: minY, minY: minY,
maxY: maxY, maxY: maxY,
clipData: const FlClipData.all(), clipData: const FlClipData.all(),
scatterSpots: [ scatterSpots: _spots,
for (TetrioPlayerFromLeaderboard entry in widget.rank[1]["entries"])
if (entry.apm != 0.0 && entry.vs != 0.0) // prevents from ScatterChart "Offset argument contained a NaN value." exception
_MyScatterSpot(
entry.getStatByEnum(_chartsX).toDouble(),
entry.getStatByEnum(_chartsY).toDouble(),
entry.userId,
entry.username,
dotPainter: FlDotCirclePainter(color: rankColors[entry.rank]??Colors.white, radius: 3))
],
scatterTouchData: ScatterTouchData( scatterTouchData: ScatterTouchData(
handleBuiltInTouches: false, handleBuiltInTouches: false,
touchCallback:(touchEvent, touchResponse) { touchCallback:(touchEvent, touchResponse) {

View File

@ -64,7 +64,7 @@ class SettingsState extends State<SettingsView> {
defaultNickname = n; defaultNickname = n;
} }
} else { } else {
defaultNickname = "dan63047"; defaultNickname = "6098518e3d5155e6ec429cdc";
} }
setState(() {}); setState(() {});
} }
@ -76,7 +76,7 @@ class SettingsState extends State<SettingsView> {
Future<void> _removePlayer() async { Future<void> _removePlayer() async {
await prefs.remove('player'); await prefs.remove('player');
await _setDefaultNickname("dan63047"); await _setDefaultNickname("6098518e3d5155e6ec429cdc");
} }
@override @override

View File

@ -2,6 +2,8 @@
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:tetra_stats/data_objects/tetrio_multiplayer_replay.dart'; import 'package:tetra_stats/data_objects/tetrio_multiplayer_replay.dart';
import 'package:tetra_stats/services/crud_exceptions.dart'; import 'package:tetra_stats/services/crud_exceptions.dart';
import 'package:tetra_stats/views/compare_view.dart' show CompareThingy, CompareBoolThingy; import 'package:tetra_stats/views/compare_view.dart' show CompareThingy, CompareBoolThingy;
@ -62,8 +64,11 @@ class TlMatchResultState extends State<TlMatchResultView> {
super.dispose(); super.dispose();
} }
Widget buildComparison(bool bigScreen, bool showMobileSelector){ Widget buildComparison(double width, bool showMobileSelector){
return FutureBuilder(future: replayData, builder: (context, snapshot){ bool bigScreen = width >= 768;
return SizedBox(
width: width,
child: FutureBuilder(future: replayData, builder: (context, snapshot){
late Duration time; late Duration time;
late String readableTime; late String readableTime;
late String reason; late String reason;
@ -477,7 +482,8 @@ class TlMatchResultState extends State<TlMatchResultView> {
], ],
) )
); );
}); }),
);
} }
Widget buildRoundSelector(double width){ Widget buildRoundSelector(double width){
@ -684,21 +690,17 @@ class TlMatchResultState extends State<TlMatchResultView> {
return Center( return Center(
child: Container( child: Container(
constraints: const BoxConstraints(maxWidth: 768), constraints: const BoxConstraints(maxWidth: 768),
child: buildComparison(viewportWidth > 768, true) child: buildComparison(viewportWidth, true)
), ),
); );
} else { } else {
double comparisonWidth = viewportWidth - 450 - 16;
comparisonWidth = comparisonWidth > 768 ? 768 : comparisonWidth;
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
SizedBox( buildComparison(comparisonWidth, false),
width: 768, buildRoundSelector(450)
child: buildComparison(true, false)
),
Container(
constraints: const BoxConstraints(maxWidth: 768),
child: buildRoundSelector(max(viewportWidth-768-16, 200)),
)
], ],
); );
} }

View File

@ -20,6 +20,10 @@ class Graphs extends StatelessWidget{
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
double attack = apm / 60 * 0.4;
double speed = pps / 3.75;
double defense = nerdStats.dss * 1.15;
double cheese = nerdStats.cheese / 110;
return Wrap( return Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
@ -27,7 +31,7 @@ class Graphs extends StatelessWidget{
crossAxisAlignment: WrapCrossAlignment.start, crossAxisAlignment: WrapCrossAlignment.start,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
children: [ children: [
Padding( if (true) Padding( // vs graph
padding: const EdgeInsets.fromLTRB(18, 0, 18, 44), padding: const EdgeInsets.fromLTRB(18, 0, 18, 44),
child: SizedBox( child: SizedBox(
height: 310, height: 310,
@ -86,7 +90,7 @@ class Graphs extends StatelessWidget{
borderColor: Colors.transparent, borderColor: Colors.transparent,
dataEntries: [ dataEntries: [
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 180),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
@ -104,7 +108,60 @@ class Graphs extends StatelessWidget{
), ),
), ),
), ),
Padding( Padding( // sq graph
padding: const EdgeInsets.fromLTRB(18, 0, 18, 44),
child: SizedBox(
height: 310,
width: 310,
child: RadarChart(
RadarChartData(
radarShape: RadarShape.polygon,
tickCount: 4,
ticksTextStyle: const TextStyle(color: Colors.white24, fontSize: 10),
radarBorderData: const BorderSide(color: Colors.transparent, width: 1),
gridBorderData: const BorderSide(color: Colors.white24, width: 1),
tickBorderData: const BorderSide(color: Colors.transparent, width: 1),
titleTextStyle: const TextStyle(height: 1.1),
radarTouchData: RadarTouchData(),
getTitle: (index, angle) {
switch (index) {
case 0:
return RadarChartTitle(text: 'Attack\n${f2.format(apm)} APM', angle: 0, positionPercentageOffset: 0.05);
case 1:
return RadarChartTitle(text: 'Speed\n${f2.format(pps)} PPS', angle: 0, positionPercentageOffset: 0.05);
case 2:
return RadarChartTitle(text: 'Defense\n${f2.format(nerdStats.dss)} DS/S', angle: angle + 180, positionPercentageOffset: 0.05);
case 3:
return RadarChartTitle(text: 'Cheese\n${f3.format(nerdStats.cheese)}', angle: 0, positionPercentageOffset: 0.05);
default:
return const RadarChartTitle(text: '');
}
},
dataSets: [
RadarDataSet(
dataEntries: [
RadarEntry(value: attack),
RadarEntry(value: speed),
RadarEntry(value: defense),
RadarEntry(value: cheese),
],
),
RadarDataSet(
fillColor: Colors.transparent,
borderColor: Colors.transparent,
dataEntries: [
const RadarEntry(value: 0),
const RadarEntry(value: 1.2),
const RadarEntry(value: 0),
const RadarEntry(value: 0),
],
)
],
)
)
)
),
Padding( // psq graph
padding: const EdgeInsets.fromLTRB(18, 0, 18, 44), padding: const EdgeInsets.fromLTRB(18, 0, 18, 44),
child: SizedBox( child: SizedBox(
height: 310, height: 310,
@ -126,7 +183,7 @@ class Graphs extends StatelessWidget{
case 1: case 1:
return RadarChartTitle(text: 'Stride\n${percentage.format(playstyle.stride)}', angle: 0, positionPercentageOffset: 0.05); return RadarChartTitle(text: 'Stride\n${percentage.format(playstyle.stride)}', angle: 0, positionPercentageOffset: 0.05);
case 2: case 2:
return RadarChartTitle(text: 'Inf Ds\n${percentage.format(playstyle.infds)}', angle: angle + 180, positionPercentageOffset: 0.05); return RadarChartTitle(text: 'Inf DS\n${percentage.format(playstyle.infds)}', angle: angle + 180, positionPercentageOffset: 0.05);
case 3: case 3:
return RadarChartTitle(text: 'Plonk\n${percentage.format(playstyle.plonk)}', angle: 0, positionPercentageOffset: 0.05); return RadarChartTitle(text: 'Plonk\n${percentage.format(playstyle.plonk)}', angle: 0, positionPercentageOffset: 0.05);
default: default:
@ -147,19 +204,9 @@ class Graphs extends StatelessWidget{
borderColor: Colors.transparent, borderColor: Colors.transparent,
dataEntries: [ dataEntries: [
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 1),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0), const RadarEntry(value: 0),
const RadarEntry(value: 0),
],
),
RadarDataSet(
fillColor: Colors.transparent,
borderColor: Colors.transparent,
dataEntries: [
const RadarEntry(value: 1),
const RadarEntry(value: 1),
const RadarEntry(value: 1),
const RadarEntry(value: 1),
], ],
) )
], ],

View File

@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:syncfusion_flutter_gauges/gauges.dart';
import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart';
class TLProgress extends StatelessWidget{
final double tr;
final String rank;
final int position;
final String? nextRank;
final String? previousRank;
final int nextRankPosition;
final int previousRankPosition;
final double? nextRankTRcutoff;
final double? previousRankTRcutoff;
final double? nextRankTRcutoffTarget;
final double? previousRankTRcutoffTarget;
const TLProgress({super.key, required this.tr, required this.rank, required this.position, required this.nextRankPosition, required this.previousRankPosition, this.nextRank, this.previousRank, this.nextRankTRcutoff, this.previousRankTRcutoff, this.nextRankTRcutoffTarget, this.previousRankTRcutoffTarget});
double getBarPosition(){
return 1 - (position - nextRankPosition)/(previousRankPosition - nextRankPosition);
}
double? getBarTR(){
return null;
}
@override
Widget build(BuildContext context) {
print(getBarPosition());
// return Container(
// alignment: Alignment.centerLeft,
// height: 50,
// width: MediaQuery.of(context).size.width,
// color: Colors.blue,
// child: Container(
// width: MediaQuery.of(context).size.width / 2,
// height: 50,
// color: Colors.red,
// ),
// );
return Padding(
padding: const EdgeInsets.all(8.0),
child: SfLinearGauge(
minimum: 0,
maximum: 1,
interval: 1,
ranges: [LinearGaugeRange(endValue: getBarPosition(), color: Colors.cyanAccent,)],
markerPointers: [LinearShapePointer(value: getBarPosition(), position: LinearElementPosition.inside, shapeType: LinearShapePointerType.triangle, color: Colors.white, height: 20),
LinearWidgetPointer(offset: 4, position: LinearElementPosition.outside, value: getBarPosition(), child: Text(NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(position)))],
isMirrored: true,
showTicks: true,
showLabels: true
)
);
}
}

View File

@ -9,6 +9,7 @@ import 'package:tetra_stats/utils/numers_formats.dart';
import 'package:tetra_stats/widgets/gauget_num.dart'; import 'package:tetra_stats/widgets/gauget_num.dart';
import 'package:tetra_stats/widgets/graphs.dart'; import 'package:tetra_stats/widgets/graphs.dart';
import 'package:tetra_stats/widgets/stat_sell_num.dart'; import 'package:tetra_stats/widgets/stat_sell_num.dart';
import 'package:tetra_stats/widgets/tl_progress_bar.dart';
var fDiff = NumberFormat("+#,###.###;-#,###.###"); var fDiff = NumberFormat("+#,###.###;-#,###.###");
var intFDiff = NumberFormat("+#,###;-#,###"); var intFDiff = NumberFormat("+#,###;-#,###");
@ -139,20 +140,36 @@ class _TLThingyState extends State<TLThingy> {
), ),
], ],
), ),
if (currentTl.gamesPlayed >= 10 && currentTl.rd! < 100 && currentTl.nextAt >=0 && currentTl.prevAt >= 0) Padding( if (currentTl.gamesPlayed >= 10 && currentTl.rd! < 100 && currentTl.nextAt >=0 && currentTl.prevAt >= 0)
padding: const EdgeInsets.all(8.0), // Padding(
child: SfLinearGauge( // padding: const EdgeInsets.all(8.0),
minimum: currentTl.nextAt.toDouble(), // child: SfLinearGauge(
maximum: currentTl.prevAt.toDouble(), // minimum: currentTl.nextAt.toDouble(),
interval: currentTl.prevAt.toDouble() - currentTl.nextAt.toDouble(), // maximum: currentTl.prevAt.toDouble(),
ranges: [LinearGaugeRange(startValue: currentTl.standing.toDouble() <= currentTl.prevAt.toDouble() ? currentTl.standing.toDouble() : currentTl.prevAt.toDouble(), endValue: currentTl.prevAt.toDouble(), color: Colors.cyanAccent,)], // interval: currentTl.prevAt.toDouble() - currentTl.nextAt.toDouble(),
markerPointers: [LinearShapePointer(value: currentTl.standing.toDouble() <= currentTl.prevAt.toDouble() ? currentTl.standing.toDouble() : currentTl.prevAt.toDouble(), position: LinearElementPosition.inside, shapeType: LinearShapePointerType.triangle, color: Colors.white, height: 20), // ranges: [
LinearWidgetPointer(offset: 4, position: LinearElementPosition.outside, value: currentTl.standing.toDouble() <= currentTl.prevAt.toDouble() ? currentTl.standing.toDouble() : currentTl.prevAt.toDouble(), child: Text(NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(currentTl.standing)))], // LinearGaugeRange(startValue: currentTl.standing.toDouble() <= currentTl.prevAt.toDouble() ? currentTl.standing.toDouble() : currentTl.prevAt.toDouble(), endValue: currentTl.prevAt.toDouble(), color: Colors.cyanAccent, position: LinearElementPosition.cross,),
isAxisInversed: true, // //LinearGaugeRange(startValue: currentTl.standing.toDouble() <= currentTl.prevAt.toDouble() ? currentTl.standing.toDouble() + 500.00 : currentTl.prevAt.toDouble(), endValue: currentTl.prevAt.toDouble(), color: Colors.amber, position: LinearElementPosition.inside,)
isMirrored: true, // ],
showTicks: true, // markerPointers: [LinearShapePointer(value: currentTl.standing.toDouble() <= currentTl.prevAt.toDouble() ? currentTl.standing.toDouble() : currentTl.prevAt.toDouble(), position: LinearElementPosition.inside, shapeType: LinearShapePointerType.triangle, color: Colors.white, height: 20),
showLabels: true // LinearWidgetPointer(offset: 4, position: LinearElementPosition.outside, value: currentTl.standing.toDouble() <= currentTl.prevAt.toDouble() ? currentTl.standing.toDouble() : currentTl.prevAt.toDouble(), child: Text(NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(currentTl.standing)))],
), // isAxisInversed: true,
// isMirrored: true,
// showTicks: true,
// showLabels: true
// ),
// ),
TLProgress(
tr: currentTl.rating,
rank: currentTl.rank,
position: currentTl.standing,
nextRankPosition: currentTl.nextAt,
previousRankPosition: currentTl.prevAt,
previousRankTRcutoff: widget.thatRankCutoff,
previousRankTRcutoffTarget: widget.thatRankTarget,
nextRankTRcutoff: widget.nextRankCutoff,
nextRankTRcutoffTarget: widget.nextRankTarget,
nextRank: widget.tl.nextRank
), ),
if (currentTl.gamesPlayed < 10) if (currentTl.gamesPlayed < 10)
Text(t.gamesUntilRanked(left: 10 - currentTl.gamesPlayed), Text(t.gamesUntilRanked(left: 10 - currentTl.gamesPlayed),
@ -323,7 +340,7 @@ class _TLThingyState extends State<TLThingy> {
if (oldTl?.estTr?.esttr != null) TextSpan(text: comparef.format(currentTl.estTr!.esttr - oldTl!.estTr!.esttr), style: TextStyle( if (oldTl?.estTr?.esttr != null) TextSpan(text: comparef.format(currentTl.estTr!.esttr - oldTl!.estTr!.esttr), style: TextStyle(
color: oldTl!.estTr!.esttr > currentTl.estTr!.esttr ? Colors.redAccent : Colors.greenAccent color: oldTl!.estTr!.esttr > currentTl.estTr!.esttr ? Colors.redAccent : Colors.greenAccent
),), ),),
if (oldTl?.estTr?.esttr != null && widget.lbPositions?.estTr != null) const TextSpan(text: ""), if (oldTl?.estTr?.esttr != null || widget.lbPositions?.estTr != null) const TextSpan(text: ""),
if (widget.lbPositions?.estTr != null) TextSpan(text: widget.lbPositions!.estTr!.position >= 1000 ? "${t.top} ${f2.format(widget.lbPositions!.estTr!.percentage*100)}%" : "${widget.lbPositions!.estTr!.position}", style: TextStyle(color: getColorOfRank(widget.lbPositions!.estTr!.position))), if (widget.lbPositions?.estTr != null) TextSpan(text: widget.lbPositions!.estTr!.position >= 1000 ? "${t.top} ${f2.format(widget.lbPositions!.estTr!.percentage*100)}%" : "${widget.lbPositions!.estTr!.position}", style: TextStyle(color: getColorOfRank(widget.lbPositions!.estTr!.position))),
if (widget.lbPositions?.estTr != null) const TextSpan(text: ""), if (widget.lbPositions?.estTr != null) const TextSpan(text: ""),
TextSpan(text: "Glicko: ${f2.format(currentTl.estTr!.estglicko)}") TextSpan(text: "Glicko: ${f2.format(currentTl.estTr!.estglicko)}")

View File

@ -81,6 +81,7 @@
"verdictGeneral": "$n $verdict than $rank rank average", "verdictGeneral": "$n $verdict than $rank rank average",
"verdictBetter": "better", "verdictBetter": "better",
"verdictWorse": "worse", "verdictWorse": "worse",
"smooth": "Smooth",
"gamesUntilRanked": "${left} games until being ranked", "gamesUntilRanked": "${left} games until being ranked",
"nerdStats": "Nerd Stats", "nerdStats": "Nerd Stats",
"playersYouTrack": "Players you track", "playersYouTrack": "Players you track",

View File

@ -81,6 +81,7 @@
"verdictGeneral": "$verdict среднего $rank ранга на $n", "verdictGeneral": "$verdict среднего $rank ранга на $n",
"verdictBetter": "Лучше", "verdictBetter": "Лучше",
"verdictWorse": "Хуже", "verdictWorse": "Хуже",
"smooth": "Гладкий",
"gamesUntilRanked": "${left} матчей до получения рейтинга", "gamesUntilRanked": "${left} матчей до получения рейтинга",
"nerdStats": "Для задротов", "nerdStats": "Для задротов",
"playersYouTrack": "Отслеживаемые игроки", "playersYouTrack": "Отслеживаемые игроки",