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:
parent
5c6c502a57
commit
8081755e71
|
@ -42,7 +42,7 @@ const Map<String, double> rankCutoffs = {
|
||||||
"": 0.5
|
"": 0.5
|
||||||
};
|
};
|
||||||
const Map<String, double> rankTargets = {
|
const Map<String, double> rankTargets = {
|
||||||
"x": 24008,
|
"x": 24503.75, // where that comes from?
|
||||||
"u": 23038,
|
"u": 23038,
|
||||||
"ss": 21583,
|
"ss": 21583,
|
||||||
"s+": 20128,
|
"s+": 20128,
|
||||||
|
|
|
@ -185,15 +185,17 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
topTR = requests.elementAtOrNull(3) as double?; // No TR - no Top TR
|
topTR = requests.elementAtOrNull(3) as double?; // No TR - no Top TR
|
||||||
|
|
||||||
meAmongEveryone = teto.getCachedLeaderboardPositions(me.userId);
|
meAmongEveryone = teto.getCachedLeaderboardPositions(me.userId);
|
||||||
if (meAmongEveryone == null && prefs.getBool("showPositions") == true){
|
if (prefs.getBool("showPositions") == true){
|
||||||
// Get tetra League leaderboard
|
// Get tetra League leaderboard
|
||||||
everyone = teto.getCachedLeaderboard();
|
everyone = teto.getCachedLeaderboard();
|
||||||
everyone ??= await teto.fetchTLLeaderboard();
|
everyone ??= await teto.fetchTLLeaderboard();
|
||||||
meAmongEveryone = await compute(everyone!.getLeaderboardPosition, me);
|
if (meAmongEveryone == null){
|
||||||
if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!);
|
meAmongEveryone = await compute(everyone!.getLeaderboardPosition, me);
|
||||||
if (me.tlSeason1.rank != "z") {
|
if (meAmongEveryone != null) teto.cacheLeaderboardPositions(me.userId, meAmongEveryone!);
|
||||||
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;
|
nextRankCutoff = nextRankCutoff??25000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -515,7 +517,7 @@ class _MainState extends State<MainView> with TickerProviderStateMixin {
|
||||||
thatRankCutoff: thatRankCutoff,
|
thatRankCutoff: thatRankCutoff,
|
||||||
thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null,
|
thatRankTarget: snapshot.data![0].tlSeason1.rank != "z" ? rankTargets[snapshot.data![0].tlSeason1.rank] : null,
|
||||||
nextRankCutoff: nextRankCutoff,
|
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,
|
averages: rankAverages,
|
||||||
lbPositions: meAmongEveryone
|
lbPositions: meAmongEveryone
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'dart:ui';
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/gestures.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/views/main_view.dart' show MainView;
|
||||||
import 'package:tetra_stats/utils/text_shadow.dart';
|
import 'package:tetra_stats/utils/text_shadow.dart';
|
||||||
import 'package:window_manager/window_manager.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),)];
|
var _chartsShortTitlesDropdowns = <DropdownMenuItem>[for (MapEntry e in chartsShortTitles.entries) DropdownMenuItem(value: e.key, child: Text(e.value),)];
|
||||||
Stats _chartsX = Stats.tr;
|
Stats _chartsX = Stats.tr;
|
||||||
Stats _chartsY = Stats.apm;
|
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<DropdownMenuItem> _itemStats = [for (MapEntry e in chartsShortTitles.entries) DropdownMenuItem(value: e.key, child: Text(e.value))];
|
||||||
List<_MyScatterSpot> _spots = [];
|
List<_MyScatterSpot> _spots = [];
|
||||||
Stats _sortBy = Stats.tr;
|
Stats _sortBy = Stats.tr;
|
||||||
|
@ -49,7 +53,7 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
|
||||||
late double yScale;
|
late double yScale;
|
||||||
String headerTooltip = t.pseudoTooltipHeaderInit;
|
String headerTooltip = t.pseudoTooltipHeaderInit;
|
||||||
String footerTooltip = t.pseudoTooltipFooterInit;
|
String footerTooltip = t.pseudoTooltipFooterInit;
|
||||||
int hoveredPointId = -1;
|
ValueNotifier<int> hoveredPointId = ValueNotifier<int>(-1);
|
||||||
double scaleFactor = 5e2;
|
double scaleFactor = 5e2;
|
||||||
double dragFactor = 7e2;
|
double dragFactor = 7e2;
|
||||||
|
|
||||||
|
@ -57,6 +61,32 @@ class RankState extends State<RankView> with SingleTickerProviderStateMixin {
|
||||||
void initState() {
|
void initState() {
|
||||||
_scrollController = ScrollController();
|
_scrollController = ScrollController();
|
||||||
_tabController = TabController(length: 6, vsync: this);
|
_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){
|
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS){
|
||||||
windowManager.getTitle().then((value) => _oldWindowTitle = value);
|
windowManager.getTitle().then((value) => _oldWindowTitle = value);
|
||||||
windowManager.setTitle("Tetra Stats: ${widget.rank[1]["everyone"] ? t.everyoneAverages : t.rankAverages(rank: widget.rank[0].rank.toUpperCase())}");
|
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.getStatByEnum(_chartsY).toDouble(),
|
||||||
entry.userId,
|
entry.userId,
|
||||||
entry.username,
|
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();
|
recalculateScales();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// TODO: Figure out wtf is going on with gestures
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: bigScreen ? const EdgeInsets.fromLTRB(40, 40, 40, 48) : const EdgeInsets.fromLTRB(0, 40, 16, 48),
|
padding: bigScreen ? const EdgeInsets.fromLTRB(40, 40, 40, 48) : const EdgeInsets.fromLTRB(0, 40, 16, 48),
|
||||||
child: Stack(
|
child: SfCartesianChart(
|
||||||
children: [
|
tooltipBehavior: _tooltipBehavior,
|
||||||
ScatterChart(
|
//primaryXAxis: CategoryAxis(),
|
||||||
key: graphKey,
|
series: [
|
||||||
ScatterChartData(
|
ScatterSeries(
|
||||||
minX: minX,
|
enableTooltip: true,
|
||||||
maxX: maxX,
|
dataSource: _spots,
|
||||||
minY: minY,
|
animationDuration: 0,
|
||||||
maxY: maxY,
|
pointColorMapper: (data, _) => data.color,
|
||||||
clipData: const FlClipData.all(),
|
xValueMapper: (data, _) => data.x,
|
||||||
scatterSpots: _spots,
|
yValueMapper: (data, _) => data.y
|
||||||
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)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -642,10 +633,12 @@ class _ListEntry extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MyScatterSpot extends ScatterSpot {
|
class _MyScatterSpot{
|
||||||
|
num x;
|
||||||
|
num y;
|
||||||
String id;
|
String id;
|
||||||
String nickname;
|
String nickname;
|
||||||
//Color color;
|
String rank;
|
||||||
//FlDotPainter painter = FlDotCirclePainter(color: color, radius: 2);
|
Color color;
|
||||||
_MyScatterSpot(super.x, super.y, this.id, this.nickname, {super.dotPainter});
|
_MyScatterSpot(this.x, this.y, this.id, this.nickname, this.rank, this.color);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
||||||
import 'package:tetra_stats/data_objects/tetrio.dart';
|
import 'package:tetra_stats/data_objects/tetrio.dart';
|
||||||
import 'package:tetra_stats/gen/strings.g.dart';
|
import 'package:tetra_stats/gen/strings.g.dart';
|
||||||
|
import 'package:tetra_stats/utils/numers_formats.dart';
|
||||||
|
|
||||||
class TLProgress extends StatelessWidget{
|
class TLProgress extends StatelessWidget{
|
||||||
final double tr;
|
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});
|
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(){
|
double getBarPosition(){
|
||||||
return 1 - (position - nextRankPosition)/(previousRankPosition - nextRankPosition);
|
return min(max(0, 1 - (position - nextRankPosition)/(previousRankPosition - nextRankPosition)), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
double? getBarTR(){
|
double? getBarTR(double tr){
|
||||||
return null;
|
return min(max(0, (tr - previousRankTRcutoff!)/(nextRankTRcutoff! - previousRankTRcutoff!)), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
print(getBarPosition());
|
|
||||||
// return Container(
|
// return Container(
|
||||||
// alignment: Alignment.centerLeft,
|
// alignment: Alignment.centerLeft,
|
||||||
// height: 50,
|
// height: 50,
|
||||||
|
@ -47,11 +49,27 @@ class TLProgress extends StatelessWidget{
|
||||||
minimum: 0,
|
minimum: 0,
|
||||||
maximum: 1,
|
maximum: 1,
|
||||||
interval: 1,
|
interval: 1,
|
||||||
ranges: [LinearGaugeRange(endValue: getBarPosition(), color: Colors.cyanAccent,)],
|
ranges: [
|
||||||
markerPointers: [LinearShapePointer(value: getBarPosition(), position: LinearElementPosition.inside, shapeType: LinearShapePointerType.triangle, color: Colors.white, height: 20),
|
if (previousRankTRcutoff != null && nextRankTRcutoff != null) LinearGaugeRange(endValue: getBarTR(tr)!, color: Colors.cyanAccent, position: LinearElementPosition.cross),
|
||||||
LinearWidgetPointer(offset: 4, position: LinearElementPosition.outside, value: getBarPosition(), child: Text(NumberFormat.decimalPatternDigits(locale: LocaleSettings.currentLocale.languageCode, decimalDigits: 0).format(position)))],
|
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,
|
isMirrored: true,
|
||||||
showTicks: 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
|
showLabels: true
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -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(
|
||||||
// padding: const EdgeInsets.all(8.0),
|
// padding: const EdgeInsets.all(8.0),
|
||||||
// child: SfLinearGauge(
|
// child: SfLinearGauge(
|
||||||
|
@ -161,7 +162,7 @@ class _TLThingyState extends State<TLThingy> {
|
||||||
// ),
|
// ),
|
||||||
TLProgress(
|
TLProgress(
|
||||||
tr: currentTl.rating,
|
tr: currentTl.rating,
|
||||||
rank: currentTl.rank,
|
rank: currentTl.rank != "z" ? currentTl.rank : currentTl.percentileRank,
|
||||||
position: currentTl.standing,
|
position: currentTl.standing,
|
||||||
nextRankPosition: currentTl.nextAt,
|
nextRankPosition: currentTl.nextAt,
|
||||||
previousRankPosition: currentTl.prevAt,
|
previousRankPosition: currentTl.prevAt,
|
||||||
|
|
|
@ -866,6 +866,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
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:
|
syncfusion_flutter_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -43,6 +43,7 @@ dependencies:
|
||||||
flutter_markdown: ^0.6.18
|
flutter_markdown: ^0.6.18
|
||||||
flutter_colorpicker: ^1.0.3
|
flutter_colorpicker: ^1.0.3
|
||||||
go_router: ^13.0.0
|
go_router: ^13.0.0
|
||||||
|
syncfusion_flutter_charts: ^24.2.9
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in New Issue