mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-04 23:24:20 +00:00
refactor(ui): Refactor charts to remove code duplication
This commit is contained in:
parent
d8d0ea0c3c
commit
149e6d5a47
|
@ -22,3 +22,20 @@ List<Color> harmonizedBasicColors(final BuildContext context) => [
|
|||
Colors.blueGrey.harmonizeWith(Theme.of(context).colorScheme.primary),
|
||||
Colors.grey.harmonizeWith(Theme.of(context).colorScheme.primary),
|
||||
];
|
||||
|
||||
List<Color> getGraphColors(final BuildContext context, final int length) {
|
||||
final colors = [
|
||||
Theme.of(context).colorScheme.primary,
|
||||
Theme.of(context).colorScheme.tertiary,
|
||||
Theme.of(context).colorScheme.secondary,
|
||||
...harmonizedBasicColors(context),
|
||||
];
|
||||
if (length <= colors.length) {
|
||||
return colors.sublist(0, length);
|
||||
} else {
|
||||
return List.generate(
|
||||
length,
|
||||
(final index) => colors[index % colors.length],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
import 'package:intl/intl.dart';
|
||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||
|
||||
String bottomTitle(
|
||||
final int value,
|
||||
final List<TimeSeriesData> data,
|
||||
final Period period,
|
||||
) {
|
||||
final hhmm = DateFormat('HH:mm');
|
||||
final day = DateFormat('MMMd');
|
||||
String res;
|
||||
|
||||
if (value <= 0 || value >= data.length) {
|
||||
return '';
|
||||
}
|
||||
|
||||
final time = data[value].time;
|
||||
switch (period) {
|
||||
case Period.hour:
|
||||
case Period.day:
|
||||
res = hhmm.format(time);
|
||||
break;
|
||||
case Period.month:
|
||||
res = day.format(time);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
|
@ -8,23 +8,6 @@ class _Chart extends StatelessWidget {
|
|||
final MetricsState state = cubit.state;
|
||||
List<Widget> charts;
|
||||
|
||||
List<Color> getGraphColors(final BuildContext context, final int length) {
|
||||
final colors = [
|
||||
Theme.of(context).colorScheme.primary,
|
||||
Theme.of(context).colorScheme.tertiary,
|
||||
Theme.of(context).colorScheme.secondary,
|
||||
...harmonizedBasicColors(context),
|
||||
];
|
||||
if (length <= colors.length) {
|
||||
return colors.sublist(0, length);
|
||||
} else {
|
||||
return List.generate(
|
||||
length,
|
||||
(final index) => colors[index % colors.length],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (state is MetricsLoaded || state is MetricsLoading) {
|
||||
charts = [
|
||||
FilledCard(
|
||||
|
@ -332,7 +315,7 @@ class _Chart extends StatelessWidget {
|
|||
return SizedBox(
|
||||
height: 200,
|
||||
child: CpuChart(
|
||||
data: data,
|
||||
data: [data],
|
||||
period: state.period,
|
||||
start: state.metrics.start,
|
||||
),
|
||||
|
@ -349,7 +332,7 @@ class _Chart extends StatelessWidget {
|
|||
return SizedBox(
|
||||
height: 200,
|
||||
child: MemoryChart(
|
||||
data: data.overallMetrics,
|
||||
data: [data.overallMetrics],
|
||||
period: state.period,
|
||||
start: state.metrics.start,
|
||||
),
|
||||
|
@ -363,7 +346,7 @@ class _Chart extends StatelessWidget {
|
|||
return SizedBox(
|
||||
height: 200,
|
||||
child: NetworkChart(
|
||||
listData: [ppsIn, ppsOut],
|
||||
data: [ppsIn, ppsOut],
|
||||
period: state.period,
|
||||
start: state.metrics.start,
|
||||
),
|
||||
|
|
|
@ -1,45 +1,29 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||
import 'package:selfprivacy/ui/pages/server_details/charts/bottom_title.dart';
|
||||
import 'package:selfprivacy/ui/pages/server_details/charts/generic_chart.dart';
|
||||
|
||||
class CpuChart extends StatelessWidget {
|
||||
class CpuChart extends GenericLineChart {
|
||||
const CpuChart({
|
||||
required this.data,
|
||||
required this.period,
|
||||
required this.start,
|
||||
required super.data,
|
||||
required super.period,
|
||||
required super.start,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<TimeSeriesData> data;
|
||||
final Period period;
|
||||
final DateTime start;
|
||||
|
||||
List<FlSpot> getSpots() {
|
||||
var i = 0;
|
||||
final List<FlSpot> res = [];
|
||||
|
||||
for (final d in data) {
|
||||
res.add(FlSpot(i.toDouble(), d.value));
|
||||
i++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@override
|
||||
String screenReaderDescription(final BuildContext context) {
|
||||
final lastData = data.last;
|
||||
final lastData = data.first.last;
|
||||
final lastValue = lastData.value;
|
||||
|
||||
final averageUsage =
|
||||
data.map((final e) => e.value).reduce((final a, final b) => a + b) /
|
||||
data.length;
|
||||
final maxUsage = data
|
||||
final averageUsage = data.first
|
||||
.map((final e) => e.value)
|
||||
.reduce((final a, final b) => a + b) /
|
||||
data.length;
|
||||
final maxUsage = data.first
|
||||
.map((final e) => e.value)
|
||||
.reduce((final a, final b) => a > b ? a : b);
|
||||
final maxUsageTime = data.firstWhere((final e) => e.value == maxUsage).time;
|
||||
final maxUsageTime =
|
||||
data.first.firstWhere((final e) => e.value == maxUsage).time;
|
||||
|
||||
final label = 'resource_chart.cpu_chart_screen_reader_explanation'.tr(
|
||||
namedArgs: {
|
||||
|
@ -54,159 +38,4 @@ class CpuChart extends StatelessWidget {
|
|||
|
||||
return label;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => Semantics(
|
||||
label: screenReaderDescription(context),
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
lineTouchData: LineTouchData(
|
||||
enabled: true,
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
getTooltipColor: (final LineBarSpot _) =>
|
||||
Theme.of(context).colorScheme.surface,
|
||||
tooltipPadding: const EdgeInsets.all(8),
|
||||
getTooltipItems: (final List<LineBarSpot> touchedBarSpots) {
|
||||
final List<LineTooltipItem> res = [];
|
||||
|
||||
for (final spot in touchedBarSpots) {
|
||||
final value = spot.y;
|
||||
final date = data[spot.x.toInt()].time;
|
||||
|
||||
res.add(
|
||||
LineTooltipItem(
|
||||
'${value.toStringAsFixed(2)}% at ${DateFormat('HH:mm dd.MM.yyyy').format(date)}',
|
||||
TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
),
|
||||
),
|
||||
lineBarsData: [
|
||||
LineChartBarData(
|
||||
spots: getSpots(),
|
||||
isCurved: false,
|
||||
barWidth: 2,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
dotData: const FlDotData(
|
||||
show: false,
|
||||
),
|
||||
belowBarData: BarAreaData(
|
||||
show: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(context).colorScheme.primary.withOpacity(0.5),
|
||||
Theme.of(context).colorScheme.primary.withOpacity(0.0),
|
||||
],
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
minY: 0,
|
||||
// Maximal value of data by 100 step
|
||||
maxY: 100,
|
||||
minX: 0,
|
||||
titlesData: FlTitlesData(
|
||||
topTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
interval: 40,
|
||||
reservedSize: 30,
|
||||
getTitlesWidget: (final value, final titleMeta) => Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ExcludeSemantics(
|
||||
child: Text(
|
||||
bottomTitle(
|
||||
value.toInt(),
|
||||
data,
|
||||
period,
|
||||
),
|
||||
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
showTitles: true,
|
||||
),
|
||||
),
|
||||
leftTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: false,
|
||||
),
|
||||
),
|
||||
rightTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
gridData: FlGridData(
|
||||
show: true,
|
||||
drawVerticalLine: true,
|
||||
horizontalInterval: 25,
|
||||
verticalInterval: 40,
|
||||
getDrawingHorizontalLine: (final value) => FlLine(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
strokeWidth: 1,
|
||||
),
|
||||
getDrawingVerticalLine: (final value) => FlLine(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
strokeWidth: 1,
|
||||
),
|
||||
),
|
||||
borderData: FlBorderData(
|
||||
show: true,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
left: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
right: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
top: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
bool checkToShowTitle(
|
||||
final double minValue,
|
||||
final double maxValue,
|
||||
final SideTitles sideTitles,
|
||||
final double appliedInterval,
|
||||
final double value,
|
||||
) {
|
||||
if (value < 0) {
|
||||
return false;
|
||||
} else if (value == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final localValue = value - minValue;
|
||||
final v = localValue / 20;
|
||||
return v - v.floor() == 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +1,22 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||
import 'package:selfprivacy/ui/pages/server_details/charts/bottom_title.dart';
|
||||
import 'package:selfprivacy/ui/pages/server_details/charts/generic_chart.dart';
|
||||
import 'package:selfprivacy/ui/pages/server_details/server_details_screen.dart';
|
||||
|
||||
class DiskChart extends StatelessWidget {
|
||||
const DiskChart({
|
||||
class DiskChart extends GenericLineChart {
|
||||
DiskChart({
|
||||
required this.diskData,
|
||||
required this.period,
|
||||
required this.start,
|
||||
required super.period,
|
||||
required super.start,
|
||||
super.key,
|
||||
});
|
||||
}) : super(
|
||||
data: diskData.map((final e) => e.diskData).toList(),
|
||||
);
|
||||
|
||||
final List<DiskGraphData> diskData;
|
||||
final Period period;
|
||||
final DateTime start;
|
||||
|
||||
List<FlSpot> getSpots(final List<TimeSeriesData> data) {
|
||||
var i = 0;
|
||||
final List<FlSpot> res = [];
|
||||
|
||||
for (final d in data) {
|
||||
res.add(FlSpot(i.toDouble(), d.value));
|
||||
i++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@override
|
||||
String screenReaderDescription(final BuildContext context) {
|
||||
final buffer = StringBuffer();
|
||||
buffer.write(
|
||||
|
@ -61,159 +48,18 @@ class DiskChart extends StatelessWidget {
|
|||
}
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => Semantics(
|
||||
label: screenReaderDescription(context),
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
lineTouchData: LineTouchData(
|
||||
enabled: true,
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
getTooltipColor: (final LineBarSpot _) =>
|
||||
Theme.of(context).colorScheme.surface,
|
||||
tooltipPadding: const EdgeInsets.all(8),
|
||||
getTooltipItems: (final List<LineBarSpot> touchedBarSpots) {
|
||||
final List<LineTooltipItem> res = [];
|
||||
|
||||
bool timeShown = false;
|
||||
for (final spot in touchedBarSpots) {
|
||||
final value = spot.y;
|
||||
final date = diskData.first.diskData[spot.x.toInt()].time;
|
||||
|
||||
res.add(
|
||||
LineTooltipItem(
|
||||
'${timeShown ? '' : DateFormat('HH:mm dd.MM.yyyy').format(date)} ${diskData[spot.barIndex].volume.displayName} ${value.toInt()}%',
|
||||
TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
timeShown = true;
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
),
|
||||
),
|
||||
lineBarsData: diskData
|
||||
.map<LineChartBarData>(
|
||||
(final disk) => LineChartBarData(
|
||||
spots: getSpots(disk.diskData),
|
||||
isCurved: false,
|
||||
barWidth: 2,
|
||||
color: disk.color,
|
||||
dotData: const FlDotData(
|
||||
show: false,
|
||||
),
|
||||
belowBarData: BarAreaData(
|
||||
show: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
disk.color.withOpacity(0.5),
|
||||
disk.color.withOpacity(0.0),
|
||||
],
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
minY: 0,
|
||||
maxY: 100,
|
||||
minX: 0,
|
||||
titlesData: FlTitlesData(
|
||||
topTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
interval: 40,
|
||||
reservedSize: 30,
|
||||
getTitlesWidget: (final value, final titleMeta) => Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ExcludeSemantics(
|
||||
child: Text(
|
||||
bottomTitle(
|
||||
value.toInt(),
|
||||
diskData.first.diskData,
|
||||
period,
|
||||
),
|
||||
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
showTitles: true,
|
||||
),
|
||||
),
|
||||
leftTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
rightTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
gridData: FlGridData(
|
||||
show: true,
|
||||
drawVerticalLine: true,
|
||||
verticalInterval: 40,
|
||||
horizontalInterval: 25,
|
||||
getDrawingHorizontalLine: (final value) => FlLine(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
strokeWidth: 1,
|
||||
),
|
||||
getDrawingVerticalLine: (final value) => FlLine(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
strokeWidth: 1,
|
||||
),
|
||||
),
|
||||
borderData: FlBorderData(
|
||||
show: true,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
left: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
right: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
top: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
LineTooltipItem generateTooltipItem({
|
||||
required final bool timeShown,
|
||||
required final DateTime date,
|
||||
required final double value,
|
||||
required final LineBarSpot spot,
|
||||
required final BuildContext context,
|
||||
}) =>
|
||||
LineTooltipItem(
|
||||
'${timeShown ? '' : DateFormat('HH:mm dd.MM.yyyy').format(date)} ${diskData[spot.barIndex].volume.displayName} ${value.toInt()}%',
|
||||
TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
);
|
||||
|
||||
bool checkToShowTitle(
|
||||
final double minValue,
|
||||
final double maxValue,
|
||||
final SideTitles sideTitles,
|
||||
final double appliedInterval,
|
||||
final double value,
|
||||
) {
|
||||
if (value < 0) {
|
||||
return false;
|
||||
} else if (value == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final diff = value - minValue;
|
||||
final finalValue = diff / 20;
|
||||
return finalValue - finalValue.floor() == 0;
|
||||
}
|
||||
}
|
||||
|
|
261
lib/ui/pages/server_details/charts/generic_chart.dart
Normal file
261
lib/ui/pages/server_details/charts/generic_chart.dart
Normal file
|
@ -0,0 +1,261 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||
import 'package:selfprivacy/theming/harmonized_basic_colors.dart';
|
||||
|
||||
class GenericLineChart extends StatelessWidget {
|
||||
const GenericLineChart({
|
||||
required this.data,
|
||||
required this.period,
|
||||
required this.start,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<List<TimeSeriesData>> data;
|
||||
final Period period;
|
||||
final DateTime start;
|
||||
|
||||
bool get showRightTitle => false;
|
||||
|
||||
static List<FlSpot> getSpots(final List<TimeSeriesData> data) {
|
||||
var i = 0;
|
||||
final List<FlSpot> res = [];
|
||||
|
||||
for (final d in data) {
|
||||
res.add(FlSpot(i.toDouble(), d.value));
|
||||
i++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool checkToShowTitle(
|
||||
final double minValue,
|
||||
final double maxValue,
|
||||
final SideTitles sideTitles,
|
||||
final double appliedInterval,
|
||||
final double value,
|
||||
) {
|
||||
if (value < 0) {
|
||||
return false;
|
||||
} else if (value == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final diff = value - minValue;
|
||||
final finalValue = diff / 20;
|
||||
return finalValue - finalValue.floor() == 0;
|
||||
}
|
||||
|
||||
String screenReaderDescription(final BuildContext context) =>
|
||||
'Overrite this function';
|
||||
|
||||
LineTooltipItem generateTooltipItem({
|
||||
required final bool timeShown,
|
||||
required final DateTime date,
|
||||
required final double value,
|
||||
required final LineBarSpot spot,
|
||||
required final BuildContext context,
|
||||
}) =>
|
||||
LineTooltipItem(
|
||||
'${value.toStringAsFixed(2)}% at ${DateFormat('HH:mm dd.MM.yyyy').format(date)}',
|
||||
TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
);
|
||||
|
||||
double getMaxY() => 100;
|
||||
|
||||
String getRightTitle(final double value) => '';
|
||||
|
||||
String bottomTitle(
|
||||
final int value,
|
||||
final List<TimeSeriesData> data,
|
||||
final Period period,
|
||||
) {
|
||||
final hhmm = DateFormat('HH:mm');
|
||||
final day = DateFormat('MMMd');
|
||||
String res;
|
||||
|
||||
if (value <= 0 || value >= data.length) {
|
||||
return '';
|
||||
}
|
||||
|
||||
final time = data[value].time;
|
||||
switch (period) {
|
||||
case Period.hour:
|
||||
case Period.day:
|
||||
res = hhmm.format(time);
|
||||
break;
|
||||
case Period.month:
|
||||
res = day.format(time);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final colors = getGraphColors(context, data.length);
|
||||
return Semantics(
|
||||
label: screenReaderDescription(context),
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
lineTouchData: LineTouchData(
|
||||
enabled: true,
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
getTooltipColor: (final LineBarSpot _) =>
|
||||
Theme.of(context).colorScheme.surface,
|
||||
tooltipPadding: const EdgeInsets.all(8),
|
||||
getTooltipItems: (final List<LineBarSpot> touchedBarSpots) {
|
||||
final List<LineTooltipItem> tooltipItems = [];
|
||||
bool timeShown = false;
|
||||
|
||||
for (final spot in touchedBarSpots) {
|
||||
final value = spot.y;
|
||||
final date = data.first[spot.x.toInt()].time;
|
||||
|
||||
tooltipItems.add(
|
||||
generateTooltipItem(
|
||||
timeShown: timeShown,
|
||||
date: date,
|
||||
value: value,
|
||||
spot: spot,
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
|
||||
timeShown = true;
|
||||
}
|
||||
|
||||
return tooltipItems;
|
||||
},
|
||||
),
|
||||
),
|
||||
lineBarsData: data
|
||||
.map<LineChartBarData>(
|
||||
(final List<TimeSeriesData> dataSeries) => LineChartBarData(
|
||||
spots: getSpots(dataSeries),
|
||||
isCurved: false,
|
||||
barWidth: 2,
|
||||
color: colors[data.indexOf(dataSeries)],
|
||||
dotData: const FlDotData(
|
||||
show: false,
|
||||
),
|
||||
belowBarData: BarAreaData(
|
||||
show: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
colors[data.indexOf(dataSeries)].withOpacity(0.5),
|
||||
colors[data.indexOf(dataSeries)].withOpacity(0.0),
|
||||
],
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
minY: 0,
|
||||
maxY: getMaxY(),
|
||||
minX: 0,
|
||||
titlesData: FlTitlesData(
|
||||
topTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
interval: 40,
|
||||
reservedSize: 30,
|
||||
getTitlesWidget: (final value, final titleMeta) => Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ExcludeSemantics(
|
||||
child: Text(
|
||||
bottomTitle(value.toInt(), data.first, period),
|
||||
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
showTitles: true,
|
||||
),
|
||||
),
|
||||
leftTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
rightTitles: showRightTitle
|
||||
? AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
reservedSize: 50,
|
||||
getTitlesWidget: (final value, final titleMeta) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 5),
|
||||
child: ExcludeSemantics(
|
||||
child: Text(
|
||||
getRightTitle(value),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelSmall
|
||||
?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
interval: getMaxY() * 2 / 6.5,
|
||||
showTitles: true,
|
||||
),
|
||||
)
|
||||
: const AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
gridData: FlGridData(
|
||||
show: true,
|
||||
drawVerticalLine: true,
|
||||
verticalInterval: 40,
|
||||
horizontalInterval: getMaxY() == 100 ? 25 : getMaxY() * 2 / 6.5,
|
||||
getDrawingHorizontalLine: (final value) => FlLine(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
strokeWidth: 1,
|
||||
),
|
||||
getDrawingVerticalLine: (final value) => FlLine(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
strokeWidth: 1,
|
||||
),
|
||||
),
|
||||
borderData: FlBorderData(
|
||||
show: true,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
left: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
right: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
top: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,45 +1,29 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||
import 'package:selfprivacy/ui/pages/server_details/charts/bottom_title.dart';
|
||||
import 'package:selfprivacy/ui/pages/server_details/charts/generic_chart.dart';
|
||||
|
||||
class MemoryChart extends StatelessWidget {
|
||||
class MemoryChart extends GenericLineChart {
|
||||
const MemoryChart({
|
||||
required this.data,
|
||||
required this.period,
|
||||
required this.start,
|
||||
required super.data,
|
||||
required super.period,
|
||||
required super.start,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<TimeSeriesData> data;
|
||||
final Period period;
|
||||
final DateTime start;
|
||||
|
||||
List<FlSpot> getSpots() {
|
||||
var i = 0;
|
||||
final List<FlSpot> res = [];
|
||||
|
||||
for (final d in data) {
|
||||
res.add(FlSpot(i.toDouble(), d.value));
|
||||
i++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@override
|
||||
String screenReaderDescription(final BuildContext context) {
|
||||
final lastData = data.last;
|
||||
final lastData = data.first.last;
|
||||
final lastValue = lastData.value;
|
||||
|
||||
final averageUsage =
|
||||
data.map((final e) => e.value).reduce((final a, final b) => a + b) /
|
||||
data.length;
|
||||
final maxUsage = data
|
||||
final averageUsage = data.first
|
||||
.map((final e) => e.value)
|
||||
.reduce((final a, final b) => a + b) /
|
||||
data.length;
|
||||
final maxUsage = data.first
|
||||
.map((final e) => e.value)
|
||||
.reduce((final a, final b) => a > b ? a : b);
|
||||
final maxUsageTime = data.firstWhere((final e) => e.value == maxUsage).time;
|
||||
final maxUsageTime =
|
||||
data.first.firstWhere((final e) => e.value == maxUsage).time;
|
||||
|
||||
final label = 'resource_chart.memory_chart_screen_reader_explanation'.tr(
|
||||
namedArgs: {
|
||||
|
@ -54,159 +38,4 @@ class MemoryChart extends StatelessWidget {
|
|||
|
||||
return label;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) => Semantics(
|
||||
label: screenReaderDescription(context),
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
lineTouchData: LineTouchData(
|
||||
enabled: true,
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
getTooltipColor: (final LineBarSpot _) =>
|
||||
Theme.of(context).colorScheme.surface,
|
||||
tooltipPadding: const EdgeInsets.all(8),
|
||||
getTooltipItems: (final List<LineBarSpot> touchedBarSpots) {
|
||||
final List<LineTooltipItem> res = [];
|
||||
|
||||
for (final spot in touchedBarSpots) {
|
||||
final value = spot.y;
|
||||
final date = data[spot.x.toInt()].time;
|
||||
|
||||
res.add(
|
||||
LineTooltipItem(
|
||||
'${value.toStringAsFixed(2)}% at ${DateFormat('HH:mm dd.MM.yyyy').format(date)}',
|
||||
TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
),
|
||||
),
|
||||
lineBarsData: [
|
||||
LineChartBarData(
|
||||
spots: getSpots(),
|
||||
isCurved: false,
|
||||
barWidth: 2,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
dotData: const FlDotData(
|
||||
show: false,
|
||||
),
|
||||
belowBarData: BarAreaData(
|
||||
show: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(context).colorScheme.primary.withOpacity(0.5),
|
||||
Theme.of(context).colorScheme.primary.withOpacity(0.0),
|
||||
],
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
minY: 0,
|
||||
// Maximal value of data by 100 step
|
||||
maxY: 100,
|
||||
minX: 0,
|
||||
titlesData: FlTitlesData(
|
||||
topTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
interval: 40,
|
||||
reservedSize: 30,
|
||||
getTitlesWidget: (final value, final titleMeta) => Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ExcludeSemantics(
|
||||
child: Text(
|
||||
bottomTitle(
|
||||
value.toInt(),
|
||||
data,
|
||||
period,
|
||||
),
|
||||
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
showTitles: true,
|
||||
),
|
||||
),
|
||||
leftTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: false,
|
||||
),
|
||||
),
|
||||
rightTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
gridData: FlGridData(
|
||||
show: true,
|
||||
drawVerticalLine: true,
|
||||
horizontalInterval: 25,
|
||||
verticalInterval: 40,
|
||||
getDrawingHorizontalLine: (final value) => FlLine(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
strokeWidth: 1,
|
||||
),
|
||||
getDrawingVerticalLine: (final value) => FlLine(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
strokeWidth: 1,
|
||||
),
|
||||
),
|
||||
borderData: FlBorderData(
|
||||
show: true,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
left: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
right: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
top: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
bool checkToShowTitle(
|
||||
final double minValue,
|
||||
final double maxValue,
|
||||
final SideTitles sideTitles,
|
||||
final double appliedInterval,
|
||||
final double value,
|
||||
) {
|
||||
if (value < 0) {
|
||||
return false;
|
||||
} else if (value == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final localValue = value - minValue;
|
||||
final v = localValue / 20;
|
||||
return v - v.floor() == 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,61 +3,45 @@ import 'dart:math';
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||
import 'package:selfprivacy/ui/pages/server_details/charts/bottom_title.dart';
|
||||
import 'package:selfprivacy/ui/pages/server_details/charts/generic_chart.dart';
|
||||
|
||||
class NetworkChart extends StatelessWidget {
|
||||
class NetworkChart extends GenericLineChart {
|
||||
const NetworkChart({
|
||||
required this.listData,
|
||||
required this.period,
|
||||
required this.start,
|
||||
required super.data,
|
||||
required super.period,
|
||||
required super.start,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<List<TimeSeriesData>> listData;
|
||||
final Period period;
|
||||
final DateTime start;
|
||||
|
||||
List<FlSpot> getSpots(final data) {
|
||||
var i = 0;
|
||||
final List<FlSpot> res = [];
|
||||
|
||||
for (final d in data) {
|
||||
res.add(FlSpot(i.toDouble(), d.value));
|
||||
i++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@override
|
||||
bool get showRightTitle => true;
|
||||
|
||||
@override
|
||||
String screenReaderDescription(final BuildContext context) {
|
||||
final lastDataIn = listData[0].last;
|
||||
final lastDataOut = listData[1].last;
|
||||
final lastDataIn = data[0].last;
|
||||
final lastDataOut = data[1].last;
|
||||
final lastValueIn = lastDataIn.value;
|
||||
final lastValueOut = lastDataOut.value;
|
||||
|
||||
final averageUsageIn = listData[0]
|
||||
.map((final e) => e.value)
|
||||
.reduce((final a, final b) => a + b) /
|
||||
listData[0].length;
|
||||
final averageUsageOut = listData[1]
|
||||
.map((final e) => e.value)
|
||||
.reduce((final a, final b) => a + b) /
|
||||
listData[1].length;
|
||||
final averageUsageIn =
|
||||
data[0].map((final e) => e.value).reduce((final a, final b) => a + b) /
|
||||
data[0].length;
|
||||
final averageUsageOut =
|
||||
data[1].map((final e) => e.value).reduce((final a, final b) => a + b) /
|
||||
data[1].length;
|
||||
|
||||
final maxUsageIn = listData[0]
|
||||
final maxUsageIn = data[0]
|
||||
.map((final e) => e.value)
|
||||
.reduce((final a, final b) => a > b ? a : b);
|
||||
final maxUsageOut = listData[1]
|
||||
final maxUsageOut = data[1]
|
||||
.map((final e) => e.value)
|
||||
.reduce((final a, final b) => a > b ? a : b);
|
||||
|
||||
final maxUsageTimeIn =
|
||||
listData[0].firstWhere((final e) => e.value == maxUsageIn).time;
|
||||
data[0].firstWhere((final e) => e.value == maxUsageIn).time;
|
||||
final maxUsageTimeOut =
|
||||
listData[1].firstWhere((final e) => e.value == maxUsageOut).time;
|
||||
data[1].firstWhere((final e) => e.value == maxUsageOut).time;
|
||||
|
||||
final label = 'resource_chart.network_chart_screen_reader_explanation'.tr(
|
||||
namedArgs: {
|
||||
|
@ -81,199 +65,30 @@ class NetworkChart extends StatelessWidget {
|
|||
}
|
||||
|
||||
@override
|
||||
Widget build(final BuildContext context) {
|
||||
final listDataMax = [
|
||||
...listData[0].map((final e) => e.value),
|
||||
...listData[1].map((final e) => e.value),
|
||||
].reduce(max);
|
||||
return Semantics(
|
||||
label: screenReaderDescription(context),
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
lineTouchData: LineTouchData(
|
||||
enabled: true,
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
getTooltipColor: (final LineBarSpot _) =>
|
||||
Theme.of(context).colorScheme.surface,
|
||||
tooltipPadding: const EdgeInsets.all(8),
|
||||
getTooltipItems: (final List<LineBarSpot> touchedBarSpots) {
|
||||
final List<LineTooltipItem> res = [];
|
||||
double getMaxY() =>
|
||||
[
|
||||
...data[0].map((final e) => e.value),
|
||||
...data[1].map((final e) => e.value),
|
||||
].reduce(max) *
|
||||
1.2;
|
||||
|
||||
bool timeShown = false;
|
||||
|
||||
for (final spot in touchedBarSpots) {
|
||||
final value = spot.y;
|
||||
final date = listData[0][spot.x.toInt()].time;
|
||||
|
||||
res.add(
|
||||
LineTooltipItem(
|
||||
'${timeShown ? '' : DateFormat('HH:mm dd.MM.yyyy').format(date)} ${spot.barIndex == 0 ? 'resource_chart.in'.tr() : 'resource_chart.out'.tr()} ${DiskSize(byte: value.toInt()).toString()}',
|
||||
TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
timeShown = true;
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
),
|
||||
),
|
||||
lineBarsData: [
|
||||
// IN
|
||||
LineChartBarData(
|
||||
spots: getSpots(listData[0]),
|
||||
isCurved: false,
|
||||
barWidth: 2,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
dotData: const FlDotData(
|
||||
show: false,
|
||||
),
|
||||
belowBarData: BarAreaData(
|
||||
show: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(context).colorScheme.primary.withOpacity(0.5),
|
||||
Theme.of(context).colorScheme.primary.withOpacity(0.0),
|
||||
],
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
),
|
||||
),
|
||||
),
|
||||
// OUT
|
||||
LineChartBarData(
|
||||
spots: getSpots(listData[1]),
|
||||
isCurved: false,
|
||||
barWidth: 2,
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
dotData: const FlDotData(
|
||||
show: false,
|
||||
),
|
||||
belowBarData: BarAreaData(
|
||||
show: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(context).colorScheme.tertiary.withOpacity(0.5),
|
||||
Theme.of(context).colorScheme.tertiary.withOpacity(0.0),
|
||||
],
|
||||
begin: Alignment.bottomCenter,
|
||||
end: Alignment.topCenter,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
minY: 0,
|
||||
maxY: listDataMax * 1.2,
|
||||
minX: 0,
|
||||
titlesData: FlTitlesData(
|
||||
topTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
interval: 40,
|
||||
reservedSize: 30,
|
||||
getTitlesWidget: (final value, final titleMeta) => Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ExcludeSemantics(
|
||||
child: Text(
|
||||
bottomTitle(
|
||||
value.toInt(),
|
||||
listData[0],
|
||||
period,
|
||||
),
|
||||
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
showTitles: true,
|
||||
),
|
||||
),
|
||||
leftTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
rightTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
reservedSize: 50,
|
||||
getTitlesWidget: (final value, final titleMeta) => Padding(
|
||||
padding: const EdgeInsets.only(left: 5),
|
||||
child: ExcludeSemantics(
|
||||
child: Text(
|
||||
DiskSize(byte: value.toInt()).toString(),
|
||||
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
interval: listDataMax * 2 / 6.5,
|
||||
showTitles: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
gridData: FlGridData(
|
||||
show: true,
|
||||
drawVerticalLine: true,
|
||||
verticalInterval: 40,
|
||||
horizontalInterval: listDataMax * 2 / 6.5,
|
||||
getDrawingHorizontalLine: (final value) => FlLine(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
strokeWidth: 1,
|
||||
),
|
||||
getDrawingVerticalLine: (final value) => FlLine(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
strokeWidth: 1,
|
||||
),
|
||||
),
|
||||
borderData: FlBorderData(
|
||||
show: true,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
left: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
right: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
top: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.3),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
@override
|
||||
LineTooltipItem generateTooltipItem({
|
||||
required final bool timeShown,
|
||||
required final DateTime date,
|
||||
required final double value,
|
||||
required final LineBarSpot spot,
|
||||
required final BuildContext context,
|
||||
}) =>
|
||||
LineTooltipItem(
|
||||
'${timeShown ? '' : DateFormat('HH:mm dd.MM.yyyy').format(date)} ${spot.barIndex == 0 ? 'resource_chart.in'.tr() : 'resource_chart.out'.tr()} ${DiskSize(byte: value.toInt()).toString()}',
|
||||
TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
bool checkToShowTitle(
|
||||
final double minValue,
|
||||
final double maxValue,
|
||||
final SideTitles sideTitles,
|
||||
final double appliedInterval,
|
||||
final double value,
|
||||
) {
|
||||
if (value < 0) {
|
||||
return false;
|
||||
} else if (value == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final diff = value - minValue;
|
||||
final finalValue = diff / 20;
|
||||
return finalValue - finalValue.floor() == 0;
|
||||
}
|
||||
@override
|
||||
String getRightTitle(final double value) =>
|
||||
DiskSize(byte: value.toInt()).toString();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue