I guess I finally found a solution to perfomance issue and replacement for fl_chart

I wish i wasn't that stupid
This commit is contained in:
dan63047 2024-04-30 01:45:11 +03:00
parent 5c6c502a57
commit 8081755e71
7 changed files with 98 additions and 75 deletions

View File

@ -42,7 +42,7 @@ const Map<String, double> rankCutoffs = {
"": 0.5
};
const Map<String, double> rankTargets = {
"x": 24008,
"x": 24503.75, // where that comes from?
"u": 23038,
"ss": 21583,
"s+": 20128,

View File

@ -185,15 +185,17 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
topTR = requests.elementAtOrNull(3) as double?; // No TR - no Top TR
meAmongEveryone = teto.getCachedLeaderboardPositions(me.userId);
if (meAmongEveryone == null && prefs.getBool("showPositions") == true){
if (prefs.getBool("showPositions") == true){
// Get tetra League leaderboard
everyone = teto.getCachedLeaderboard();
everyone ??= await teto.fetchTLLeaderboard();
if (meAmongEveryone == null){
meAmongEveryone = await compute(everyone!.getLeaderboardPosition, me);
if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!);
if (me.tlSeason1.rank != "z") {
thatRankCutoff = everyone!.cutoffs[me.tlSeason1.rank];
nextRankCutoff = everyone!.cutoffs[ranks.indexOf(me.tlSeason1.rank)+1];
}
if (me.tlSeason1.gamesPlayed > 9) {
thatRankCutoff = everyone!.cutoffs[me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank];
nextRankCutoff = everyone!.cutoffs[ranks.elementAtOrNull(ranks.indexOf(me.tlSeason1.rank != "z" ? me.tlSeason1.rank : me.tlSeason1.percentileRank)+1)];
nextRankCutoff = nextRankCutoff??25000;
}
}
@ -515,7 +517,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
thatRankCutoff: thatRankCutoff,
thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null,
nextRankCutoff: nextRankCutoff,
nextRankTarget: snapshot.data![0].tlSeason1.rank != "z" || snapshot.data![0].tlSeason1.rank != "x" ? rankTargets[ranks.indexOf(snapshot.data![0].tlSeason1.rank)+1] : null,
nextRankTarget: snapshot.data![0].tlSeason1.rank != "z" || snapshot.data![0].tlSeason1.rank != "x" ? rankTargets[ranks.elementAtOrNull(ranks.indexOf(snapshot.data![0].tlSeason1.rank)+1)] : null,
averages: rankAverages,
lbPositions: meAmongEveryone
),

View File

@ -1,5 +1,6 @@
import 'dart:io';
import 'dart:math';
import 'dart:ui';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
@ -10,10 +11,13 @@ import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/views/main_view.dart' show MainView;
import 'package:tetra_stats/utils/text_shadow.dart';
import 'package:window_manager/window_manager.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:syncfusion_flutter_charts/sparkcharts.dart';
var _chartsShortTitlesDropdowns = <DropdownMenuItem>[for (MapEntry e in chartsShortTitles.entries) DropdownMenuItem(value: e.key, child: Text(e.value),)];
Stats _chartsX = Stats.tr;
Stats _chartsY = Stats.apm;
late TooltipBehavior _tooltipBehavior;
List<DropdownMenuItem> _itemStats = [for (MapEntry e in chartsShortTitles.entries) DropdownMenuItem(value: e.key, child: Text(e.value))];
List<_MyScatterSpot> _spots = [];
Stats _sortBy = Stats.tr;
@ -49,7 +53,7 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
late double yScale;
String headerTooltip = t.pseudoTooltipHeaderInit;
String footerTooltip = t.pseudoTooltipFooterInit;
int hoveredPointId = -1;
ValueNotifier<int> hoveredPointId = ValueNotifier<int>(-1);
double scaleFactor = 5e2;
double dragFactor = 7e2;
@ -57,6 +61,32 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
void initState() {
_scrollController = ScrollController();
_tabController = TabController(length: 6, vsync: this);
_tooltipBehavior = TooltipBehavior(
enable: true,
animationDuration: 0,
builder: (dynamic data, dynamic point, dynamic series,
int pointIndex, int seriesIndex) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
"${data.nickname} (${data.rank.toUpperCase()})",
style: TextStyle(color: Colors.black, fontFamily: "Eurostile Round Extended", fontSize: 20),
),
),
Text(
'${_f4.format(data.x)} ${chartsShortTitles[_chartsX]}\n${_f4.format(data.y)} ${chartsShortTitles[_chartsY]}',
style: TextStyle(color: Colors.black),
),
],
),
);
}
);
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){
windowManager.getTitle().then((value) => _oldWindowTitle = value);
windowManager.setTitle("Tetra Stats: ${widget.rank[1]["everyone"] ? t.everyoneAverages : t.rankAverages(rank: widget.rank[0].rank.toUpperCase())}");
@ -113,7 +143,9 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
entry.getStatByEnum(_chartsY).toDouble(),
entry.userId,
entry.username,
dotPainter: FlDotCirclePainter(color: rankColors[entry.rank]??Colors.white, radius: 3))
entry.rank,
rankColors[entry.rank]??Colors.white
)
];
}
@ -328,60 +360,19 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
recalculateScales();
});
},
// TODO: Figure out wtf is going on with gestures
child: Padding(
padding: bigScreen ? const EdgeInsets.fromLTRB(40, 40, 40, 48) : const EdgeInsets.fromLTRB(0, 40, 16, 48),
child: Stack(
children: [
ScatterChart(
key: graphKey,
ScatterChartData(
minX: minX,
maxX: maxX,
minY: minY,
maxY: maxY,
clipData: const FlClipData.all(),
scatterSpots: _spots,
scatterTouchData: ScatterTouchData(
handleBuiltInTouches: false,
touchCallback:(touchEvent, touchResponse) {
if (touchEvent is FlPanUpdateEvent){
dragHandler(touchEvent.details);
return;
}
if (touchEvent is FlPointerHoverEvent){
setState(() {
if (touchResponse?.touchedSpot == null) {
hoveredPointId = -1;
} else {
hoveredPointId = touchResponse!.touchedSpot!.spotIndex;
_MyScatterSpot castedPoint = touchResponse.touchedSpot!.spot as _MyScatterSpot;
headerTooltip = castedPoint.nickname;
footerTooltip = "${_f4.format(castedPoint.x)} ${chartsShortTitles[_chartsX]}; ${_f4.format(castedPoint.y)} ${chartsShortTitles[_chartsY]}";
}
});
}
if (touchEvent is FlPointerExitEvent){
setState(() {hoveredPointId = -1;});
}
if (touchEvent is FlTapUpEvent && touchResponse?.touchedSpot?.spot != null){
_MyScatterSpot spot = touchResponse!.touchedSpot!.spot as _MyScatterSpot;
Navigator.push(context, MaterialPageRoute(builder: (context) => MainView(player: spot.nickname), maintainState: false));
}
},
),
),
swapAnimationDuration: const Duration(milliseconds: 150), // Optional
swapAnimationCurve: Curves.linear, // Optional
),
Padding(
padding: EdgeInsets.fromLTRB(graphStartX+8, padding.top/2+8, 0, 0),
child: Column(
children: [
AnimatedDefaultTextStyle(style: TextStyle(fontFamily: "Eurostile Round Extended", fontSize: 24, color: Color.fromARGB(hoveredPointId == -1 ? 100 : 255, 255, 255, 255), shadows: hoveredPointId != -1 ? textShadow : null), duration: Durations.medium1, curve: Curves.elasticInOut, child: Text(headerTooltip)),
AnimatedDefaultTextStyle(style: TextStyle(fontFamily: "Eurostile Round", color: Color.fromARGB(hoveredPointId == -1 ? 100 : 255, 255, 255, 255), shadows: hoveredPointId != -1 ? textShadow : null), duration: Durations.medium1, curve: Curves.elasticInOut, child: Text(footerTooltip)),
],
),
child: SfCartesianChart(
tooltipBehavior: _tooltipBehavior,
//primaryXAxis: CategoryAxis(),
series: [
ScatterSeries(
enableTooltip: true,
dataSource: _spots,
animationDuration: 0,
pointColorMapper: (data, _) => data.color,
xValueMapper: (data, _) => data.x,
yValueMapper: (data, _) => data.y
)
],
),
@ -642,10 +633,12 @@ class _ListEntry extends StatelessWidget {
}
}
class _MyScatterSpot extends ScatterSpot {
class _MyScatterSpot{
num x;
num y;
String id;
String nickname;
//Color color;
//FlDotPainter painter = FlDotCirclePainter(color: color, radius: 2);
_MyScatterSpot(super.x, super.y, this.id, this.nickname, {super.dotPainter});
String rank;
Color color;
_MyScatterSpot(this.x, this.y, this.id, this.nickname, this.rank, this.color);
}

View File

@ -1,8 +1,11 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:syncfusion_flutter_gauges/gauges.dart';
import 'package:tetra_stats/data_objects/tetrio.dart';
import 'package:tetra_stats/gen/strings.g.dart';
import 'package:tetra_stats/utils/numers_formats.dart';
class TLProgress extends StatelessWidget{
final double tr;
@ -20,16 +23,15 @@ class TLProgress extends StatelessWidget{
const TLProgress({super.key, required this.tr, required this.rank, required this.position, required this.nextRankPosition, required this.previousRankPosition, this.nextRank, this.previousRank, this.nextRankTRcutoff, this.previousRankTRcutoff, this.nextRankTRcutoffTarget, this.previousRankTRcutoffTarget});
double getBarPosition(){
return 1 - (position - nextRankPosition)/(previousRankPosition - nextRankPosition);
return min(max(0, 1 - (position - nextRankPosition)/(previousRankPosition - nextRankPosition)), 1);
}
double? getBarTR(){
return null;
double? getBarTR(double tr){
return min(max(0, (tr - previousRankTRcutoff!)/(nextRankTRcutoff! - previousRankTRcutoff!)), 1);
}
@override
Widget build(BuildContext context) {
print(getBarPosition());
// return Container(
// alignment: Alignment.centerLeft,
// height: 50,
@ -47,11 +49,27 @@ class TLProgress extends StatelessWidget{
minimum: 0,
maximum: 1,
interval: 1,
ranges: [LinearGaugeRange(endValue: getBarPosition(), color: Colors.cyanAccent,)],
markerPointers: [LinearShapePointer(value: getBarPosition(), position: LinearElementPosition.inside, shapeType: LinearShapePointerType.triangle, color: Colors.white, height: 20),
LinearWidgetPointer(offset: 4, position: LinearElementPosition.outside, value: getBarPosition(), child: Text(NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(position)))],
ranges: [
if (previousRankTRcutoff != null && nextRankTRcutoff != null) LinearGaugeRange(endValue: getBarTR(tr)!, color: Colors.cyanAccent, position: LinearElementPosition.cross),
if (position != -1) LinearGaugeRange(endValue: getBarPosition(), color: Colors.cyanAccent, position: LinearElementPosition.cross),
if (previousRankTRcutoff != null && previousRankTRcutoffTarget != null) LinearGaugeRange(endValue: getBarTR(previousRankTRcutoffTarget!)!, color: Colors.greenAccent, position: LinearElementPosition.inside),
if (nextRankTRcutoff != null && nextRankTRcutoffTarget != null) LinearGaugeRange(startValue: getBarTR(nextRankTRcutoffTarget!)!, endValue: 1, color: Colors.yellowAccent, position: LinearElementPosition.inside)
],
markerPointers: [
LinearShapePointer(value: getBarPosition(), position: LinearElementPosition.cross, shapeType: LinearShapePointerType.diamond, color: Colors.white, height: 20),
LinearWidgetPointer(offset: 4, position: LinearElementPosition.outside, value: getBarPosition(), child: Text("${NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(position)}"))
],
isMirrored: true,
showTicks: true,
axisLabelStyle: TextStyle(),
onGenerateLabels: () => [
LinearAxisLabel(text: "${f0.format(previousRankPosition)}\n ${intf.format(previousRankTRcutoff)} TR", value: 0),
LinearAxisLabel(text: "${f0.format(nextRankPosition)}\n ${intf.format(nextRankTRcutoff)} TR", value: 1),
],
// labelFormatterCallback: (value) {
// if (value == "0") return "${f0.format(previousRankPosition)}\n 26,700 TR";
// else return f0.format(nextRankPosition);
// },
showLabels: true
)
);

View File

@ -140,7 +140,8 @@ class _TLThingyState extends State<TLThingy> {
),
],
),
if (currentTl.gamesPlayed >= 10 && currentTl.rd! < 100 && currentTl.nextAt >=0 && currentTl.prevAt >= 0)
if (currentTl.gamesPlayed > 9)
// if (currentTl.gamesPlayed >= 10 && currentTl.rd! < 100 && currentTl.nextAt >=0 && currentTl.prevAt >= 0)
// Padding(
// padding: const EdgeInsets.all(8.0),
// child: SfLinearGauge(
@ -161,7 +162,7 @@ class _TLThingyState extends State<TLThingy> {
// ),
TLProgress(
tr: currentTl.rating,
rank: currentTl.rank,
rank: currentTl.rank != "z" ? currentTl.rank : currentTl.percentileRank,
position: currentTl.standing,
nextRankPosition: currentTl.nextAt,
previousRankPosition: currentTl.prevAt,

View File

@ -866,6 +866,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
syncfusion_flutter_charts:
dependency: "direct main"
description:
name: syncfusion_flutter_charts
sha256: ab73109c586f5ec2b01adc2672026a1fb3f93b2b5f6061ba8d7126c119061002
url: "https://pub.dev"
source: hosted
version: "24.2.9"
syncfusion_flutter_core:
dependency: transitive
description:

View File

@ -43,6 +43,7 @@ dependencies:
flutter_markdown: ^0.6.18
flutter_colorpicker: ^1.0.3
go_router: ^13.0.0
syncfusion_flutter_charts: ^24.2.9
dev_dependencies:
flutter_test: