Thinking about adaptivity
This commit is contained in:
parent
1b9249f36b
commit
874d5a2e5c
|
@ -51,6 +51,7 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId "com.dan63.tetra_stats"
|
applicationId "com.dan63.tetra_stats"
|
||||||
|
testApplicationId "com.dan63.tetra_stats.dev_build"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||||
minSdkVersion flutter.minSdkVersion
|
minSdkVersion flutter.minSdkVersion
|
||||||
|
|
|
@ -49,7 +49,7 @@ class Summaries {
|
||||||
];
|
];
|
||||||
league =
|
league =
|
||||||
TetraLeague.fromJson(json['league'], DateTime.now(), currentSeason, i);
|
TetraLeague.fromJson(json['league'], DateTime.now(), currentSeason, i);
|
||||||
if (json['league']['past'].isNotEmpty)
|
if (json['league']['past'] != null && json['league']['past'].isNotEmpty)
|
||||||
for (var key in json['league']['past'].keys) {
|
for (var key in json['league']['past'].keys) {
|
||||||
pastLeague[int.parse(key)] = TetraLeague.fromJson(
|
pastLeague[int.parse(key)] = TetraLeague.fromJson(
|
||||||
json['league']['past'][key],
|
json['league']['past'][key],
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
/// Locales: 3
|
/// Locales: 3
|
||||||
/// Strings: 1818 (606 per locale)
|
/// Strings: 1818 (606 per locale)
|
||||||
///
|
///
|
||||||
/// Built on 2024-09-30 at 21:23 UTC
|
/// Built on 2024-11-16 at 13:39 UTC
|
||||||
|
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
|
|
|
@ -59,7 +59,17 @@ ThemeData theme = ThemeData(
|
||||||
expandedAlignment: Alignment.bottomCenter,
|
expandedAlignment: Alignment.bottomCenter,
|
||||||
),
|
),
|
||||||
dropdownMenuTheme: DropdownMenuThemeData(textStyle: TextStyle(fontFamily: "Eurostile Round", fontSize: 18)),
|
dropdownMenuTheme: DropdownMenuThemeData(textStyle: TextStyle(fontFamily: "Eurostile Round", fontSize: 18)),
|
||||||
scaffoldBackgroundColor: Colors.black
|
scaffoldBackgroundColor: Colors.black,
|
||||||
|
tooltipTheme: TooltipThemeData(
|
||||||
|
textStyle: TextStyle(color: Colors.white),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8.0)),
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.white
|
||||||
|
),
|
||||||
|
color: Colors.black,
|
||||||
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -118,7 +118,10 @@ class _DestinationSavedData extends State<DestinationSavedData> {
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Card(
|
Card(
|
||||||
child: TabBar(tabs: [
|
child: TabBar(
|
||||||
|
labelStyle: Theme.of(context).textTheme.titleMedium!.copyWith(fontSize: 28),
|
||||||
|
labelColor: Theme.of(context).colorScheme.primary,
|
||||||
|
tabs: [
|
||||||
Tab(text: "S${currentSeason} TL States"),
|
Tab(text: "S${currentSeason} TL States"),
|
||||||
Tab(text: "S1 TL States"),
|
Tab(text: "S1 TL States"),
|
||||||
Tab(text: "TL Records")
|
Tab(text: "TL Records")
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:tetra_stats/data_objects/cutoff_tetrio.dart';
|
import 'package:tetra_stats/data_objects/cutoff_tetrio.dart';
|
||||||
import 'package:tetra_stats/data_objects/news.dart';
|
import 'package:tetra_stats/data_objects/news.dart';
|
||||||
import 'package:tetra_stats/data_objects/p1nkl0bst3r.dart';
|
import 'package:tetra_stats/data_objects/p1nkl0bst3r.dart';
|
||||||
|
@ -24,6 +25,7 @@ import 'package:tetra_stats/main.dart';
|
||||||
|
|
||||||
late Future<FetchResults> _data;
|
late Future<FetchResults> _data;
|
||||||
TetrioPlayersLeaderboard? _everyone;
|
TetrioPlayersLeaderboard? _everyone;
|
||||||
|
int destination = 0;
|
||||||
|
|
||||||
Future<FetchResults> getData(String searchFor) async {
|
Future<FetchResults> getData(String searchFor) async {
|
||||||
TetrioPlayer player;
|
TetrioPlayer player;
|
||||||
|
@ -102,15 +104,23 @@ Map<Cards, String> cardsTitles = {
|
||||||
late ScrollController controller;
|
late ScrollController controller;
|
||||||
|
|
||||||
class _MainState extends State<MainView> with TickerProviderStateMixin {
|
class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
int destination = 0;
|
|
||||||
String _searchFor = "6098518e3d5155e6ec429cdc";
|
String _searchFor = "6098518e3d5155e6ec429cdc";
|
||||||
final TextEditingController _searchController = TextEditingController();
|
final TextEditingController _searchController = TextEditingController();
|
||||||
|
Timer _backgroundUpdate = Timer(const Duration(days: 365), (){});
|
||||||
|
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
teto.open();
|
teto.open();
|
||||||
controller = ScrollController();
|
controller = ScrollController();
|
||||||
changePlayer(_searchFor);
|
changePlayer(_searchFor);
|
||||||
|
|
||||||
|
if (prefs.getBool("updateInBG") == true) {
|
||||||
|
_backgroundUpdate = Timer(Duration(minutes: 5), () {
|
||||||
|
changePlayer(_searchFor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,47 +149,144 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NavigationDestination getMobileDestinationButton(IconData icon, String title){
|
||||||
|
return NavigationDestination(
|
||||||
|
icon: Tooltip(
|
||||||
|
message: title,
|
||||||
|
child: Icon(icon)
|
||||||
|
),
|
||||||
|
selectedIcon: Icon(icon),
|
||||||
|
label: title,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return LayoutBuilder(
|
||||||
drawer: SearchDrawer(changePlayer: changePlayer, controller: _searchController),
|
builder: (BuildContext context, BoxConstraints constraints){
|
||||||
body: LayoutBuilder(
|
bool screenIsBig = constraints.maxWidth > 768.00;
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
return Scaffold(
|
||||||
return Row(
|
key: _scaffoldKey,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
drawer: SearchDrawer(changePlayer: changePlayer, controller: _searchController),
|
||||||
children: [
|
endDrawer: DestinationsDrawer(changeDestination: (value) {setState(() {destination = value;});}),
|
||||||
TweenAnimationBuilder(
|
bottomNavigationBar: screenIsBig ? null : BottomAppBar(
|
||||||
child: NavigationRail(
|
shape: const AutomaticNotchedShape(RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(0.0))), RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16.0)))),
|
||||||
leading: FloatingActionButton(
|
notchMargin: 2.0,
|
||||||
elevation: 0,
|
height: 88,
|
||||||
onPressed: () {
|
child: IconTheme(
|
||||||
Scaffold.of(context).openDrawer();
|
data: IconThemeData(color: Theme.of(context).colorScheme.primary),
|
||||||
},
|
child: Row(
|
||||||
child: const Icon(Icons.search),
|
children: <Widget>[
|
||||||
),
|
IconButton(
|
||||||
trailing: IconButton(
|
tooltip: 'Open navigation menu',
|
||||||
onPressed: () {
|
icon: const Icon(Icons.menu),
|
||||||
// Add your onPressed code here!
|
onPressed: () {
|
||||||
},
|
_scaffoldKey.currentState!.openEndDrawer();
|
||||||
icon: const Icon(Icons.refresh),
|
},
|
||||||
),
|
|
||||||
destinations: [
|
|
||||||
getDestinationButton(Icons.home, "Home"),
|
|
||||||
getDestinationButton(Icons.data_thresholding_outlined, "Graphs"),
|
|
||||||
getDestinationButton(Icons.leaderboard, "Leaderboards"),
|
|
||||||
getDestinationButton(Icons.compress, "Cutoffs"),
|
|
||||||
getDestinationButton(Icons.calculate, "Calc"),
|
|
||||||
getDestinationButton(Icons.info_outline, "Information"),
|
|
||||||
getDestinationButton(Icons.storage, "Saved Data"),
|
|
||||||
getDestinationButton(Icons.settings, "Settings"),
|
|
||||||
],
|
|
||||||
selectedIndex: destination,
|
|
||||||
onDestinationSelected: (value) {
|
|
||||||
setState(() {
|
|
||||||
destination = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: SegmentedButton<CardMod>(
|
||||||
|
showSelectedIcon: false,
|
||||||
|
selected: <CardMod>{cardMod},
|
||||||
|
segments: modeButtons[rightCard]!,
|
||||||
|
onSelectionChanged: (p0) {
|
||||||
|
setState(() {
|
||||||
|
cardMod = p0.first;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SegmentedButton<Cards>(
|
||||||
|
showSelectedIcon: false,
|
||||||
|
segments: <ButtonSegment<Cards>>[
|
||||||
|
const ButtonSegment<Cards>(
|
||||||
|
value: Cards.overview,
|
||||||
|
tooltip: 'Overview',
|
||||||
|
icon: Icon(Icons.calendar_view_day)),
|
||||||
|
ButtonSegment<Cards>(
|
||||||
|
value: Cards.tetraLeague,
|
||||||
|
tooltip: 'Tetra League',
|
||||||
|
icon: SvgPicture.asset("res/icons/league.svg", height: 16, colorFilter: ColorFilter.mode(theme.colorScheme.primary, BlendMode.modulate))),
|
||||||
|
ButtonSegment<Cards>(
|
||||||
|
value: Cards.quickPlay,
|
||||||
|
tooltip: 'Quick Play',
|
||||||
|
icon: SvgPicture.asset("res/icons/qp.svg", height: 16, colorFilter: ColorFilter.mode(theme.colorScheme.primary, BlendMode.modulate))),
|
||||||
|
ButtonSegment<Cards>(
|
||||||
|
value: Cards.sprint,
|
||||||
|
tooltip: '40 Lines',
|
||||||
|
icon: SvgPicture.asset("res/icons/40l.svg", height: 16, colorFilter: ColorFilter.mode(theme.colorScheme.primary, BlendMode.modulate))),
|
||||||
|
ButtonSegment<Cards>(
|
||||||
|
value: Cards.blitz,
|
||||||
|
tooltip: 'Blitz',
|
||||||
|
icon: SvgPicture.asset("res/icons/blitz.svg", height: 16, colorFilter: ColorFilter.mode(theme.colorScheme.primary, BlendMode.modulate))),
|
||||||
|
],
|
||||||
|
selected: <Cards>{rightCard},
|
||||||
|
onSelectionChanged: (Set<Cards> newSelection) {
|
||||||
|
setState(() {
|
||||||
|
cardMod = CardMod.info;
|
||||||
|
rightCard = newSelection.first;
|
||||||
|
});})
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
tooltip: 'Fake "Open navigation menu" button\nHere only for symmetry',
|
||||||
|
icon: const Icon(Icons.menu, color: Colors.transparent),
|
||||||
|
onPressed: () {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
floatingActionButtonLocation: screenIsBig ? null : FloatingActionButtonLocation.endDocked,
|
||||||
|
floatingActionButton: screenIsBig ? null : FloatingActionButton(
|
||||||
|
elevation: 0,
|
||||||
|
onPressed: () {
|
||||||
|
_scaffoldKey.currentState!.openDrawer();
|
||||||
|
},
|
||||||
|
child: const Icon(Icons.search),
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
if (screenIsBig) TweenAnimationBuilder(
|
||||||
|
child: NavigationRail(
|
||||||
|
leading: FloatingActionButton(
|
||||||
|
elevation: 0,
|
||||||
|
onPressed: () {
|
||||||
|
Scaffold.of(context).openDrawer();
|
||||||
|
},
|
||||||
|
child: const Icon(Icons.search),
|
||||||
|
),
|
||||||
|
trailing: IconButton(
|
||||||
|
tooltip: "Refresh data",
|
||||||
|
onPressed: () {
|
||||||
|
changePlayer(_searchFor);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.refresh),
|
||||||
|
),
|
||||||
|
destinations: [
|
||||||
|
getDestinationButton(Icons.home, "Home"),
|
||||||
|
getDestinationButton(Icons.data_thresholding_outlined, "Graphs"),
|
||||||
|
getDestinationButton(Icons.leaderboard, "Leaderboards"),
|
||||||
|
getDestinationButton(Icons.compress, "Cutoffs"),
|
||||||
|
getDestinationButton(Icons.calculate, "Calc"),
|
||||||
|
getDestinationButton(Icons.info_outline, "Information"),
|
||||||
|
getDestinationButton(Icons.storage, "Saved Data"),
|
||||||
|
getDestinationButton(Icons.settings, "Settings"),
|
||||||
|
],
|
||||||
|
selectedIndex: destination,
|
||||||
|
onDestinationSelected: (value) {
|
||||||
|
setState(() {
|
||||||
|
destination = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
duration: Durations.long4,
|
duration: Durations.long4,
|
||||||
tween: Tween<double>(begin: 0, end: 1),
|
tween: Tween<double>(begin: 0, end: 1),
|
||||||
curve: Easing.standard,
|
curve: Easing.standard,
|
||||||
|
@ -189,23 +296,25 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
child: Opacity(opacity: value, child: child),
|
child: Opacity(opacity: value, child: child),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: switch (destination){
|
child: switch (destination){
|
||||||
0 => DestinationHome(searchFor: _searchFor, constraints: constraints, dataFuture: _data),
|
0 => DestinationHome(searchFor: _searchFor, constraints: constraints, dataFuture: _data, noSidebar: !screenIsBig),
|
||||||
1 => DestinationGraphs(searchFor: _searchFor, constraints: constraints),
|
1 => DestinationGraphs(searchFor: _searchFor, constraints: constraints),
|
||||||
2 => DestinationLeaderboards(constraints: constraints),
|
2 => DestinationLeaderboards(constraints: constraints),
|
||||||
3 => DestinationCutoffs(constraints: constraints),
|
3 => DestinationCutoffs(constraints: constraints),
|
||||||
4 => DestinationCalculator(constraints: constraints),
|
4 => DestinationCalculator(constraints: constraints),
|
||||||
5 => DestinationInfo(constraints: constraints),
|
5 => DestinationInfo(constraints: constraints),
|
||||||
6 => DestinationSavedData(constraints: constraints),
|
6 => DestinationSavedData(constraints: constraints),
|
||||||
7 => DestinationSettings(constraints: constraints),
|
7 => DestinationSettings(constraints: constraints),
|
||||||
_ => Text("Unknown destination $destination")
|
_ => Text("Unknown destination $destination")
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
]);
|
]
|
||||||
},
|
),
|
||||||
));
|
));
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,80 +331,178 @@ class _SearchDrawerState extends State<SearchDrawer> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Drawer(
|
return Drawer(
|
||||||
child: StreamBuilder(
|
child: SafeArea(
|
||||||
stream: teto.allPlayers,
|
child: StreamBuilder(
|
||||||
builder: (context, snapshot) {
|
stream: teto.allPlayers,
|
||||||
switch (snapshot.connectionState) {
|
builder: (context, snapshot) {
|
||||||
case ConnectionState.none:
|
switch (snapshot.connectionState) {
|
||||||
case ConnectionState.waiting:
|
case ConnectionState.none:
|
||||||
case ConnectionState.done:
|
case ConnectionState.waiting:
|
||||||
case ConnectionState.active:
|
case ConnectionState.done:
|
||||||
final allPlayers = (snapshot.data != null)
|
case ConnectionState.active:
|
||||||
? snapshot.data as Map<String, String>
|
final allPlayers = (snapshot.data != null)
|
||||||
: <String, String>{};
|
? snapshot.data as Map<String, String>
|
||||||
allPlayers.remove(prefs.getString("playerID") ?? "6098518e3d5155e6ec429cdc"); // player from the home button will be delisted
|
: <String, String>{};
|
||||||
List<String> keys = allPlayers.keys.toList();
|
allPlayers.remove(prefs.getString("playerID") ?? "6098518e3d5155e6ec429cdc"); // player from the home button will be delisted
|
||||||
return NestedScrollView(
|
List<String> keys = allPlayers.keys.toList();
|
||||||
headerSliverBuilder: (BuildContext context, bool value){
|
return NestedScrollView(
|
||||||
return [
|
headerSliverBuilder: (BuildContext context, bool value){
|
||||||
SliverToBoxAdapter(
|
return [
|
||||||
child: SearchBar(
|
SliverToBoxAdapter(
|
||||||
controller: widget.controller,
|
child: SearchBar(
|
||||||
hintText: "Enter the username",
|
controller: widget.controller,
|
||||||
hintStyle: const WidgetStatePropertyAll(TextStyle(color: Colors.grey)),
|
hintText: "Enter the username",
|
||||||
trailing: [
|
hintStyle: const WidgetStatePropertyAll(TextStyle(color: Colors.grey)),
|
||||||
IconButton(onPressed: (){setState(() {
|
trailing: [
|
||||||
widget.changePlayer(widget.controller.value.text);
|
IconButton(onPressed: (){setState(() {
|
||||||
Navigator.of(context).pop();
|
widget.changePlayer(widget.controller.value.text);
|
||||||
});}, icon: const Icon(Icons.search))
|
Navigator.of(context).pop();
|
||||||
],
|
});}, icon: const Icon(Icons.search))
|
||||||
onSubmitted: (value) {
|
],
|
||||||
setState(() {
|
onSubmitted: (value) {
|
||||||
widget.changePlayer(value);
|
setState(() {
|
||||||
Navigator.of(context).pop();
|
widget.changePlayer(value);
|
||||||
});
|
Navigator.of(context).pop();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(Icons.home),
|
||||||
|
title: Text(prefs.getString("player") ?? "dan63"),
|
||||||
|
onTap: () {
|
||||||
|
widget.changePlayer(prefs.getString("playerID") ?? "6098518e3d5155e6ec429cdc");
|
||||||
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
|
||||||
SliverToBoxAdapter(
|
|
||||||
child: ListTile(
|
|
||||||
leading: Icon(Icons.home),
|
|
||||||
title: Text(prefs.getString("player") ?? "dan63"),
|
|
||||||
onTap: () {
|
|
||||||
widget.changePlayer(prefs.getString("playerID") ?? "6098518e3d5155e6ec429cdc");
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SliverToBoxAdapter(
|
|
||||||
child: Divider(),
|
|
||||||
),
|
|
||||||
SliverToBoxAdapter(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 10.0),
|
|
||||||
child: Text("Tracked Players", style: Theme.of(context).textTheme.headlineLarge),
|
|
||||||
),
|
),
|
||||||
)
|
SliverToBoxAdapter(
|
||||||
];
|
child: Divider(),
|
||||||
},
|
),
|
||||||
body: ListView.builder( // Builds list of tracked players.
|
SliverToBoxAdapter(
|
||||||
itemCount: allPlayers.length,
|
child: Padding(
|
||||||
itemBuilder: (context, index) {
|
padding: const EdgeInsets.only(left: 10.0),
|
||||||
var i = allPlayers.length-1-index; // Last players in this map are most recent ones, they are gonna be shown at the top.
|
child: Text("Tracked Players", style: Theme.of(context).textTheme.headlineLarge),
|
||||||
return ListTile(
|
),
|
||||||
title: Text(allPlayers[keys[i]]??keys[i]), // Takes last known username from list of states
|
)
|
||||||
trailing: IconButton(onPressed: (){
|
];
|
||||||
teto.deletePlayerToTrack(keys[i]);
|
},
|
||||||
}, icon: Icon(Icons.delete, color: Colors.grey)),
|
body: ListView.builder( // Builds list of tracked players.
|
||||||
onTap: () {
|
itemCount: allPlayers.length,
|
||||||
widget.changePlayer(keys[i]); // changes to chosen player
|
itemBuilder: (context, index) {
|
||||||
Navigator.of(context).pop(); // and closes itself.
|
var i = allPlayers.length-1-index; // Last players in this map are most recent ones, they are gonna be shown at the top.
|
||||||
},
|
return ListTile(
|
||||||
);
|
title: Text(allPlayers[keys[i]]??keys[i]), // Takes last known username from list of states
|
||||||
})
|
trailing: IconButton(onPressed: (){
|
||||||
);
|
teto.deletePlayerToTrack(keys[i]);
|
||||||
|
}, icon: Icon(Icons.delete, color: Colors.grey)),
|
||||||
|
onTap: () {
|
||||||
|
widget.changePlayer(keys[i]); // changes to chosen player
|
||||||
|
Navigator.of(context).pop(); // and closes itself.
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DestinationsDrawer extends StatefulWidget{
|
||||||
|
final Function changeDestination;
|
||||||
|
|
||||||
|
const DestinationsDrawer({super.key, required this.changeDestination});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _DestinationsDrawerState();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DestinationsDrawerState extends State<DestinationsDrawer>{
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Drawer(
|
||||||
|
child: NestedScrollView(
|
||||||
|
headerSliverBuilder: (BuildContext context, bool value){
|
||||||
|
return [
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: DrawerHeader(
|
||||||
|
child: Text("Navigation menu", style: const TextStyle(color: Colors.white, fontSize: 25),
|
||||||
|
)))
|
||||||
|
];
|
||||||
|
},
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.home),
|
||||||
|
title: Text("Home"),
|
||||||
|
onTap: (){
|
||||||
|
widget.changeDestination(0);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.data_thresholding_outlined),
|
||||||
|
title: Text("Graphs"),
|
||||||
|
onTap: (){
|
||||||
|
widget.changeDestination(1);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.leaderboard),
|
||||||
|
title: Text("Leaderboards"),
|
||||||
|
onTap: (){
|
||||||
|
widget.changeDestination(2);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.compress),
|
||||||
|
title: Text("Cutoffs"),
|
||||||
|
onTap: (){
|
||||||
|
widget.changeDestination(3);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.calculate),
|
||||||
|
title: Text("Calc"),
|
||||||
|
onTap: (){
|
||||||
|
widget.changeDestination(4);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.info_outline),
|
||||||
|
title: Text("Information"),
|
||||||
|
onTap: (){
|
||||||
|
widget.changeDestination(5);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.storage),
|
||||||
|
title: Text("Saved Data"),
|
||||||
|
onTap: (){
|
||||||
|
widget.changeDestination(6);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.settings),
|
||||||
|
title: Text("Settings"),
|
||||||
|
onTap: (){
|
||||||
|
widget.changeDestination(7);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:tetra_stats/data_objects/record_single.dart';
|
import 'package:tetra_stats/data_objects/record_single.dart';
|
||||||
import 'package:tetra_stats/gen/strings.g.dart';
|
import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
|
import 'package:tetra_stats/views/destination_home.dart';
|
||||||
import 'package:tetra_stats/widgets/singleplayer_record.dart';
|
import 'package:tetra_stats/widgets/singleplayer_record.dart';
|
||||||
import 'package:tetra_stats/widgets/text_timestamp.dart';
|
import 'package:tetra_stats/widgets/text_timestamp.dart';
|
||||||
|
|
||||||
|
@ -15,28 +16,28 @@ class SingleplayerRecordView extends StatelessWidget {
|
||||||
//bool bigScreen = MediaQuery.of(context).size.width >= 368;
|
//bool bigScreen = MediaQuery.of(context).size.width >= 368;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
appBar: AppBar(
|
floatingActionButtonLocation: FloatingActionButtonLocation.startTop,
|
||||||
title: Text("${
|
floatingActionButton: Padding(
|
||||||
switch (record.gamemode){
|
padding: const EdgeInsets.fromLTRB(16.0, 8.0, 0.0, 0.0),
|
||||||
"40l" => t.sprint,
|
child: FloatingActionButton(
|
||||||
"blitz" => t.blitz,
|
onPressed: () => Navigator.pop(context),
|
||||||
String() => "5000000 Blast",
|
tooltip: 'Fuck go back',
|
||||||
}
|
child: const Icon(Icons.arrow_back),
|
||||||
} ${timestamp(record.timestamp)}"),
|
),
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Row(
|
child: Center(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
child: Container(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
constraints: BoxConstraints(
|
||||||
children: [
|
maxWidth: 768
|
||||||
Column(
|
),
|
||||||
children: [
|
child: switch (record.gamemode){
|
||||||
SingleplayerRecord(record: record, hideTitle: true),
|
"zenith" => ZenithCard(record, false),
|
||||||
// TODO: Insert replay link here
|
"zenithex" => ZenithCard(record, false),
|
||||||
]
|
_ => SingleplayerRecord(record: record, hideTitle: true)
|
||||||
)
|
},
|
||||||
],
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
|
@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:tetra_stats/data_objects/tetra_league.dart';
|
import 'package:tetra_stats/data_objects/tetra_league.dart';
|
||||||
import 'package:tetra_stats/gen/strings.g.dart';
|
import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
|
import 'package:tetra_stats/widgets/graphs.dart';
|
||||||
|
import 'package:tetra_stats/widgets/nerd_stats_thingy.dart';
|
||||||
import 'package:tetra_stats/widgets/text_timestamp.dart';
|
import 'package:tetra_stats/widgets/text_timestamp.dart';
|
||||||
import 'package:tetra_stats/widgets/tl_thingy.dart';
|
import 'package:tetra_stats/widgets/tl_thingy.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
@ -45,11 +47,19 @@ class StateState extends State<StateView> {
|
||||||
//final t = Translations.of(context);
|
//final t = Translations.of(context);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text("State from ${timestamp(widget.state.timestamp)}"),
|
title: Text("State from ${timestamp(widget.state.timestamp)}", style: Theme.of(context).textTheme.titleMedium!.copyWith(fontSize: 28)),
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: TetraLeagueThingy(league: widget.state)
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
TetraLeagueThingy(league: widget.state),
|
||||||
|
if (widget.state.nerdStats != null) NerdStatsThingy(nerdStats: widget.state.nerdStats!),
|
||||||
|
if (widget.state.playstyle != null) Graphs(widget.state.apm!, widget.state.pps!, widget.state.vs!, widget.state.nerdStats!, widget.state.playstyle!)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,128 @@ class NerdStatsThingy extends StatelessWidget{
|
||||||
final NerdStats? oldNerdStats;
|
final NerdStats? oldNerdStats;
|
||||||
final CutoffTetrio? averages;
|
final CutoffTetrio? averages;
|
||||||
final PlayerLeaderboardPosition? lbPos;
|
final PlayerLeaderboardPosition? lbPos;
|
||||||
|
final double width;
|
||||||
|
|
||||||
const NerdStatsThingy({super.key, required this.nerdStats, this.oldNerdStats, this.averages, this.lbPos});
|
const NerdStatsThingy({super.key, required this.nerdStats, this.oldNerdStats, this.averages, this.lbPos, this.width = double.infinity});
|
||||||
|
|
||||||
|
Widget big(){
|
||||||
|
return SizedBox(
|
||||||
|
height: 256.0,
|
||||||
|
width: 256.0,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(1000),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(gradient: RadialGradient(colors: [Colors.black12.withAlpha(100), Colors.black], radius: 0.6)),
|
||||||
|
child: SfRadialGauge(
|
||||||
|
axes: [
|
||||||
|
RadialAxis(
|
||||||
|
startAngle: 190,
|
||||||
|
endAngle: 350,
|
||||||
|
showLabels: false,
|
||||||
|
showTicks: true,
|
||||||
|
radiusFactor: 1,
|
||||||
|
centerY: 0.5,
|
||||||
|
minimum: 0,
|
||||||
|
maximum: 1,
|
||||||
|
ranges: [
|
||||||
|
GaugeRange(startValue: 0, endValue: 0.2, color: Colors.red),
|
||||||
|
GaugeRange(startValue: 0.2, endValue: 0.4, color: Colors.yellow),
|
||||||
|
GaugeRange(startValue: 0.4, endValue: 0.6, color: Colors.green),
|
||||||
|
GaugeRange(startValue: 0.6, endValue: 0.8, color: Colors.blue),
|
||||||
|
GaugeRange(startValue: 0.8, endValue: 1, color: Colors.purple),
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
NeedlePointer(
|
||||||
|
value: nerdStats.app,
|
||||||
|
enableAnimation: true,
|
||||||
|
needleLength: 0.9,
|
||||||
|
needleStartWidth: 2,
|
||||||
|
needleEndWidth: 15,
|
||||||
|
knobStyle: const KnobStyle(color: Colors.transparent),
|
||||||
|
gradient: const LinearGradient(colors: [Colors.transparent, Colors.white], begin: Alignment.bottomCenter, end: Alignment.topCenter, stops: [0.5, 1]),)
|
||||||
|
],
|
||||||
|
annotations: [
|
||||||
|
GaugeAnnotation(widget: Container(child:
|
||||||
|
RichText(
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
text: TextSpan(
|
||||||
|
style: const TextStyle(fontFamily: "Eurostile Round"),
|
||||||
|
children: [
|
||||||
|
const TextSpan(text: "APP\n"),
|
||||||
|
TextSpan(text: f3.format(nerdStats.app), style: TextStyle(fontSize: 25, fontFamily: "Eurostile Round Extended", fontWeight: FontWeight.w100, color: getStatColor(nerdStats.app, averages?.nerdStats?.app, true))),
|
||||||
|
if (lbPos != null) TextSpan(text: lbPos!.app!.position >= 1000 ? "\n${t.top} ${f2.format(lbPos!.app!.percentage*100)}%" : "\n№${lbPos!.app!.position}", style: TextStyle(color: getColorOfRank(lbPos!.app!.position))),
|
||||||
|
if (oldNerdStats != null) TextSpan(text: "\n${comparef.format(nerdStats.app - oldNerdStats!.app)}", style: TextStyle(color: getDifferenceColor(nerdStats.app - oldNerdStats!.app)))
|
||||||
|
]
|
||||||
|
))),
|
||||||
|
angle: 270,positionFactor: 0.5
|
||||||
|
)],
|
||||||
|
),
|
||||||
|
RadialAxis(
|
||||||
|
startAngle: 20,
|
||||||
|
endAngle: 160,
|
||||||
|
isInversed: true,
|
||||||
|
showLabels: false,
|
||||||
|
showTicks: true,
|
||||||
|
radiusFactor: 1,
|
||||||
|
centerY: 0.5,
|
||||||
|
minimum: 1.8,
|
||||||
|
maximum: 2.4,
|
||||||
|
ranges: [
|
||||||
|
GaugeRange(startValue: 1.8, endValue: 2.0, color: Colors.green),
|
||||||
|
GaugeRange(startValue: 2.0, endValue: 2.2, color: Colors.blue),
|
||||||
|
GaugeRange(startValue: 2.2, endValue: 2.4, color: Colors.purple),
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
NeedlePointer(
|
||||||
|
value: nerdStats.vsapm,
|
||||||
|
enableAnimation: true,
|
||||||
|
needleLength: 0.9,
|
||||||
|
needleStartWidth: 2,
|
||||||
|
needleEndWidth: 15,
|
||||||
|
knobStyle: const KnobStyle(color: Colors.transparent),
|
||||||
|
gradient: const LinearGradient(colors: [Colors.transparent, Colors.white], begin: Alignment.bottomCenter, end: Alignment.topCenter, stops: [0.5, 1]),)
|
||||||
|
],
|
||||||
|
annotations: [
|
||||||
|
GaugeAnnotation(widget: Container(child:
|
||||||
|
RichText(
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
text: TextSpan(
|
||||||
|
style: const TextStyle(fontFamily: "Eurostile Round"),
|
||||||
|
children: [
|
||||||
|
const TextSpan(text: "VS/APM\n"),
|
||||||
|
TextSpan(text: f3.format(nerdStats.vsapm), style: TextStyle(fontSize: 25, fontFamily: "Eurostile Round Extended", fontWeight: FontWeight.w100, color: getStatColor(nerdStats.vsapm, averages?.nerdStats?.vsapm, true))),
|
||||||
|
if (lbPos != null) TextSpan(text: lbPos!.vsapm!.position >= 1000 ? "\n${t.top} ${f2.format(lbPos!.vsapm!.percentage*100)}%" : "\n№${lbPos!.vsapm!.position}", style: TextStyle(color: getColorOfRank(lbPos!.vsapm!.position))),
|
||||||
|
if (oldNerdStats != null) TextSpan(text: "\n${comparef.format(nerdStats.vsapm - oldNerdStats!.vsapm)}", style: TextStyle(color: getDifferenceColor(nerdStats.vsapm - oldNerdStats!.vsapm))),
|
||||||
|
]
|
||||||
|
))),
|
||||||
|
angle: 90,positionFactor: 0.5
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget manySmalls(){
|
||||||
|
return Wrap(
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
spacing: 10.0,
|
||||||
|
runSpacing: 10.0,
|
||||||
|
runAlignment: WrapAlignment.start,
|
||||||
|
children: [
|
||||||
|
GaugetThingy(value: nerdStats.dss, oldValue: oldNerdStats?.dss, min: 0, max: 1.0, tickInterval: .2, label: "DS/S", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true, avgValue: averages?.nerdStats?.dss, lbPos: lbPos?.dss),
|
||||||
|
GaugetThingy(value: nerdStats.dsp, oldValue: oldNerdStats?.dsp, min: 0, max: 1.0, tickInterval: .2, label: "DS/P", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true, avgValue: averages?.nerdStats?.dsp, lbPos: lbPos?.dsp),
|
||||||
|
GaugetThingy(value: nerdStats.appdsp, oldValue: oldNerdStats?.appdsp, min: 0, max: 1.2, tickInterval: .2, label: "APP+DS/P", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true, avgValue: averages?.nerdStats?.appdsp, lbPos: lbPos?.appdsp),
|
||||||
|
GaugetThingy(value: nerdStats.cheese, oldValue: oldNerdStats?.cheese, min: -80, max: 80, tickInterval: 40, label: "Cheese", sideSize: 128.0, fractionDigits: 2, moreIsBetter: false, lbPos: lbPos?.cheese),
|
||||||
|
GaugetThingy(value: nerdStats.gbe, oldValue: oldNerdStats?.gbe, min: 0, max: 1.0, tickInterval: .2, label: "GbE", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true, avgValue: averages?.nerdStats?.gbe, lbPos: lbPos?.gbe),
|
||||||
|
GaugetThingy(value: nerdStats.nyaapp, oldValue: oldNerdStats?.nyaapp, min: 0, max: 1.2, tickInterval: .2, label: "wAPP", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true, avgValue: averages?.nerdStats?.nyaapp, lbPos: lbPos?.nyaapp),
|
||||||
|
GaugetThingy(value: nerdStats.area, oldValue: oldNerdStats?.area, min: 0, max: 1000, tickInterval: 100, label: "Area", sideSize: 128.0, fractionDigits: 1, moreIsBetter: true, avgValue: averages?.nerdStats?.area, lbPos: lbPos?.area),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -23,126 +143,20 @@ class NerdStatsThingy extends StatelessWidget{
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(12.0, 8.0, 12.0, 8.0),
|
padding: const EdgeInsets.fromLTRB(12.0, 8.0, 12.0, 8.0),
|
||||||
child: Row(
|
child: width > 600 ? Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
big(),
|
||||||
height: 256.0,
|
Expanded(child: manySmalls())
|
||||||
width: 256.0,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(1000),
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(gradient: RadialGradient(colors: [Colors.black12.withAlpha(100), Colors.black], radius: 0.6)),
|
|
||||||
child: SfRadialGauge(
|
|
||||||
axes: [
|
|
||||||
RadialAxis(
|
|
||||||
startAngle: 190,
|
|
||||||
endAngle: 350,
|
|
||||||
showLabels: false,
|
|
||||||
showTicks: true,
|
|
||||||
radiusFactor: 1,
|
|
||||||
centerY: 0.5,
|
|
||||||
minimum: 0,
|
|
||||||
maximum: 1,
|
|
||||||
ranges: [
|
|
||||||
GaugeRange(startValue: 0, endValue: 0.2, color: Colors.red),
|
|
||||||
GaugeRange(startValue: 0.2, endValue: 0.4, color: Colors.yellow),
|
|
||||||
GaugeRange(startValue: 0.4, endValue: 0.6, color: Colors.green),
|
|
||||||
GaugeRange(startValue: 0.6, endValue: 0.8, color: Colors.blue),
|
|
||||||
GaugeRange(startValue: 0.8, endValue: 1, color: Colors.purple),
|
|
||||||
],
|
|
||||||
pointers: [
|
|
||||||
NeedlePointer(
|
|
||||||
value: nerdStats.app,
|
|
||||||
enableAnimation: true,
|
|
||||||
needleLength: 0.9,
|
|
||||||
needleStartWidth: 2,
|
|
||||||
needleEndWidth: 15,
|
|
||||||
knobStyle: const KnobStyle(color: Colors.transparent),
|
|
||||||
gradient: const LinearGradient(colors: [Colors.transparent, Colors.white], begin: Alignment.bottomCenter, end: Alignment.topCenter, stops: [0.5, 1]),)
|
|
||||||
],
|
|
||||||
annotations: [
|
|
||||||
GaugeAnnotation(widget: Container(child:
|
|
||||||
RichText(
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
text: TextSpan(
|
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round"),
|
|
||||||
children: [
|
|
||||||
const TextSpan(text: "APP\n"),
|
|
||||||
TextSpan(text: f3.format(nerdStats.app), style: TextStyle(fontSize: 25, fontFamily: "Eurostile Round Extended", fontWeight: FontWeight.w100, color: getStatColor(nerdStats.app, averages?.nerdStats?.app, true))),
|
|
||||||
if (lbPos != null) TextSpan(text: lbPos!.app!.position >= 1000 ? "\n${t.top} ${f2.format(lbPos!.app!.percentage*100)}%" : "\n№${lbPos!.app!.position}", style: TextStyle(color: getColorOfRank(lbPos!.app!.position))),
|
|
||||||
if (oldNerdStats != null) TextSpan(text: "\n${comparef.format(nerdStats.app - oldNerdStats!.app)}", style: TextStyle(color: getDifferenceColor(nerdStats.app - oldNerdStats!.app)))
|
|
||||||
]
|
|
||||||
))),
|
|
||||||
angle: 270,positionFactor: 0.5
|
|
||||||
)],
|
|
||||||
),
|
|
||||||
RadialAxis(
|
|
||||||
startAngle: 20,
|
|
||||||
endAngle: 160,
|
|
||||||
isInversed: true,
|
|
||||||
showLabels: false,
|
|
||||||
showTicks: true,
|
|
||||||
radiusFactor: 1,
|
|
||||||
centerY: 0.5,
|
|
||||||
minimum: 1.8,
|
|
||||||
maximum: 2.4,
|
|
||||||
ranges: [
|
|
||||||
GaugeRange(startValue: 1.8, endValue: 2.0, color: Colors.green),
|
|
||||||
GaugeRange(startValue: 2.0, endValue: 2.2, color: Colors.blue),
|
|
||||||
GaugeRange(startValue: 2.2, endValue: 2.4, color: Colors.purple),
|
|
||||||
],
|
|
||||||
pointers: [
|
|
||||||
NeedlePointer(
|
|
||||||
value: nerdStats.vsapm,
|
|
||||||
enableAnimation: true,
|
|
||||||
needleLength: 0.9,
|
|
||||||
needleStartWidth: 2,
|
|
||||||
needleEndWidth: 15,
|
|
||||||
knobStyle: const KnobStyle(color: Colors.transparent),
|
|
||||||
gradient: const LinearGradient(colors: [Colors.transparent, Colors.white], begin: Alignment.bottomCenter, end: Alignment.topCenter, stops: [0.5, 1]),)
|
|
||||||
],
|
|
||||||
annotations: [
|
|
||||||
GaugeAnnotation(widget: Container(child:
|
|
||||||
RichText(
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
text: TextSpan(
|
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round"),
|
|
||||||
children: [
|
|
||||||
const TextSpan(text: "VS/APM\n"),
|
|
||||||
TextSpan(text: f3.format(nerdStats.vsapm), style: TextStyle(fontSize: 25, fontFamily: "Eurostile Round Extended", fontWeight: FontWeight.w100, color: getStatColor(nerdStats.vsapm, averages?.nerdStats?.vsapm, true))),
|
|
||||||
if (lbPos != null) TextSpan(text: lbPos!.vsapm!.position >= 1000 ? "\n${t.top} ${f2.format(lbPos!.vsapm!.percentage*100)}%" : "\n№${lbPos!.vsapm!.position}", style: TextStyle(color: getColorOfRank(lbPos!.vsapm!.position))),
|
|
||||||
if (oldNerdStats != null) TextSpan(text: "\n${comparef.format(nerdStats.vsapm - oldNerdStats!.vsapm)}", style: TextStyle(color: getDifferenceColor(nerdStats.vsapm - oldNerdStats!.vsapm))),
|
|
||||||
]
|
|
||||||
))),
|
|
||||||
angle: 90,positionFactor: 0.5
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
]
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Wrap(
|
|
||||||
alignment: WrapAlignment.center,
|
|
||||||
spacing: 10.0,
|
|
||||||
runSpacing: 10.0,
|
|
||||||
runAlignment: WrapAlignment.start,
|
|
||||||
children: [
|
|
||||||
GaugetThingy(value: nerdStats.dss, oldValue: oldNerdStats?.dss, min: 0, max: 1.0, tickInterval: .2, label: "DS/S", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true, avgValue: averages?.nerdStats?.dss, lbPos: lbPos?.dss),
|
|
||||||
GaugetThingy(value: nerdStats.dsp, oldValue: oldNerdStats?.dsp, min: 0, max: 1.0, tickInterval: .2, label: "DS/P", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true, avgValue: averages?.nerdStats?.dsp, lbPos: lbPos?.dsp),
|
|
||||||
GaugetThingy(value: nerdStats.appdsp, oldValue: oldNerdStats?.appdsp, min: 0, max: 1.2, tickInterval: .2, label: "APP+DS/P", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true, avgValue: averages?.nerdStats?.appdsp, lbPos: lbPos?.appdsp),
|
|
||||||
GaugetThingy(value: nerdStats.cheese, oldValue: oldNerdStats?.cheese, min: -80, max: 80, tickInterval: 40, label: "Cheese", sideSize: 128.0, fractionDigits: 2, moreIsBetter: false, lbPos: lbPos?.cheese),
|
|
||||||
GaugetThingy(value: nerdStats.gbe, oldValue: oldNerdStats?.gbe, min: 0, max: 1.0, tickInterval: .2, label: "GbE", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true, avgValue: averages?.nerdStats?.gbe, lbPos: lbPos?.gbe),
|
|
||||||
GaugetThingy(value: nerdStats.nyaapp, oldValue: oldNerdStats?.nyaapp, min: 0, max: 1.2, tickInterval: .2, label: "wAPP", sideSize: 128.0, fractionDigits: 3, moreIsBetter: true, avgValue: averages?.nerdStats?.nyaapp, lbPos: lbPos?.nyaapp),
|
|
||||||
GaugetThingy(value: nerdStats.area, oldValue: oldNerdStats?.area, min: 0, max: 1000, tickInterval: 100, label: "Area", sideSize: 128.0, fractionDigits: 1, moreIsBetter: true, avgValue: averages?.nerdStats?.area, lbPos: lbPos?.area),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
|
) : Center(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
big(),
|
||||||
|
manySmalls()
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,29 +1,16 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:tetra_stats/data_objects/record_single.dart';
|
import 'package:tetra_stats/data_objects/record_single.dart';
|
||||||
import 'package:tetra_stats/data_objects/singleplayer_stream.dart';
|
|
||||||
import 'package:tetra_stats/data_objects/tetrio_constants.dart';
|
import 'package:tetra_stats/data_objects/tetrio_constants.dart';
|
||||||
import 'package:tetra_stats/gen/strings.g.dart';
|
import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
import 'package:tetra_stats/utils/colors_functions.dart';
|
import 'package:tetra_stats/views/destination_home.dart';
|
||||||
import 'package:tetra_stats/utils/numers_formats.dart';
|
|
||||||
import 'package:tetra_stats/utils/open_in_browser.dart';
|
|
||||||
import 'package:tetra_stats/utils/relative_timestamps.dart';
|
|
||||||
import 'package:tetra_stats/utils/text_shadow.dart';
|
|
||||||
import 'package:tetra_stats/views/singleplayer_record_view.dart';
|
|
||||||
import 'package:tetra_stats/widgets/finesse_thingy.dart';
|
|
||||||
import 'package:tetra_stats/widgets/lineclears_thingy.dart';
|
|
||||||
import 'package:tetra_stats/widgets/sp_trailing_stats.dart';
|
|
||||||
import 'package:tetra_stats/widgets/stat_sell_num.dart';
|
|
||||||
import 'package:tetra_stats/widgets/text_timestamp.dart';
|
|
||||||
|
|
||||||
class SingleplayerRecord extends StatelessWidget {
|
class SingleplayerRecord extends StatelessWidget {
|
||||||
final RecordSingle? record;
|
final RecordSingle? record;
|
||||||
final SingleplayerStream? stream;
|
|
||||||
final String? rank;
|
final String? rank;
|
||||||
final bool hideTitle;
|
final bool hideTitle;
|
||||||
|
|
||||||
/// Widget that displays data from [record]
|
/// Widget that displays data from [record]
|
||||||
const SingleplayerRecord({super.key, required this.record, this.stream, this.rank, this.hideTitle = false});
|
const SingleplayerRecord({super.key, required this.record, this.rank, this.hideTitle = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -42,114 +29,6 @@ class SingleplayerRecord extends StatelessWidget {
|
||||||
blitzBetterThanClosestAverage = record!.stats.score > closestAverageBlitz.value;
|
blitzBetterThanClosestAverage = record!.stats.score > closestAverageBlitz.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return LayoutBuilder(
|
return RecordCard(record, [], record!.gamemode == "40l" ? sprintBetterThanRankAverage : blitzBetterThanRankAverage, record!.gamemode == "40l" ? closestAverageSprint : closestAverageBlitz, record!.gamemode == "40l" ? sprintBetterThanClosestAverage : blitzBetterThanClosestAverage, rank);
|
||||||
builder: (context, constraints) {
|
|
||||||
bool bigScreen = constraints.maxWidth > 768;
|
|
||||||
return SingleChildScrollView(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
if (record!.gamemode == "40l") Padding(padding: const EdgeInsets.only(right: 8.0),
|
|
||||||
child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageSprint.key}.png", height: 96)
|
|
||||||
),
|
|
||||||
if (record!.gamemode == "blitz") Padding(padding: const EdgeInsets.only(right: 8.0),
|
|
||||||
child: Image.asset("res/tetrio_tl_alpha_ranks/${closestAverageBlitz.key}.png", height: 96)
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
if (record!.gamemode == "40l" && !hideTitle) Text(t.sprint, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
|
||||||
if (record!.gamemode == "blitz" && !hideTitle) Text(t.blitz, style: const TextStyle(height: 0.1, fontFamily: "Eurostile Round Extended", fontSize: 18)),
|
|
||||||
RichText(text: TextSpan(
|
|
||||||
text: record!.gamemode == "40l" ? get40lTime(record!.stats.finalTime.inMicroseconds) : NumberFormat.decimalPattern().format(record!.stats.score),
|
|
||||||
style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: bigScreen ? 36 : 32, fontWeight: FontWeight.w500, color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
RichText(text: TextSpan(
|
|
||||||
text: "",
|
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
|
|
||||||
children: [
|
|
||||||
if (record!.gamemode == "40l" && (rank != null && rank != "z")) TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(record!.stats.finalTime, sprintAverages[rank]!), verdict: sprintBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
|
||||||
color: sprintBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent
|
|
||||||
))
|
|
||||||
else if (record!.gamemode == "40l" && (rank == null || rank == "z" || rank != "x+")) TextSpan(text: "${t.verdictGeneral(n: readableTimeDifference(record!.stats.finalTime, closestAverageSprint.value), verdict: sprintBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageSprint.key.toUpperCase())}\n", style: TextStyle(
|
|
||||||
color: sprintBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
|
|
||||||
))
|
|
||||||
else if (record!.gamemode == "blitz" && (rank != null && rank != "z")) TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(record!.stats.score, blitzAverages[rank]!), verdict: blitzBetterThanRankAverage??false ? t.verdictBetter : t.verdictWorse, rank: rank!.toUpperCase())}\n", style: TextStyle(
|
|
||||||
color: blitzBetterThanRankAverage??false ? Colors.greenAccent : Colors.redAccent
|
|
||||||
))
|
|
||||||
else if (record!.gamemode == "blitz" && (rank == null || rank == "z" || rank != "x+")) TextSpan(text: "${t.verdictGeneral(n: readableIntDifference(record!.stats.score, closestAverageBlitz.value), verdict: blitzBetterThanClosestAverage ? t.verdictBetter : t.verdictWorse, rank: closestAverageBlitz.key.toUpperCase())}\n", style: TextStyle(
|
|
||||||
color: blitzBetterThanClosestAverage ? Colors.greenAccent : Colors.redAccent
|
|
||||||
)),
|
|
||||||
if (record!.rank != -1) TextSpan(text: "№${record!.rank}", style: TextStyle(color: getColorOfRank(record!.rank))),
|
|
||||||
if (record!.rank != -1) const TextSpan(text: " • "),
|
|
||||||
TextSpan(text: timestamp(record!.timestamp)),
|
|
||||||
]
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (record!.gamemode == "40l") Wrap(
|
|
||||||
alignment: WrapAlignment.spaceBetween,
|
|
||||||
spacing: 20,
|
|
||||||
children: [ // TODO: replace
|
|
||||||
StatCellNum(playerStat: record!.stats.piecesPlaced, playerStatLabel: t.statCellNum.pieces, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
|
||||||
StatCellNum(playerStat: record!.stats.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
|
||||||
StatCellNum(playerStat: record!.stats.kpp, playerStatLabel: t.statCellNum.kpp, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (record!.gamemode == "blitz") Wrap(
|
|
||||||
alignment: WrapAlignment.spaceBetween,
|
|
||||||
crossAxisAlignment: WrapCrossAlignment.start,
|
|
||||||
spacing: 20,
|
|
||||||
children: [
|
|
||||||
StatCellNum(playerStat: record!.stats.level, playerStatLabel: t.statCellNum.level, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
|
||||||
StatCellNum(playerStat: record!.stats.pps, playerStatLabel: t.statCellNum.pps, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true, smallDecimal: false),
|
|
||||||
StatCellNum(playerStat: record!.stats.spp, playerStatLabel: t.statCellNum.spp, fractionDigits: 2, isScreenBig: bigScreen, higherIsBetter: true)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
FinesseThingy(record?.stats.finesse, record?.stats.finessePercentage),
|
|
||||||
LineclearsThingy(record!.stats.clears, record!.stats.lines, record!.stats.holds, record!.stats.tSpins),
|
|
||||||
if (record!.gamemode == "40l") Text("${record!.stats.inputs} KP • ${f2.format(record!.stats.kps)} KPS"),
|
|
||||||
if (record!.gamemode == "blitz") Text("${record!.stats.piecesPlaced} P • ${record!.stats.inputs} KP • ${f2.format(record!.stats.kpp)} KPP • ${f2.format(record!.stats.kps)} KPS"),
|
|
||||||
if (record != null) Wrap(
|
|
||||||
alignment: WrapAlignment.spaceBetween,
|
|
||||||
crossAxisAlignment: WrapCrossAlignment.start,
|
|
||||||
spacing: 20,
|
|
||||||
children: [
|
|
||||||
TextButton(onPressed: (){launchInBrowser(Uri.parse("https://tetr.io/#r:${record!.replayId}"));}, child: Text(t.openSPreplay)),
|
|
||||||
TextButton(onPressed: (){launchInBrowser(Uri.parse("https://inoue.szy.lol/api/replay/${record!.replayId}"));}, child: Text(t.downloadSPreplay)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (stream != null && stream!.records.length > 1) for(int i = 1; i < stream!.records.length; i++) ListTile(
|
|
||||||
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => SingleplayerRecordView(record: stream!.records[i]))),
|
|
||||||
leading: Text("#${i+1}",
|
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 28, shadows: textShadow, height: 0.9)
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
switch (stream!.records[i].gamemode){
|
|
||||||
"40l" => get40lTime(stream!.records[i].stats.finalTime.inMicroseconds),
|
|
||||||
"blitz" => t.blitzScore(p: NumberFormat.decimalPattern().format(stream!.records[i].stats.score)),
|
|
||||||
"5mblast" => get40lTime(stream!.records[i].stats.finalTime.inMicroseconds),
|
|
||||||
String() => "huh",
|
|
||||||
},
|
|
||||||
style: Theme.of(context).textTheme.displayLarge),
|
|
||||||
subtitle: Text(timestamp(stream!.records[i].timestamp), style: const TextStyle(color: Colors.grey, height: 0.85)),
|
|
||||||
trailing: SpTrailingStats(stream!.records[i], stream!.records[i].gamemode)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -40,9 +40,10 @@ class TLRatingThingy extends StatelessWidget{
|
||||||
? Image.asset("res/icons/kagari.png", height: 128) // Btw why she wearing Kazamatsuri high school uniform?
|
? Image.asset("res/icons/kagari.png", height: 128) // Btw why she wearing Kazamatsuri high school uniform?
|
||||||
: Image.asset("res/tetrio_tl_alpha_ranks/${tlData.rank}.png", height: 128),
|
: Image.asset("res/tetrio_tl_alpha_ranks/${tlData.rank}.png", height: 128),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: bigScreen ? CrossAxisAlignment.start : CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
RichText(
|
RichText(
|
||||||
|
textAlign: bigScreen ? TextAlign.start : TextAlign.center,
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 20, color: Colors.white, height: 0.9),
|
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 20, color: Colors.white, height: 0.9),
|
||||||
children: (tlData.gamesPlayed > 9) ? switch(prefs.getInt("ratingMode")){
|
children: (tlData.gamesPlayed > 9) ? switch(prefs.getInt("ratingMode")){
|
||||||
|
@ -125,7 +126,7 @@ class TLRatingThingy extends StatelessWidget{
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (showPositions == true) RichText(
|
if (showPositions == true) RichText(
|
||||||
textAlign: TextAlign.start,
|
textAlign: bigScreen ? TextAlign.start : TextAlign.center,
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
text: "",
|
text: "",
|
||||||
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
|
style: const TextStyle(fontFamily: "Eurostile Round", fontSize: 14, color: Colors.grey),
|
||||||
|
|
|
@ -17,8 +17,35 @@ class TetraLeagueThingy extends StatelessWidget{
|
||||||
final Cutoffs? cutoffs;
|
final Cutoffs? cutoffs;
|
||||||
final CutoffTetrio? averages;
|
final CutoffTetrio? averages;
|
||||||
final PlayerLeaderboardPosition? lbPos;
|
final PlayerLeaderboardPosition? lbPos;
|
||||||
|
final double width;
|
||||||
|
|
||||||
const TetraLeagueThingy({super.key, required this.league, this.toCompare, this.cutoffs, this.averages, this.lbPos});
|
const TetraLeagueThingy({super.key, required this.league, this.toCompare, this.cutoffs, this.averages, this.lbPos, this.width = double.infinity});
|
||||||
|
|
||||||
|
List<TableRow> secondColumn(){
|
||||||
|
return [
|
||||||
|
TableRow(children: [
|
||||||
|
//Text("APM: ", style: TextStyle(fontSize: 21)),
|
||||||
|
Text(intf.format(league.gamesPlayed), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
|
||||||
|
const Text(" Games", style: TextStyle(fontSize: 21)),
|
||||||
|
if (toCompare != null) Text(" (${comparef2.format(league.gamesPlayed-toCompare!.gamesPlayed)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: Colors.grey)),
|
||||||
|
if (lbPos != null) Text(lbPos?.gamesPlayed != null ? (lbPos!.gamesPlayed!.position >= 1000 ? " (${t.top} ${f2.format(lbPos!.gamesPlayed!.percentage*100)}%)" : " (№ ${lbPos!.gamesPlayed!.position})") : "(№ ---)", style: TextStyle(color: lbPos?.gamesPlayed != null ? getColorOfRank(lbPos!.gamesPlayed!.position) : null))
|
||||||
|
]),
|
||||||
|
TableRow(children: [
|
||||||
|
//Text("PPS: ", style: TextStyle(fontSize: 21)),
|
||||||
|
Text(intf.format(league.gamesWon), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
|
||||||
|
const Text(" Won", style: TextStyle(fontSize: 21)),
|
||||||
|
if (toCompare != null) Text(" (${comparef2.format(league.gamesWon-toCompare!.gamesWon)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: Colors.grey)),
|
||||||
|
if (lbPos != null) Text(lbPos?.gamesWon != null ? (lbPos!.gamesWon!.position >= 1000 ? " (${t.top} ${f2.format(lbPos!.gamesWon!.percentage*100)}%)" : " (№ ${lbPos!.gamesWon!.position})") : "(№ ---)", style: TextStyle(color: lbPos?.gamesWon != null ? getColorOfRank(lbPos!.gamesWon!.position) : null))
|
||||||
|
]),
|
||||||
|
TableRow(children: [
|
||||||
|
//Text("VS: ", style: TextStyle(fontSize: 21)),
|
||||||
|
Tooltip(child: Text("${league.gxe.isNegative ? "---" : f3.format(league.gxe)}", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: league.gxe.isNegative ? Colors.grey : Colors.white)), message: "${f2.format(league.s1tr)} S1 TR"),
|
||||||
|
Tooltip(child: Text(" GXE", style: TextStyle(fontSize: 21, color: league.gxe.isNegative ? Colors.grey : Colors.white)), message: "Glixare"),
|
||||||
|
if (toCompare != null) Text(" (${comparef.format(league.gxe-toCompare!.gxe)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: getDifferenceColor(league.gxe-toCompare!.gxe))),
|
||||||
|
if (lbPos != null) Text(lbPos?.glixare != null ? (lbPos!.glixare!.position >= 1000 ? " (${t.top} ${f2.format(lbPos!.glixare!.percentage*100)}%)" : " (№ ${lbPos!.glixare!.position})") : "(№ ---)", style: TextStyle(color: lbPos?.glixare != null ? getColorOfRank(lbPos!.glixare!.position) : null))
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -37,8 +64,6 @@ class TetraLeagueThingy extends StatelessWidget{
|
||||||
nextRankGlickoCutoff: cutoffs != null ? (league.rank != "z" ? league.rank == "x+" : league.percentileRank == "x+") ? 25000 : cutoffs!.glicko[ranks.elementAtOrNull(ranks.indexOf(league.rank != "z" ? league.rank : league.percentileRank)+1)] : null,
|
nextRankGlickoCutoff: cutoffs != null ? (league.rank != "z" ? league.rank == "x+" : league.percentileRank == "x+") ? 25000 : cutoffs!.glicko[ranks.elementAtOrNull(ranks.indexOf(league.rank != "z" ? league.rank : league.percentileRank)+1)] : null,
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
// spacing: 25.0,
|
|
||||||
// alignment: WrapAlignment.spaceAround,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -65,41 +90,26 @@ class TetraLeagueThingy extends StatelessWidget{
|
||||||
Text(" VS", style: TextStyle(fontSize: 21, color: league.vs != null ? getStatColor(league.vs!, averages?.vs, true) : Colors.grey)),
|
Text(" VS", style: TextStyle(fontSize: 21, color: league.vs != null ? getStatColor(league.vs!, averages?.vs, true) : Colors.grey)),
|
||||||
if (toCompare != null) Text(" (${comparef2.format(league.vs!-toCompare!.vs!)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: getDifferenceColor(league.vs!-toCompare!.vs!))),
|
if (toCompare != null) Text(" (${comparef2.format(league.vs!-toCompare!.vs!)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: getDifferenceColor(league.vs!-toCompare!.vs!))),
|
||||||
if (lbPos != null) Text(lbPos?.vs != null ? (lbPos!.vs!.position >= 1000 ? " (${t.top} ${f2.format(lbPos!.vs!.percentage*100)}%)" : " (№ ${lbPos!.vs!.position})") : "(№ ---)", style: TextStyle(color: lbPos?.vs != null ? getColorOfRank(lbPos!.vs!.position) : null))
|
if (lbPos != null) Text(lbPos?.vs != null ? (lbPos!.vs!.position >= 1000 ? " (${t.top} ${f2.format(lbPos!.vs!.percentage*100)}%)" : " (№ ${lbPos!.vs!.position})") : "(№ ---)", style: TextStyle(color: lbPos?.vs != null ? getColorOfRank(lbPos!.vs!.position) : null))
|
||||||
])
|
]),
|
||||||
|
if (width <= 600) TableRow(children: [
|
||||||
|
Text(!league.winrate.isNegative ? percentage.format(league.winrate) : "---", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: !league.winrate.isNegative ? Colors.white : Colors.grey)),
|
||||||
|
Text(" WR", style: TextStyle(fontSize: 21, color: !league.winrate.isNegative ? Colors.white : Colors.grey)),
|
||||||
|
if (toCompare != null) Text(" (${comparef2.format((league.winrate-toCompare!.winrate)*100)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: getDifferenceColor(league.winrate-toCompare!.winrate))),
|
||||||
|
if (lbPos != null) Text(lbPos?.winrate != null ? (lbPos!.winrate!.position >= 1000 ? " (${t.top} ${f2.format(lbPos!.winrate!.percentage*100)}%)" : " (№ ${lbPos!.winrate!.position})") : "(№ ---)", style: TextStyle(color: lbPos?.winrate != null ? getColorOfRank(lbPos!.winrate!.position) : null))
|
||||||
|
]),
|
||||||
|
if (width <= 400) ...secondColumn()
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
GaugetThingy(value: league.winrate, min: 0, max: 1, tickInterval: 0.25, label: "Winrate", sideSize: 128, fractionDigits: 2, moreIsBetter: true, oldValue: toCompare?.winrate, percentileFormat: true, lbPos: lbPos?.winrate),
|
if (width > 600) GaugetThingy(value: league.winrate, min: 0, max: 1, tickInterval: 0.25, label: "Winrate", sideSize: 128, fractionDigits: 2, moreIsBetter: true, oldValue: toCompare?.winrate, percentileFormat: true, lbPos: lbPos?.winrate),
|
||||||
Expanded(
|
if (width > 400) Expanded(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Table(
|
child: Table(
|
||||||
defaultVerticalAlignment: TableCellVerticalAlignment.baseline,
|
defaultVerticalAlignment: TableCellVerticalAlignment.baseline,
|
||||||
textBaseline: TextBaseline.alphabetic,
|
textBaseline: TextBaseline.alphabetic,
|
||||||
defaultColumnWidth:const IntrinsicColumnWidth(),
|
defaultColumnWidth:const IntrinsicColumnWidth(),
|
||||||
children: [
|
children: secondColumn(),
|
||||||
TableRow(children: [
|
|
||||||
//Text("APM: ", style: TextStyle(fontSize: 21)),
|
|
||||||
Text(intf.format(league.gamesPlayed), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
|
|
||||||
const Text(" Games", style: TextStyle(fontSize: 21)),
|
|
||||||
if (toCompare != null) Text(" (${comparef2.format(league.gamesPlayed-toCompare!.gamesPlayed)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: Colors.grey)),
|
|
||||||
if (lbPos != null) Text(lbPos?.gamesPlayed != null ? (lbPos!.gamesPlayed!.position >= 1000 ? " (${t.top} ${f2.format(lbPos!.gamesPlayed!.percentage*100)}%)" : " (№ ${lbPos!.gamesPlayed!.position})") : "(№ ---)", style: TextStyle(color: lbPos?.gamesPlayed != null ? getColorOfRank(lbPos!.gamesPlayed!.position) : null))
|
|
||||||
]),
|
|
||||||
TableRow(children: [
|
|
||||||
//Text("PPS: ", style: TextStyle(fontSize: 21)),
|
|
||||||
Text(intf.format(league.gamesWon), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
|
|
||||||
const Text(" Won", style: TextStyle(fontSize: 21)),
|
|
||||||
if (toCompare != null) Text(" (${comparef2.format(league.gamesWon-toCompare!.gamesWon)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: Colors.grey)),
|
|
||||||
if (lbPos != null) Text(lbPos?.gamesWon != null ? (lbPos!.gamesWon!.position >= 1000 ? " (${t.top} ${f2.format(lbPos!.gamesWon!.percentage*100)}%)" : " (№ ${lbPos!.gamesWon!.position})") : "(№ ---)", style: TextStyle(color: lbPos?.gamesWon != null ? getColorOfRank(lbPos!.gamesWon!.position) : null))
|
|
||||||
]),
|
|
||||||
TableRow(children: [
|
|
||||||
//Text("VS: ", style: TextStyle(fontSize: 21)),
|
|
||||||
Tooltip(child: Text("${league.gxe.isNegative ? "---" : f3.format(league.gxe)}", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: league.gxe.isNegative ? Colors.grey : Colors.white)), message: "${f2.format(league.s1tr)} S1 TR"),
|
|
||||||
Tooltip(child: Text(" GXE", style: TextStyle(fontSize: 21, color: league.gxe.isNegative ? Colors.grey : Colors.white)), message: "Glixare"),
|
|
||||||
if (toCompare != null) Text(" (${comparef.format(league.gxe-toCompare!.gxe)})", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: getDifferenceColor(league.gxe-toCompare!.gxe))),
|
|
||||||
if (lbPos != null) Text(lbPos?.glixare != null ? (lbPos!.glixare!.position >= 1000 ? " (${t.top} ${f2.format(lbPos!.glixare!.percentage*100)}%)" : " (№ ${lbPos!.glixare!.position})") : "(№ ---)", style: TextStyle(color: lbPos?.glixare != null ? getColorOfRank(lbPos!.glixare!.position) : null))
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -9,8 +9,51 @@ import 'package:tetra_stats/widgets/text_timestamp.dart';
|
||||||
class ZenithThingy extends StatelessWidget{
|
class ZenithThingy extends StatelessWidget{
|
||||||
final RecordSingle? zenith;
|
final RecordSingle? zenith;
|
||||||
final bool old;
|
final bool old;
|
||||||
|
final double width;
|
||||||
|
|
||||||
const ZenithThingy({super.key, required this.zenith, this.old = false});
|
const ZenithThingy({super.key, required this.zenith, this.old = false, this.width = double.infinity});
|
||||||
|
|
||||||
|
List<TableRow> secondColumn(){
|
||||||
|
return [
|
||||||
|
TableRow(children: [
|
||||||
|
Text(intf.format(zenith!.stats.kills), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
|
||||||
|
const Text(" KO's", style: TextStyle(fontSize: 21))
|
||||||
|
]),
|
||||||
|
TableRow(children: [
|
||||||
|
Text(zenith!.stats.topBtB.toString(), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
|
||||||
|
const Text(" B2B", style: TextStyle(fontSize: 21))
|
||||||
|
]),
|
||||||
|
TableRow(children: [
|
||||||
|
Text(zenith!.stats.garbage.maxspike_nomult.toString(), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
|
||||||
|
const Text(" Top spike", style: TextStyle(fontSize: 21))
|
||||||
|
]),
|
||||||
|
if (width <= 600) TableRow(children: [
|
||||||
|
Text(f2.format(zenith!.stats.zenith!.peakrank), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
|
||||||
|
const Text(" Peak CSP", style: TextStyle(fontSize: 21)),
|
||||||
|
])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TableRow> noRecordSecondColumn(){
|
||||||
|
return [
|
||||||
|
const TableRow(children: [
|
||||||
|
Text("---", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: Colors.grey)),
|
||||||
|
Text(" KO's", style: TextStyle(fontSize: 21, color: Colors.grey))
|
||||||
|
]),
|
||||||
|
const TableRow(children: [
|
||||||
|
Text("---", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: Colors.grey)),
|
||||||
|
Text(" B2B", style: TextStyle(fontSize: 21, color: Colors.grey))
|
||||||
|
]),
|
||||||
|
const TableRow(children: [
|
||||||
|
Text("---", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: Colors.grey)),
|
||||||
|
Text(" Top spike", style: TextStyle(fontSize: 21, color: Colors.grey))
|
||||||
|
]),
|
||||||
|
if (width <= 600) TableRow(children: [
|
||||||
|
Text("-.--", textAlign: TextAlign.right, style: const TextStyle(fontSize: 21, color: Colors.grey)),
|
||||||
|
const Text(" Peak CSP", style: TextStyle(fontSize: 21, color: Colors.grey)),
|
||||||
|
])
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -68,30 +111,22 @@ class ZenithThingy extends StatelessWidget{
|
||||||
TableRow(children: [
|
TableRow(children: [
|
||||||
Text(f2.format(zenith!.aggregateStats.vs), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
|
Text(f2.format(zenith!.aggregateStats.vs), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
|
||||||
const Text(" VS", style: TextStyle(fontSize: 21)),
|
const Text(" VS", style: TextStyle(fontSize: 21)),
|
||||||
])
|
]),
|
||||||
|
if (width <= 600) TableRow(children: [
|
||||||
|
Text(f2.format(zenith!.stats.cps), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
|
||||||
|
const Text(" CSP", style: TextStyle(fontSize: 21)),
|
||||||
|
]),
|
||||||
|
if (width <= 400) ...secondColumn().reversed
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
GaugetThingy(value: zenith!.stats.cps, min: 0, max: 12, tickInterval: 3, label: "Climb\nSpeed", subString: "Peak: ${f2.format(zenith!.stats.zenith!.peakrank)}", sideSize: 128, fractionDigits: 2, moreIsBetter: true),
|
if (width > 600) GaugetThingy(value: zenith!.stats.cps, min: 0, max: 12, tickInterval: 3, label: "Climb\nSpeed", subString: "Peak: ${f2.format(zenith!.stats.zenith!.peakrank)}", sideSize: 128, fractionDigits: 2, moreIsBetter: true),
|
||||||
Expanded(
|
if (width > 400) Expanded(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Table(
|
child: Table(
|
||||||
defaultColumnWidth:const IntrinsicColumnWidth(),
|
defaultColumnWidth:const IntrinsicColumnWidth(),
|
||||||
children: [
|
children: secondColumn(),
|
||||||
TableRow(children: [
|
|
||||||
Text(intf.format(zenith!.stats.kills), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
|
|
||||||
const Text(" KO's", style: TextStyle(fontSize: 21))
|
|
||||||
]),
|
|
||||||
TableRow(children: [
|
|
||||||
Text(zenith!.stats.topBtB.toString(), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
|
|
||||||
const Text(" B2B", style: TextStyle(fontSize: 21))
|
|
||||||
]),
|
|
||||||
TableRow(children: [
|
|
||||||
Text(zenith!.stats.garbage.maxspike_nomult.toString(), textAlign: TextAlign.right, style: const TextStyle(fontSize: 21)),
|
|
||||||
const Text(" Top spike", style: TextStyle(fontSize: 21))
|
|
||||||
])
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -114,30 +149,21 @@ class ZenithThingy extends StatelessWidget{
|
||||||
const TableRow(children: [
|
const TableRow(children: [
|
||||||
Text("-.--", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: Colors.grey)),
|
Text("-.--", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: Colors.grey)),
|
||||||
Text(" VS", style: TextStyle(fontSize: 21, color: Colors.grey)),
|
Text(" VS", style: TextStyle(fontSize: 21, color: Colors.grey)),
|
||||||
|
]),
|
||||||
|
if (width <= 600) TableRow(children: [
|
||||||
|
Text("-.--", textAlign: TextAlign.right, style: const TextStyle(fontSize: 21, color: Colors.grey)),
|
||||||
|
const Text(" CSP", style: TextStyle(fontSize: 21, color: Colors.grey)),
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
GaugetThingy(value: null, min: 0, max: 12, tickInterval: 3, label: "Climb\nSpeed", subString: "Peak: ---", sideSize: 128, fractionDigits: 0, moreIsBetter: true),
|
if (width > 600) GaugetThingy(value: null, min: 0, max: 12, tickInterval: 3, label: "Climb\nSpeed", subString: "Peak: ---", sideSize: 128, fractionDigits: 0, moreIsBetter: true),
|
||||||
Expanded(
|
if (width > 400) Expanded(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Table(
|
child: Table(
|
||||||
defaultColumnWidth: IntrinsicColumnWidth(),
|
defaultColumnWidth: IntrinsicColumnWidth(),
|
||||||
children: [
|
children: noRecordSecondColumn(),
|
||||||
const TableRow(children: [
|
|
||||||
Text("---", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: Colors.grey)),
|
|
||||||
Text(" KO's", style: TextStyle(fontSize: 21, color: Colors.grey))
|
|
||||||
]),
|
|
||||||
const TableRow(children: [
|
|
||||||
Text("---", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: Colors.grey)),
|
|
||||||
Text(" B2B", style: TextStyle(fontSize: 21, color: Colors.grey))
|
|
||||||
]),
|
|
||||||
const TableRow(children: [
|
|
||||||
Text("---", textAlign: TextAlign.right, style: TextStyle(fontSize: 21, color: Colors.grey)),
|
|
||||||
Text(" Top spike", style: TextStyle(fontSize: 21, color: Colors.grey))
|
|
||||||
])
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
16
pubspec.lock
16
pubspec.lock
|
@ -278,6 +278,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.1"
|
version: "0.13.1"
|
||||||
|
flutter_layout_grid:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_layout_grid
|
||||||
|
sha256: "88b4f8484a0874962e27c47733ad256aeb26acc694a9f029edbef771d301885a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.7"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -645,6 +653,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.4"
|
||||||
|
quiver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: quiver
|
||||||
|
sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.2"
|
||||||
screen_retriever:
|
screen_retriever:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -43,6 +43,7 @@ dependencies:
|
||||||
window_manager: ^0.3.7
|
window_manager: ^0.3.7
|
||||||
flutter_markdown: ^0.6.18
|
flutter_markdown: ^0.6.18
|
||||||
flutter_colorpicker: ^1.0.3
|
flutter_colorpicker: ^1.0.3
|
||||||
|
flutter_layout_grid: ^2.0.0
|
||||||
go_router: ^13.0.0
|
go_router: ^13.0.0
|
||||||
syncfusion_flutter_charts: ^24.2.9
|
syncfusion_flutter_charts: ^24.2.9
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue