drafter 0.2.0 copy "drafter: ^0.2.0" to clipboard
drafter: ^0.2.0 copied to clipboard

A premium, dependency-free Flutter charting library with ~27 chart types, smooth Catmull-Rom curves, soft gradient fills and a left-to-right reveal.

example/lib/main.dart

/*
 * Designed and developed by 2024 androidpoet (Ranbir Singh)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import 'dart:math' as math;

import 'package:drafter/drafter.dart';
import 'package:flutter/material.dart';

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

/// Deterministic RNG so the gallery always shows the same sample data.
final math.Random _rng = math.Random(7);

const List<String> _months = [
  'Jan',
  'Feb',
  'Mar',
  'Apr',
  'May',
  'Jun',
  'Jul',
  'Aug',
];

/// A list of labeled monthly points in roughly [10, 100].
List<ChartPoint> _monthlyPoints({double base = 40, double swing = 45}) {
  return [
    for (final m in _months)
      ChartPoint(m, (base + _rng.nextDouble() * swing).roundToDouble()),
  ];
}

/// A run of raw values in [lo, hi].
List<double> _values(int n, {double lo = 10, double hi = 100}) => [
  for (var i = 0; i < n; i++)
    (lo + _rng.nextDouble() * (hi - lo)).roundToDouble(),
];

/// A small set of named, colored series over [count] points.
List<ChartSeries> _series(int seriesCount, int count) {
  return [
    for (var s = 0; s < seriesCount; s++)
      ChartSeries(
        name: 'S${s + 1}',
        color: DrafterColors.palette[s % DrafterColors.palette.length],
        values: _values(count, lo: 15, hi: 90),
      ),
  ];
}

class DemoApp extends StatelessWidget {
  const DemoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Drafter',
      debugShowCheckedModeBanner: false,
      theme: ThemeData.light(
        useMaterial3: true,
      ).copyWith(scaffoldBackgroundColor: Colors.white),
      home: const _Gallery(),
    );
  }
}

class _Gallery extends StatelessWidget {
  const _Gallery();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        backgroundColor: Colors.white,
        surfaceTintColor: Colors.white,
        title: const Text('Drafter — 27 charts'),
      ),
      body: DrafterTheme(
        colors: DrafterThemeColors.light,
        child: GridView(
          padding: const EdgeInsets.fromLTRB(14, 14, 14, 28),
          gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
            maxCrossAxisExtent: 460,
            mainAxisExtent: 300,
            crossAxisSpacing: 14,
            mainAxisSpacing: 14,
          ),
          children: _cards(),
        ),
      ),
    );
  }

  List<Widget> _cards() {
    final palette = DrafterColors.palette;

    // ---- Lines ----
    final linePoints = _monthlyPoints();
    final lineSeries2 = _series(2, _months.length);
    final lineSeries3 = _series(3, _months.length);
    final stackSeries = _series(3, _months.length);

    // ---- Bars ----
    final bars = [
      for (var i = 0; i < _months.length; i++)
        BarItem(
          _months[i],
          (20 + _rng.nextDouble() * 70).roundToDouble(),
          color: palette[i % palette.length],
        ),
    ];
    final groupedBars = _series(3, 5);
    final stackedBars = _series(3, 5);
    final histValues = [
      for (var i = 0; i < 200; i++) (50 + _rng.nextDouble() * 50 - 25),
    ];
    final waterfall = [
      WaterfallStep('Sales', 60, color: DrafterColors.green),
      WaterfallStep('Refund', -18, color: DrafterColors.coral),
      WaterfallStep('Fees', -12, color: DrafterColors.amber),
      WaterfallStep('Bonus', 24, color: DrafterColors.teal),
    ];

    // ---- Pie / donut ----
    final pieSlices = [
      for (var i = 0; i < 5; i++)
        PieSlice(
          value: (10 + _rng.nextDouble() * 40).roundToDouble(),
          color: palette[i % palette.length],
          label: 'P${i + 1}',
        ),
    ];

    // ---- Radar ----
    const axes = ['Speed', 'Power', 'Range', 'Agility', 'Focus', 'Stamina'];
    final radarSeries = [
      RadarSeries(
        color: DrafterColors.blue,
        values: {for (final a in axes) a: 0.4 + _rng.nextDouble() * 0.55},
      ),
      RadarSeries(
        color: DrafterColors.coral,
        values: {for (final a in axes) a: 0.35 + _rng.nextDouble() * 0.55},
      ),
    ];

    // ---- Polar ----
    final polarSlices = [
      for (var i = 0; i < 6; i++)
        PolarSlice(
          label: 'Q${i + 1}',
          value: (20 + _rng.nextDouble() * 80).roundToDouble(),
          color: palette[i % palette.length],
        ),
    ];

    // ---- Scatter ----
    final scatter = [
      for (var i = 0; i < 24; i++)
        ScatterPoint(
          x: (5 + _rng.nextDouble() * 95).roundToDouble(),
          y: (5 + _rng.nextDouble() * 95).roundToDouble(),
          color: palette[i % palette.length],
        ),
    ];

    // ---- Bubble ----
    final bubbles = [
      [
        for (var i = 0; i < 6; i++)
          BubbleData(
            x: (10 + _rng.nextDouble() * 80).roundToDouble(),
            y: (10 + _rng.nextDouble() * 80).roundToDouble(),
            size: 1 + _rng.nextDouble() * 5,
            color: DrafterColors.violet,
          ),
      ],
      [
        for (var i = 0; i < 6; i++)
          BubbleData(
            x: (10 + _rng.nextDouble() * 80).roundToDouble(),
            y: (10 + _rng.nextDouble() * 80).roundToDouble(),
            size: 1 + _rng.nextDouble() * 5,
            color: DrafterColors.teal,
          ),
      ],
    ];

    // ---- Heatmap (fixed end date, ~120 days) ----
    final end = DateTime(2026, 6, 25);
    final contributions = [
      for (var d = 119; d >= 0; d--)
        ContributionData(
          date: end.subtract(Duration(days: d)),
          count: _rng.nextInt(12),
        ),
    ];

    // ---- Funnel ----
    final funnel = [
      FunnelStage(label: 'Visits', value: 1000, color: DrafterColors.blue),
      FunnelStage(label: 'Signups', value: 640, color: DrafterColors.teal),
      FunnelStage(label: 'Trials', value: 360, color: DrafterColors.violet),
      FunnelStage(label: 'Paid', value: 140, color: DrafterColors.green),
    ];

    // ---- Bullet ----
    final bullets = [
      BulletMetric(
        label: 'Revenue',
        value: 76,
        target: 85,
        ranges: const [50, 75, 100],
        color: DrafterColors.indigo,
      ),
      BulletMetric(
        label: 'Profit',
        value: 58,
        target: 70,
        ranges: const [40, 70, 100],
        color: DrafterColors.teal,
      ),
      BulletMetric(
        label: 'Growth',
        value: 92,
        target: 80,
        ranges: const [50, 75, 100],
        color: DrafterColors.green,
      ),
    ];

    // ---- Box plot ----
    final boxes = [
      for (var i = 0; i < 4; i++)
        BoxGroup(
          label: 'G${i + 1}',
          min: 10 + _rng.nextDouble() * 10,
          q1: 30 + _rng.nextDouble() * 10,
          median: 48 + _rng.nextDouble() * 10,
          q3: 66 + _rng.nextDouble() * 10,
          max: 88 + _rng.nextDouble() * 10,
          color: palette[i % palette.length],
        ),
    ];

    // ---- Treemap ----
    final treemap = [
      for (var i = 0; i < 7; i++)
        TreemapItem(
          label: 'T${i + 1}',
          value: (20 + _rng.nextDouble() * 90).roundToDouble(),
          color: palette[i % palette.length],
        ),
    ];

    // ---- Sunburst ----
    final sunburst = [
      SunburstNode(
        label: 'Apps',
        value: 50,
        color: DrafterColors.blue,
        children: [
          SunburstNode(label: 'iOS', value: 30, color: DrafterColors.teal),
          SunburstNode(label: 'And', value: 20, color: DrafterColors.violet),
        ],
      ),
      SunburstNode(
        label: 'Web',
        value: 30,
        color: DrafterColors.amber,
        children: [
          SunburstNode(label: 'SSR', value: 18, color: DrafterColors.green),
          SunburstNode(label: 'SPA', value: 12, color: DrafterColors.coral),
        ],
      ),
      SunburstNode(
        label: 'Other',
        value: 20,
        color: DrafterColors.pink,
        children: [
          SunburstNode(label: 'CLI', value: 20, color: DrafterColors.indigo),
        ],
      ),
    ];

    // ---- Sankey ----
    final sankeyNodes = [
      SankeyNode(
        id: 'a',
        label: 'Source',
        column: 0,
        color: DrafterColors.blue,
      ),
      SankeyNode(
        id: 'b',
        label: 'Direct',
        column: 0,
        color: DrafterColors.teal,
      ),
      SankeyNode(
        id: 'c',
        label: 'Mobile',
        column: 1,
        color: DrafterColors.violet,
      ),
      SankeyNode(
        id: 'd',
        label: 'Desktop',
        column: 1,
        color: DrafterColors.amber,
      ),
      SankeyNode(
        id: 'e',
        label: 'Convert',
        column: 2,
        color: DrafterColors.green,
      ),
    ];
    const sankeyLinks = [
      SankeyLink(from: 'a', to: 'c', value: 30),
      SankeyLink(from: 'a', to: 'd', value: 20),
      SankeyLink(from: 'b', to: 'c', value: 15),
      SankeyLink(from: 'b', to: 'd', value: 25),
      SankeyLink(from: 'c', to: 'e', value: 28),
      SankeyLink(from: 'd', to: 'e', value: 32),
    ];

    // ---- Gantt ----
    const gantt = [
      GanttTask(name: 'Design', startMonth: 0, duration: 2),
      GanttTask(name: 'Build', startMonth: 2, duration: 3),
      GanttTask(name: 'Test', startMonth: 4, duration: 2),
      GanttTask(name: 'Ship', startMonth: 6, duration: 1),
    ];

    // ---- Candles ----
    final candles = <Candle>[];
    var price = 100.0;
    for (var i = 0; i < 18; i++) {
      final open = price;
      final close = open + (_rng.nextDouble() - 0.5) * 14;
      final high = math.max(open, close) + _rng.nextDouble() * 6;
      final low = math.min(open, close) - _rng.nextDouble() * 6;
      candles.add(
        Candle(
          label: 'D${i + 1}',
          open: open,
          high: high,
          low: low,
          close: close,
        ),
      );
      price = close;
    }
    const movingAverages = [
      MovingAverage(period: 5, color: Color(0xFF4C8DF6)),
      MovingAverage(period: 10, color: Color(0xFFF6B24C)),
    ];

    return [
      _ChartCard(
        title: 'Line',
        child: LineChart(points: linePoints),
      ),
      _ChartCard(
        title: 'Grouped Line',
        child: GroupedLineChart(series: lineSeries2, categories: _months),
      ),
      _ChartCard(
        title: 'Stacked Line',
        child: StackedLineChart(series: stackSeries, categories: _months),
      ),
      _ChartCard(
        title: 'Area',
        child: AreaChart(points: _monthlyPoints()),
      ),
      _ChartCard(
        title: 'Step Line',
        child: StepLineChart(points: _monthlyPoints()),
      ),
      _ChartCard(
        title: 'Simple Bar',
        child: SimpleBarChart(bars: bars),
      ),
      _ChartCard(
        title: 'Grouped Bar',
        child: GroupedBarChart(
          series: groupedBars,
          categories: const ['A', 'B', 'C', 'D', 'E'],
        ),
      ),
      _ChartCard(
        title: 'Stacked Bar',
        child: StackedBarChart(
          series: stackedBars,
          categories: const ['A', 'B', 'C', 'D', 'E'],
        ),
      ),
      _ChartCard(
        title: 'Histogram',
        child: Histogram(values: histValues, binCount: 8),
      ),
      _ChartCard(
        title: 'Waterfall',
        child: WaterfallChart(
          steps: waterfall,
          initialValue: 30,
          startLabel: 'Start',
          totalLabel: 'Total',
        ),
      ),
      _ChartCard(
        title: 'Pie',
        child: PieChart(slices: pieSlices),
      ),
      _ChartCard(
        title: 'Donut',
        child: DonutChart(slices: pieSlices),
      ),
      _ChartCard(
        title: 'Radar',
        child: RadarChart(series: radarSeries),
      ),
      _ChartCard(
        title: 'Polar Area',
        child: PolarAreaChart(slices: polarSlices),
      ),
      const _ChartCard(
        title: 'Gauge',
        child: GaugeChart(value: 72, label: 'Score'),
      ),
      _ChartCard(
        title: 'Scatter Plot',
        child: ScatterPlot(points: scatter),
      ),
      _ChartCard(
        title: 'Bubble',
        child: BubbleChart(series: bubbles),
      ),
      _ChartCard(
        title: 'Heatmap',
        child: Heatmap(contributions: contributions),
      ),
      _ChartCard(
        title: 'Funnel',
        child: FunnelChart(stages: funnel),
      ),
      _ChartCard(
        title: 'Bullet',
        child: BulletChart(metrics: bullets),
      ),
      _ChartCard(
        title: 'Box Plot',
        child: BoxPlotChart(groups: boxes),
      ),
      _ChartCard(
        title: 'Treemap',
        child: TreemapChart(items: treemap),
      ),
      _ChartCard(
        title: 'Sunburst',
        child: SunburstChart(roots: sunburst),
      ),
      _ChartCard(
        title: 'Sankey',
        child: SankeyChart(nodes: sankeyNodes, links: sankeyLinks),
      ),
      const _ChartCard(
        title: 'Gantt',
        child: GanttChart(tasks: gantt),
      ),
      _ChartCard(
        title: 'Stream Graph',
        child: StreamGraphChart(series: lineSeries3, categories: _months),
      ),
      _ChartCard(
        title: 'Candlestick',
        child: CandlestickChart(
          candles: candles,
          movingAverages: movingAverages,
        ),
      ),
    ];
  }
}

/// A white, softly-shadowed card with a small grey title above a 220pt chart.
class _ChartCard extends StatelessWidget {
  const _ChartCard({required this.title, required this.child});

  final String title;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.fromLTRB(16, 14, 16, 16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(18),
        border: Border.all(color: const Color(0x14000000)),
        boxShadow: const [
          BoxShadow(
            color: Color(0x0F000000),
            blurRadius: 16,
            offset: Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            title,
            style: const TextStyle(
              color: Color(0xFF55606C),
              fontSize: 13,
              fontWeight: FontWeight.w600,
            ),
          ),
          const SizedBox(height: 10),
          Expanded(child: child),
        ],
      ),
    );
  }
}
7
likes
160
points
206
downloads

Documentation

Documentation
API reference

Publisher

verified publisherandroidpoet.dev

Weekly Downloads

A premium, dependency-free Flutter charting library with ~27 chart types, smooth Catmull-Rom curves, soft gradient fills and a left-to-right reveal.

Repository (GitHub)
View/report issues
Contributing

Topics

#chart #visualization #graph #diagram

License

Apache-2.0 (license)

Dependencies

flutter

More

Packages that depend on drafter