radar_chart_plus 3.0.0 copy "radar_chart_plus: ^3.0.0" to clipboard
radar_chart_plus: ^3.0.0 copied to clipboard

A versatile Flutter package for creating beautiful and customizable radar charts. Easily visualize multivariate data for comparisons and analysis.

example/lib/main.dart

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:radar_chart_plus/radar_chart_plus.dart';

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool _isDark = true;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Radar Chart Plus',
      debugShowCheckedModeBanner: false,
      themeMode: _isDark ? ThemeMode.dark : ThemeMode.light,
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: const Color(0xFF6C63FF),
        brightness: Brightness.light,
      ),
      darkTheme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: const Color(0xFF6C63FF),
        brightness: Brightness.dark,
      ),
      home: DemoPage(
        isDark: _isDark,
        onToggleTheme: () => setState(() => _isDark = !_isDark),
      ),
    );
  }
}

// ─── label & color pools ───────────────────────────────────────────────────

const _labels = [
  'AAAA',
  'BBBB',
  'CCCC',
  'DDDD',
  'EEEE',
  'FFFF',
  'GGGG',
  'HHHH',
  'IIII',
  'JJJJ',
  'KKKK',
  'LLLL',
];

const _seriesColors = [
  Color(0xFF6C63FF),
  Color(0xFFFF6B9D),
  Color(0xFF43E8A0),
  Color(0xFFFFA94D),
  Color(0xFF4DBFFF),
];

List<double> makeData(int count, int seed) {
  final r = Random(seed);
  return List.generate(count, (_) {
    double value = r.nextDouble() * (10 - 1) + 1;
    return double.parse(value.toStringAsFixed(2));
  });
}

// ─── Demo Page ─────────────────────────────────────────────────────────────

class DemoPage extends StatefulWidget {
  final bool isDark;
  final VoidCallback onToggleTheme;

  const DemoPage({
    super.key,
    required this.isDark,
    required this.onToggleTheme,
  });

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  double _axes = 6;
  double _series = 2;
  bool _horizontalLabels = false;
  bool _customToolTip = false;
  bool _polygonShape = false;
  Color _ringsColor = Colors.grey.withValues(alpha: 0.2);
  Color _borderColor = Colors.grey.withValues(alpha: 0.2);

  @override
  Widget build(BuildContext context) {
    final cs = Theme.of(context).colorScheme;
    final axesCount = _axes.round();
    final seriesCount = _series.round();

    final labels = _labels.take(axesCount).toList();
    final dataSets = List.generate(seriesCount, (i) {
      final c = _seriesColors[i % _seriesColors.length];
      return RadarDataSet(
        label: 'S${i + 1}',
        data: makeData(axesCount, i + 1),
        borderColor: c,
        fillColor: c.withValues(alpha: 0.2),
        dotColor: c,
      );
    });

    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'Radar Chart Plus',
          style: TextStyle(fontWeight: FontWeight.w600),
        ),
        centerTitle: false,
        actions: [
          IconButton(
            tooltip: widget.isDark ? 'Light mode' : 'Dark mode',
            onPressed: widget.onToggleTheme,
            icon: Icon(
              widget.isDark
                  ? Icons.light_mode_rounded
                  : Icons.dark_mode_rounded,
            ),
          ),
          const SizedBox(width: 8),
        ],
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            // ── Chart ──────────────────────────────────────────────────────────
            SizedBox(
              height: MediaQuery.of(context).size.height * 0.5,
              child: Padding(
                padding: const EdgeInsets.fromLTRB(24, 16, 24, 0),
                child: RadarChartPlus(
                  shape: _polygonShape
                      ? RadarChartShape.polygon
                      : RadarChartShape.circle,
                  ringsStyle: RadarChartLineStyle(
                    color: _ringsColor,
                    strokeWidth: 1.5,
                  ),
                  borderStyle: RadarChartLineStyle(
                    color: _borderColor,
                    strokeWidth: 1.5,
                  ),
                  key: ValueKey('$axesCount-$seriesCount'),
                  ticks: const [2.0, 4.0, 6.0, 8.0, 10.0],
                  tickFractionDigits: 0,
                  tickTextOffset: const Offset(5, 0),
                  customToolTipText: _customToolTip
                      ? (label, value, dataSetLabel) =>
                            '${dataSetLabel} ${label} = ${value.toStringAsFixed(1)} pts'
                      : null,
                  labels: labels,
                  dataSets: dataSets,
                  dotTapEnabled: true,
                  tooltipStyle: const RadarTooltipStyle(
                    showColorIndicator: true,
                  ),
                  horizontalLabels: _horizontalLabels,
                  maxWordsPerLine: 1,
                  labelSpacing: 4,
                  labelTextStyle: TextStyle(
                    color: cs.onSurface.withValues(alpha: 0.75),
                    fontSize: 11,
                    fontWeight: FontWeight.w600,
                  ),
                ),
              ),
            ),
            SizedBox(height: 40),

            // ── Controls ───────────────────────────────────────────────────────
            Container(
              padding: const EdgeInsets.fromLTRB(24, 20, 24, 32),
              decoration: BoxDecoration(
                color: cs.surfaceContainerLow,
                border: Border(
                  top: BorderSide(color: cs.outlineVariant, width: 1),
                ),
              ),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.start,
                spacing: 10,
                children: [
                  _SliderRow(
                    label: 'Axes',
                    value: _axes,
                    min: 3,
                    max: 12,
                    divisions: 9,
                    onChanged: (v) => setState(() => _axes = v),
                    color: _seriesColors[0],
                  ),
                  const SizedBox(height: 16),
                  _SliderRow(
                    label: 'Series',
                    value: _series,
                    min: 1,
                    max: 5,
                    divisions: 4,
                    onChanged: (v) => setState(() => _series = v),
                    color: _seriesColors[1],
                  ),
                  const SizedBox(height: 16),
                  Row(
                    children: [
                      SizedBox(
                        width: 100,
                        child: Text(
                          'Horizontal Labels',
                          style: TextStyle(
                            color: cs.onSurface.withValues(alpha: 0.7),
                            fontSize: 13,
                            fontWeight: FontWeight.w500,
                          ),
                        ),
                      ),
                      Switch(
                        value: _horizontalLabels,
                        onChanged: (v) => setState(() => _horizontalLabels = v),
                        activeColor: _seriesColors[2],
                      ),
                      const Spacer(),
                      SizedBox(
                        width: 100,
                        child: Text(
                          'Custom Tool Tip',
                          style: TextStyle(
                            color: cs.onSurface.withValues(alpha: 0.7),
                            fontSize: 13,
                            fontWeight: FontWeight.w500,
                          ),
                        ),
                      ),
                      Switch(
                        value: _customToolTip,
                        onChanged: (v) => setState(() => _customToolTip = v),
                        activeColor: _seriesColors[2],
                      ),
                    ],
                  ),
                  Row(
                    children: [
                      SizedBox(
                        width: 100,
                        child: Text(
                          'Polygon shape',
                          style: TextStyle(
                            color: cs.onSurface.withValues(alpha: 0.7),
                            fontSize: 13,
                            fontWeight: FontWeight.w500,
                          ),
                        ),
                      ),
                      Switch(
                        value: _polygonShape,
                        onChanged: (v) => setState(() => _polygonShape = v),
                        activeColor: _seriesColors[2],
                      ),
                      Spacer(),
                      Column(
                        spacing: 10,
                        crossAxisAlignment: CrossAxisAlignment.end,
                        children: [
                          Row(
                            children: [
                              Text(
                                'Ring Color',
                                style: TextStyle(
                                  color: cs.onSurface.withValues(alpha: 0.7),
                                  fontSize: 13,
                                  fontWeight: FontWeight.w500,
                                ),
                              ),
                              for (final color in [
                                Colors.grey.withValues(alpha: 0.2),
                                Colors.white.withValues(alpha: 0.5),
                                Colors.red.withValues(alpha: 0.5),
                                Colors.green.withValues(alpha: 0.5),
                                Colors.blue.withValues(alpha: 0.5),
                              ])
                                GestureDetector(
                                  onTap: () =>
                                      setState(() => _ringsColor = color),
                                  child: Container(
                                    margin: const EdgeInsets.only(left: 8),
                                    width: 24,
                                    height: 24,
                                    decoration: BoxDecoration(
                                      color: color,
                                      shape: BoxShape.circle,
                                      border: Border.all(
                                        color: _ringsColor == color
                                            ? cs.primary
                                            : Colors.transparent,
                                        width: 2,
                                      ),
                                    ),
                                  ),
                                ),
                            ],
                          ),
                          Row(
                            children: [
                              Text(
                                'Border Color',
                                style: TextStyle(
                                  color: cs.onSurface.withValues(alpha: 0.7),
                                  fontSize: 13,
                                  fontWeight: FontWeight.w500,
                                ),
                              ),
                              for (final color in [
                                Colors.grey.withValues(alpha: 0.2),
                                Colors.white.withValues(alpha: 0.5),
                                Colors.red.withValues(alpha: 0.5),
                                Colors.green.withValues(alpha: 0.5),
                                Colors.blue.withValues(alpha: 0.5),
                              ])
                                GestureDetector(
                                  onTap: () =>
                                      setState(() => _borderColor = color),
                                  child: Container(
                                    margin: const EdgeInsets.only(left: 8),
                                    width: 24,
                                    height: 24,
                                    decoration: BoxDecoration(
                                      color: color,
                                      shape: BoxShape.circle,
                                      border: Border.all(
                                        color: _borderColor == color
                                            ? cs.primary
                                            : Colors.transparent,
                                        width: 2,
                                      ),
                                    ),
                                  ),
                                ),
                            ],
                          ),
                        ],
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// ─── Slider row component ──────────────────────────────────────────────────

class _SliderRow extends StatelessWidget {
  final String label;
  final double value;
  final double min;
  final double max;
  final int divisions;
  final ValueChanged<double> onChanged;
  final Color color;

  const _SliderRow({
    required this.label,
    required this.value,
    required this.min,
    required this.max,
    required this.divisions,
    required this.onChanged,
    required this.color,
  });

  @override
  Widget build(BuildContext context) {
    final cs = Theme.of(context).colorScheme;
    return Row(
      children: [
        SizedBox(
          width: 52,
          child: Text(
            label,
            style: TextStyle(
              color: cs.onSurface.withValues(alpha: 0.7),
              fontSize: 13,
              fontWeight: FontWeight.w500,
            ),
          ),
        ),
        Expanded(
          child: SliderTheme(
            data: SliderThemeData(
              activeTrackColor: color,
              inactiveTrackColor: color.withValues(alpha: 0.2),
              thumbColor: color,
              overlayColor: color.withValues(alpha: 0.12),
              trackHeight: 3,
              thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 8),
            ),
            child: Slider(
              min: min,
              max: max,
              divisions: divisions,
              value: value,
              onChanged: onChanged,
            ),
          ),
        ),
        SizedBox(
          width: 24,
          child: Text(
            '${value.round()}',
            textAlign: TextAlign.right,
            style: TextStyle(
              color: color,
              fontSize: 14,
              fontWeight: FontWeight.w700,
            ),
          ),
        ),
      ],
    );
  }
}
14
likes
160
points
438
downloads
screenshot

Documentation

API reference

Publisher

verified publisherbaasautotraders.in

Weekly Downloads

A versatile Flutter package for creating beautiful and customizable radar charts. Easily visualize multivariate data for comparisons and analysis.

Homepage
Repository (GitHub)
View/report issues

Topics

#chart #visualization #radar-chart #graph #spider

License

MIT (license)

Dependencies

flutter

More

Packages that depend on radar_chart_plus