import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/infrastructure/services/packages/list.dart'; import 'package:uae_stat/infrastructure/services/packages/num_abbreviation.dart'; import 'package:uae_stat/presentation/components/space.dart'; import 'package:uae_stat/presentation/components/charts/fl_dot_circle_painter_with_text_above.dart'; import 'package:uae_stat/presentation/components/charts/line_chart_data_entity.dart'; class ThemedLineChart extends ConsumerWidget { const ThemedLineChart({ super.key, required this.lineDataList, this.yAxisTitleBuilder, this.yAxisInterval, }); final double? yAxisInterval; final List lineDataList; final String Function(double value)? yAxisTitleBuilder; double get _minY => lineDataList .map( (e) => e.data.values.fold( double.maxFinite, (prev, curr) => curr < prev ? curr : prev, ), ) .fold( double.maxFinite, (prev, curr) => curr < prev ? curr : prev, ); double get _maxY => lineDataList .map( (e) => e.data.values.fold( double.negativeInfinity, (prev, curr) => curr > prev ? curr : prev, ), ) .fold( double.negativeInfinity, (prev, curr) => curr > prev ? curr : prev, ); @override Widget build(BuildContext context, ref) { final isMinValZero = _minY == 0; final range = _maxY - _minY; const paddingRate = 0.24; final maxYPadded = _maxY + (range * paddingRate); final minYPadded = _minY - (range * paddingRate); final titlesData = FlTitlesData( show: true, leftTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), rightTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), topTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 30, interval: yAxisInterval ?? 1, getTitlesWidget: (value, meta) => Column( children: [ const SizedBox( height: 7, child: VerticalDivider( color: Colors.black, ), ), Text( yAxisTitleBuilder == null ? value.toString() : yAxisTitleBuilder!(value), style: TextStyle( fontFamily: context.translate( 'Roboto', 'NotoKufi', ), fontSize: 7, color: Colors.black, ), ), ], ), ), ), ); final lineChart = LineChart( LineChartData( maxY: maxYPadded, minY: isMinValZero ? 0 : minYPadded, lineTouchData: const LineTouchData(enabled: false), borderData: FlBorderData( border: const Border( bottom: BorderSide(), ), ), gridData: const FlGridData(show: false), titlesData: titlesData, lineBarsData: lineDataList .map( (e) => LineChartBarData( belowBarData: BarAreaData( color: e.color.withOpacity(0.3), show: e.isShaded, ), color: e.color, dotData: FlDotData( getDotPainter: ( flSpot, yValue, lineChartBarData, i, ) => FlDotCirclePainterWithTextAbove( text: e.textAboveMarkerBuilder == null ? flSpot.y.toAbbreviatedString() : e.textAboveMarkerBuilder!(flSpot.y), color: e.color, textStyle: TextStyle( fontFamily: context.translate( 'Roboto', 'NotoKufi', ), fontWeight: FontWeight.w600, fontSize: 10, color: Colors.black, ), ), ), spots: e.data.entries .map( (e) => FlSpot( e.key, e.value, ), ) .toList(), ), ) .toList(), ), ); return Column( children: [ if (lineDataList.any((e) => e.title != null)) ...lineDataList .map( (e) => Row( children: [ Container( height: 12, width: 12, decoration: BoxDecoration( color: e.color, borderRadius: BorderRadius.circular(2), ), ), 12.horizontalSpace, Expanded( child: Text( e.title!, style: TextStyle( fontFamily: context.translate( 'Roboto', 'NotoKufi', ), fontSize: 11, color: Colors.black, ), ), ), ], ), ) .toList() ..intersperseDivider(4.verticalSpace), 12.verticalSpace, Expanded(child: lineChart), ], ); } }