Full leaderboard - full capabilities

This commit is contained in:
dan63047 2024-08-19 20:59:25 +03:00
parent e21ec84fc1
commit c0d395235b
6 changed files with 234 additions and 187 deletions

View File

@ -66,6 +66,8 @@ const Map<String, double> rankCutoffs = {
enum Stats {
tr,
glicko,
gxe,
s1tr,
rd,
gp,
gw,
@ -95,6 +97,8 @@ enum Stats {
const Map<Stats, String> chartsShortTitles = {
Stats.tr: "TR",
Stats.gxe: "Glixare",
Stats.s1tr: "S1 TR",
Stats.glicko: "Glicko",
Stats.rd: "RD",
Stats.gp: "GP",
@ -351,6 +355,10 @@ class TetrioPlayer {
return tlSeason1?.tr;
case Stats.glicko:
return tlSeason1?.glicko;
case Stats.gxe:
return tlSeason1?.gxe;
case Stats.s1tr:
return tlSeason1?.s1tr;
case Stats.rd:
return tlSeason1?.rd;
case Stats.gp:
@ -1414,6 +1422,7 @@ class TetraLeague {
}
double get winrate => gamesWon / gamesPlayed;
double get s1tr => gxe * 250;
TetraLeague.fromJson(Map<String, dynamic> json, ts) {
timestamp = ts;
@ -1451,7 +1460,7 @@ class TetraLeague {
TetrioPlayerFromLeaderboard convertToPlayerFromLeaderboard(String id) => TetrioPlayerFromLeaderboard(
id, "", "user", -1, null, timestamp, gamesPlayed, gamesWon,
tr, glicko??0, rd??noTrRd, rank, bestRank, apm??0, pps??0, vs??0, decaying);
tr, gxe, glicko??0, rd??noTrRd, rank, bestRank, apm??0, pps??0, vs??0, decaying);
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
@ -1757,6 +1766,7 @@ class TetrioPlayersLeaderboard {
avgPPS = 0,
avgVS = 0,
avgTR = 0,
avgGlixare = 0,
avgGlicko = 0,
avgRD = 0,
avgAPP = 0,
@ -1775,6 +1785,7 @@ class TetrioPlayersLeaderboard {
avgStride = 0,
avgInfDS = 0,
lowestTR = 25000,
lowestGlixare = double.infinity,
lowestGlicko = double.infinity,
lowestRD = double.infinity,
lowestWinrate = double.infinity,
@ -1797,6 +1808,7 @@ class TetrioPlayersLeaderboard {
lowestStride = double.infinity,
lowestInfDS = double.infinity,
highestTR = double.negativeInfinity,
highestGlixare = double.negativeInfinity,
highestGlicko = double.negativeInfinity,
highestRD = double.negativeInfinity,
highestWinrate = double.negativeInfinity,
@ -1827,6 +1839,7 @@ class TetrioPlayersLeaderboard {
highestGamesPlayed = 0,
highestGamesWon = 0;
String lowestTRid = "", lowestTRnick = "",
lowestGlixareID = "", lowestGlixareNick = "",
lowestGlickoID = "", lowestGlickoNick = "",
lowestRdID = "", lowestRdNick = "",
lowestGamesPlayedID = "", lowestGamesPlayedNick = "",
@ -1851,6 +1864,7 @@ class TetrioPlayersLeaderboard {
lowestStrideID = "", lowestStrideNick = "",
lowestInfDSid = "", lowestInfDSnick = "",
highestTRid = "", highestTRnick = "",
highestGlixareID = "", highestGlixareNick = "",
highestGlickoID = "", highestGlickoNick = "",
highestRdID = "", highestRdNick = "",
highestGamesPlayedID = "", highestGamesPlayedNick = "",
@ -1879,6 +1893,7 @@ class TetrioPlayersLeaderboard {
avgPPS += entry.pps;
avgVS += entry.vs;
avgTR += entry.tr;
avgGlixare += entry.gxe;
if (entry.glicko != null) avgGlicko += entry.glicko!;
if (entry.rd != null) avgRD += entry.rd!;
avgAPP += entry.nerdStats.app;
@ -1903,6 +1918,11 @@ class TetrioPlayersLeaderboard {
lowestTRid = entry.userId;
lowestTRnick = entry.username;
}
if (entry.gxe < lowestGlixare){
lowestGlixare = entry.gxe;
lowestGlixareID = entry.userId;
lowestGlixareNick = entry.username;
}
if (entry.glicko != null && entry.glicko! < lowestGlicko){
lowestGlicko = entry.glicko!;
lowestGlickoID = entry.userId;
@ -2023,6 +2043,11 @@ class TetrioPlayersLeaderboard {
highestTRid = entry.userId;
highestTRnick = entry.username;
}
if (entry.gxe > highestGlixare){
highestGlixare = entry.gxe;
highestGlixareID = entry.userId;
highestGlixareNick = entry.username;
}
if (entry.glicko != null && entry.glicko! > highestGlicko){
highestGlicko = entry.glicko!;
highestGlickoID = entry.userId;
@ -2143,6 +2168,7 @@ class TetrioPlayersLeaderboard {
avgPPS /= filtredLeaderboard.length;
avgVS /= filtredLeaderboard.length;
avgTR /= filtredLeaderboard.length;
avgGlixare /= filtredLeaderboard.length;
avgGlicko /= filtredLeaderboard.length;
avgRD /= filtredLeaderboard.length;
avgAPP /= filtredLeaderboard.length;
@ -2162,7 +2188,7 @@ class TetrioPlayersLeaderboard {
avgInfDS /= filtredLeaderboard.length;
avgGamesPlayed = (totalGamesPlayed / filtredLeaderboard.length).floor();
avgGamesWon = (totalGamesWon / filtredLeaderboard.length).floor();
return [TetraLeague(timestamp: DateTime.now(), apm: avgAPM, pps: avgPPS, vs: avgVS, glicko: avgGlicko, rd: avgRD, gamesPlayed: avgGamesPlayed, gamesWon: avgGamesWon, bestRank: rank, gxe: -1, decaying: false, tr: avgTR, rank: rank == "" ? "z" : rank, percentileRank: rank, percentile: rankCutoffs[rank]!, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1),
return [TetraLeague(timestamp: DateTime.now(), apm: avgAPM, pps: avgPPS, vs: avgVS, gxe: avgGlixare, glicko: avgGlicko, rd: avgRD, gamesPlayed: avgGamesPlayed, gamesWon: avgGamesWon, bestRank: rank, decaying: false, tr: avgTR, rank: rank == "" ? "z" : rank, percentileRank: rank, percentile: rankCutoffs[rank]!, standing: -1, standingLocal: -1, nextAt: -1, prevAt: -1),
{
"everyone": rank == "",
"totalGamesPlayed": totalGamesPlayed,
@ -2171,6 +2197,12 @@ class TetrioPlayersLeaderboard {
"lowestTR": lowestTR,
"lowestTRid": lowestTRid,
"lowestTRnick": lowestTRnick,
"lowestGlixare": lowestGlixare,
"lowestGlixareID": lowestGlixareID,
"lowestGlixareNick": lowestGlixareNick,
"lowestS1tr": lowestGlixare * 250,
"lowestS1trID": lowestGlixareID,
"lowestS1trNick": lowestGlixareNick,
"lowestGlicko": lowestGlicko,
"lowestGlickoID": lowestGlickoID,
"lowestGlickoNick": lowestGlickoNick,
@ -2243,6 +2275,12 @@ class TetrioPlayersLeaderboard {
"highestTR": highestTR,
"highestTRid": highestTRid,
"highestTRnick": highestTRnick,
"highestGlixare": highestGlixare,
"highestGlixareID": highestGlixareID,
"highestGlixareNick": highestGlixareNick,
"highestS1tr": highestGlixare * 250,
"highestS1trID": highestGlixareID,
"highestS1trNick": highestGlixareNick,
"highestGlicko": highestGlicko,
"highestGlickoID": highestGlickoID,
"highestGlickoNick": highestGlickoNick,
@ -2327,8 +2365,8 @@ class TetrioPlayersLeaderboard {
"avgPlonk": avgPlonk,
"avgStride": avgStride,
"avgInfDS": avgInfDS,
"toEnterTR": rank.toLowerCase() != "z" ? leaderboard[(leaderboard.length * rankCutoffs[rank]!).floor()].tr : lowestTR,
"toEnterGlicko": rank.toLowerCase() != "z" ? leaderboard[(leaderboard.length * rankCutoffs[rank]!).floor()].glicko : 0,
"toEnterTR": rank.toLowerCase() != "z" ? leaderboard[(leaderboard.length * rankCutoffs[rank]!).floor()-1].tr : lowestTR,
"toEnterGlicko": rank.toLowerCase() != "z" ? leaderboard[(leaderboard.length * rankCutoffs[rank]!).floor()-1].glicko : 0,
"entries": filtredLeaderboard
}];
}else{
@ -2447,6 +2485,7 @@ class TetrioPlayerFromLeaderboard {
late int gamesPlayed;
late int gamesWon;
late double tr;
late double gxe;
late double? glicko;
late double? rd;
late String rank;
@ -2469,6 +2508,7 @@ class TetrioPlayerFromLeaderboard {
this.gamesPlayed,
this.gamesWon,
this.tr,
this.gxe,
this.glicko,
this.rd,
this.rank,
@ -2484,6 +2524,7 @@ class TetrioPlayerFromLeaderboard {
double get winrate => gamesWon / gamesPlayed;
double get esttracc => estTr.esttr - tr;
double get s1tr => gxe * 250;
TetrioPlayerFromLeaderboard.fromJson(Map<String, dynamic> json, DateTime ts) {
userId = json['_id'];
@ -2495,6 +2536,7 @@ class TetrioPlayerFromLeaderboard {
gamesPlayed = json['league']['gamesplayed'] as int;
gamesWon = json['league']['gameswon'] as int;
tr = json['league']['tr'] != null ? json['league']['tr'].toDouble() : 0;
gxe = json['league']['gxe']??-1;
glicko = json['league']['glicko']?.toDouble();
rd = json['league']['rd']?.toDouble();
rank = json['league']['rank'];
@ -2514,6 +2556,10 @@ class TetrioPlayerFromLeaderboard {
return tr;
case Stats.glicko:
return glicko??-1;
case Stats.gxe:
return gxe;
case Stats.s1tr:
return s1tr;
case Stats.rd:
return rd??-1;
case Stats.gp:

View File

@ -636,18 +636,12 @@ class TetrioService extends DB {
}
/// Retrieves full Tetra League leaderboard from Tetra Channel api. Returns a leaderboard object. Throws an exception if fails to retrieve.
Future<TetrioPlayersLeaderboard> fetchTLLeaderboard({double? after}) async {
TetrioPlayersLeaderboard? cached = _cache.get("league${after != null ? after.toString() : ""}", TetrioPlayersLeaderboard);
Future<TetrioPlayersLeaderboard> fetchTLLeaderboard() async {
TetrioPlayersLeaderboard? cached = _cache.get("league", TetrioPlayersLeaderboard);
if (cached != null) return cached;
Uri url;
if (kIsWeb) {
url = Uri.https('ts.dan63.by', 'oskware_bridge.php', {"endpoint": "TLLeaderboard"});
} else {
url = Uri.https('ch.tetr.io', 'api/users/by/league', {
"limit": "100",
if (after != null) "after": "$after:0:0"
});
}
Uri url = Uri.https('ts.dan63.by', 'beanserver_blaster/leaderboard.json');
try{
final response = await client.get(url);
@ -655,16 +649,10 @@ class TetrioService extends DB {
case 200:
_lbPositions.clear();
var rawJson = jsonDecode(response.body);
if (rawJson['success']) { // if api confirmed that everything ok
TetrioPlayersLeaderboard leaderboard = TetrioPlayersLeaderboard.fromJson(rawJson['data']['entries'], "league", DateTime.fromMillisecondsSinceEpoch(rawJson['cache']['cached_at']));
TetrioPlayersLeaderboard leaderboard = TetrioPlayersLeaderboard.fromJson(rawJson['data'], "league", DateTime.fromMillisecondsSinceEpoch(rawJson['created']));
developer.log("fetchTLLeaderboard: Leaderboard retrieved and cached", name: "services/tetrio_crud");
//_leaderboardsCache[rawJson['cache']['cached_until'].toString()] = leaderboard;
_cache.store(leaderboard, rawJson['cache']['cached_until']);
_cache.store(leaderboard, rawJson['cache_until']);
return leaderboard;
} else { // idk how to hit that one
developer.log("fetchTLLeaderboard: Bruh", name: "services/tetrio_crud", error: rawJson);
throw Exception("Failed to get leaderboard (problems on the tetr.io side)"); // will it be on tetr.io side?
}
case 403:
throw TetrioForbidden();
case 429:
@ -686,19 +674,19 @@ class TetrioService extends DB {
}
}
Stream<TetrioPlayersLeaderboard> fetchFullLeaderboard() async* {
late double after;
int lbLength = 100;
TetrioPlayersLeaderboard leaderboard = await fetchTLLeaderboard();
after = leaderboard.leaderboard.last.tr;
while (lbLength == 100){
TetrioPlayersLeaderboard pseudoLb = await fetchTLLeaderboard(after: after);
leaderboard.addPlayers(pseudoLb.leaderboard);
lbLength = pseudoLb.leaderboard.length;
after = pseudoLb.leaderboard.last.tr;
yield leaderboard;
}
}
// Stream<TetrioPlayersLeaderboard> fetchFullLeaderboard() async* {
// late double after;
// int lbLength = 100;
// TetrioPlayersLeaderboard leaderboard = await fetchTLLeaderboard();
// after = leaderboard.leaderboard.last.tr;
// while (lbLength == 100){
// TetrioPlayersLeaderboard pseudoLb = await fetchTLLeaderboard(after: after);
// leaderboard.addPlayers(pseudoLb.leaderboard);
// lbLength = pseudoLb.leaderboard.length;
// after = pseudoLb.leaderboard.last.tr;
// yield leaderboard;
// }
// }
// i want to know progress, so i trying to figure out this thing:
// Stream<TetrioPlayersLeaderboard> fetchTLLeaderboardAsStream() async {

View File

@ -213,7 +213,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
nextRankGlickoCutoff = (summaries.league.rank != "z" ? summaries.league.rank == "x+" : summaries.league.percentileRank == "x+") ? topOne?.glicko??double.infinity : cutoffsGlicko?[ranks.elementAtOrNull(ranks.indexOf(summaries.league.rank != "z" ? summaries.league.rank : summaries.league.percentileRank)+1)];
}
// if (everyone != null && summaries.league.gamesPlayed > 9) rankAverages = everyone?.averages[summaries.league.percentileRank]?[0];
if (everyone != null && summaries.league.gamesPlayed > 9) rankAverages = everyone?.averages[summaries.league.percentileRank]?[0];
// Making list of Tetra League matches
//bool isTracking = await teto.isPlayerTracking(me.userId);
@ -482,8 +482,8 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
nextRankCutoff: nextRankCutoff,
nextRankCutoffGlicko: nextRankGlickoCutoff,
//nextRankTarget: (snapshot.data![1].league.rank != "z" && snapshot.data![1].league.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![1].league.rank)+1)] : null,
//averages: rankAverages,
//lbPositions: meAmongEveryone
averages: rankAverages,
lbPositions: meAmongEveryone
),
),
SizedBox(
@ -523,8 +523,8 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
nextRankCutoff: nextRankCutoff,
nextRankCutoffGlicko: nextRankGlickoCutoff,
//nextRankTarget: (snapshot.data![1].league.rank != "z" && snapshot.data![1].league.rank != "x") ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![1].league.rank)+1)] : null,
//averages: rankAverages,
//lbPositions: meAmongEveryone
averages: rankAverages,
lbPositions: meAmongEveryone
),
_TLRecords(userID: snapshot.data![0].userId, changePlayer: changePlayer, data: snapshot.data![3].records, wasActiveInTL: true, oldMathcesHere: _TLHistoryWasFetched, separateScrollController: true),
_History(chartsData: chartsData, changePlayer: changePlayer, userID: _searchFor, update: _justUpdate, wasActiveInTL: snapshot.data![1].league.gamesPlayed > 0),

View File

@ -379,6 +379,8 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
child: ListView(
children: [
_ListEntry(value: widget.rank[1]["lowestTR"], label: t.statCellNum.tr.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestTRid"], username: widget.rank[1]["lowestTRnick"], approximate: false, fractionDigits: 2),
_ListEntry(value: widget.rank[1]["lowestGlixare"], label: "Glixare", id: widget.rank[1]["lowestGlixareID"], username: widget.rank[1]["lowestGlixareNick"], approximate: false, fractionDigits: 3),
_ListEntry(value: widget.rank[1]["lowestS1tr"], label: "S1 ${t.statCellNum.tr.replaceAll(RegExp(r'\n'), " ")}", id: widget.rank[1]["lowestS1trID"], username: widget.rank[1]["lowestS1trNick"], approximate: false, fractionDigits: 2),
_ListEntry(value: widget.rank[1]["lowestGlicko"], label: "Glicko", id: widget.rank[1]["lowestGlickoID"], username: widget.rank[1]["lowestGlickoNick"], approximate: false, fractionDigits: 2),
_ListEntry(value: widget.rank[1]["lowestRD"], label: t.statCellNum.rd.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestRdID"], username: widget.rank[1]["lowestRdNick"], approximate: false, fractionDigits: 3),
_ListEntry(value: widget.rank[1]["lowestGamesPlayed"], label: t.statCellNum.gamesPlayed.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["lowestGamesPlayedID"], username: widget.rank[1]["lowestGamesPlayedNick"], approximate: false),
@ -413,6 +415,8 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
Expanded(
child: ListView(children: [
_ListEntry(value: widget.rank[0].tr, label: t.statCellNum.tr.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 2),
_ListEntry(value: widget.rank[0].gxe, label: "Glixare", id: "", username: "", approximate: false, fractionDigits: 3),
_ListEntry(value: widget.rank[0].s1tr, label: "S1 ${t.statCellNum.tr.replaceAll(RegExp(r'\n'), " ")}", id: "", username: "", approximate: false, fractionDigits: 2),
_ListEntry(value: widget.rank[0].glicko, label: "Glicko", id: "", username: "", approximate: true, fractionDigits: 2),
_ListEntry(value: widget.rank[0].rd, label: t.statCellNum.rd.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 3),
_ListEntry(value: widget.rank[0].gamesPlayed, label: t.statCellNum.gamesPlayed.replaceAll(RegExp(r'\n'), " "), id: "", username: "", approximate: true, fractionDigits: 0),
@ -446,6 +450,8 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
child: ListView(
children: [
_ListEntry(value: widget.rank[1]["highestTR"], label: t.statCellNum.tr.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestTRid"], username: widget.rank[1]["highestTRnick"], approximate: false, fractionDigits: 2),
_ListEntry(value: widget.rank[1]["highestGlixare"], label: "Glixare", id: widget.rank[1]["highestGlixareID"], username: widget.rank[1]["highestGlixareNick"], approximate: false, fractionDigits: 3),
_ListEntry(value: widget.rank[1]["highestS1tr"], label: "S1 ${t.statCellNum.tr.replaceAll(RegExp(r'\n'), " ")}", id: widget.rank[1]["highestS1trID"], username: widget.rank[1]["highestS1trNick"], approximate: false, fractionDigits: 2),
_ListEntry(value: widget.rank[1]["highestGlicko"], label: "Glicko", id: widget.rank[1]["highestGlickoID"], username: widget.rank[1]["highestGlickoNick"], approximate: false, fractionDigits: 2),
_ListEntry(value: widget.rank[1]["highestRD"], label: t.statCellNum.rd.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestRdID"], username: widget.rank[1]["highestRdNick"], approximate: false, fractionDigits: 3),
_ListEntry(value: widget.rank[1]["highestGamesPlayed"], label: t.statCellNum.gamesPlayed.replaceAll(RegExp(r'\n'), " "), id: widget.rank[1]["highestGamesPlayedID"], username: widget.rank[1]["highestGamesPlayedNick"], approximate: false),
@ -517,7 +523,7 @@ class _ListEntry extends StatelessWidget {
children: [
Text(f.format(value),
style: const TextStyle(fontSize: 22, height: 0.9)),
if (id.isNotEmpty) Text(t.forPlayer(username: username))
if (id.isNotEmpty) Text(t.forPlayer(username: username), style: TextStyle(color: Colors.grey, fontWeight: FontWeight.w100),)
],
),
onTap: id.isNotEmpty

View File

@ -4,13 +4,13 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/services/tetrio_crud.dart';
import 'package:tetra_stats/main.dart';
import 'package:tetra_stats/views/main_view.dart';
import 'package:tetra_stats/views/rank_averages_view.dart';
import 'package:tetra_stats/views/ranks_averages_view.dart';
import 'package:window_manager/window_manager.dart';
import 'package:tetra_stats/widgets/text_timestamp.dart';
final TetrioService _teto = TetrioService();
List<DropdownMenuItem> _itemStats = [for (MapEntry e in chartsShortTitles.entries) DropdownMenuItem(value: e.key, child: Text(e.value))];
Stats _sortBy = Stats.tr;
bool reversed = false;
@ -65,7 +65,7 @@ class TLLeaderboardState extends State<TLLeaderboardView> {
backgroundColor: Colors.black,
body: SafeArea(
child: FutureBuilder(
future: _teto.fetchTLLeaderboard(),
future: teto.fetchTLLeaderboard(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
@ -73,21 +73,12 @@ class TLLeaderboardState extends State<TLLeaderboardView> {
case ConnectionState.active:
return const Center(child: CircularProgressIndicator());
case ConnectionState.done:
if (snapshot.hasData){
final allPlayers = snapshot.data?.getStatRanking(snapshot.data!.leaderboard, _sortBy, reversed: reversed, country: _country);
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) windowManager.setTitle("Tetra Stats: ${t.tlLeaderboard} - ${t.players(n: allPlayers != null ? allPlayers.length : 0)}");
bool bigScreen = MediaQuery.of(context).size.width > 768;
return NestedScrollView(
headerSliverBuilder: (context, value) {
String howManyPlayers(int numberOfPlayers) => Intl.plural(
numberOfPlayers,
zero: t.lbViewZeroEntrys,
one: t.lbViewOneEntry,
other: t.lbViewManyEntrys(numberOfPlayers: t.players(n: numberOfPlayers)),
name: 'howManyPeople',
args: [numberOfPlayers],
desc: 'Description of how many people are seen in a place.',
examples: const {'numberOfPeople': 3},
);
return [
SliverToBoxAdapter(
child: Padding(
@ -97,7 +88,7 @@ class TLLeaderboardState extends State<TLLeaderboardView> {
alignment: WrapAlignment.spaceBetween,
children: [
Text(
howManyPlayers(allPlayers.length),
"${t.players(n: allPlayers.length)}${t.sprintAndBlitsRelevance(date: timestamp(snapshot.data!.timestamp))}",
style: const TextStyle(color: Colors.white, fontSize: 25),
),
TextButton(onPressed: (){
@ -205,6 +196,22 @@ class TLLeaderboardState extends State<TLLeaderboardView> {
);
}));
}
if (snapshot.hasError){
return Center(child:
Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(snapshot.error.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 42, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
if (snapshot.stackTrace != null) Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(snapshot.stackTrace.toString(), style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 18), textAlign: TextAlign.center),
),
],
)
);
}
return Text("end of FutureBuilder");
}
})),
);
}

View File

@ -2,7 +2,7 @@ name: tetra_stats
description: Track your and other player stats in TETR.IO
publish_to: 'none'
version: 1.6.5+31
version: 1.6.6+32
environment:
sdk: '>=3.0.0'