ecg_paper 0.1.0 copy "ecg_paper: ^0.1.0" to clipboard
ecg_paper: ^0.1.0 copied to clipboard

Scrollable ECG "paper" widget (25 mm/s, 10 mm/mV) for Flutter. Feed mV samples and sample rate to render clinical-style grids and trace.

example/lib/main.dart

import 'dart:io';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:screenshot/screenshot.dart';
import 'package:ecg_paper/ecg_paper.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: DemoPage());
  }
}

class DemoPage extends StatefulWidget {
  const DemoPage({super.key});
  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  final fs = 512.0;
  final seconds = 8.0;
  late final List<double> samplesMv;
  final sc = ScreenshotController();
  bool fixedY = false;
  bool decimation = true;

  @override
  void initState() {
    super.initState();
    final n = (fs * seconds).toInt();
    samplesMv = List<double>.generate(n, (i) {
      final t = i / fs;
      // crude pseudo-ECG shape: base sine + small spikes
      final s = math.sin(2 * math.pi * 1.3 * t);
      final qrs = (i % 60 == 0) ? 1.8 : 0.0; // sparse spike
      final noise = 0.05 * math.sin(2 * math.pi * 50 * t);
      return 0.7 * s + qrs + noise; // ~±1 mV
    });
  }

  Future<void> _savePng() async {
    final bytes = await sc.capture(pixelRatio: 2.0);
    if (bytes == null) return;
    final dir = await getApplicationDocumentsDirectory();
    final file = File('${dir.path}/ecg_${DateTime.now().millisecondsSinceEpoch}.png');
    await file.writeAsBytes(bytes);
    if (!mounted) return;
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Saved: ${file.path}')),
    );
  }

  @override
  Widget build(BuildContext context) {
    final style = EcgStyle(
      height: 220,
      fixedYRangeMv: fixedY ? 2.0 : null, // toggle lock ±2 mV
    );

    final widgetECG = EcgScrollableWidget(
      samples: samplesMv,
      sampleRateHz: fs,
      durationSeconds: seconds,
      style: style,
      enableDecimation: decimation,
      autoDetectUnits: true,
    );

    return Scaffold(
      appBar: AppBar(title: const Text('ECG Paper Demo')),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Screenshot(controller: sc, child: widgetECG),
            const SizedBox(height: 12),
            Wrap(
              spacing: 12,
              children: [
                ElevatedButton(
                  onPressed: _savePng,
                  child: const Text('Save PNG'),
                ),
                FilterChip(
                  label: const Text('Fixed ±2 mV'),
                  selected: fixedY,
                  onSelected: (v) => setState(() => fixedY = v),
                ),
                FilterChip(
                  label: const Text('Decimation'),
                  selected: decimation,
                  onSelected: (v) => setState(() => decimation = v),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
1
likes
150
points
14
downloads

Publisher

unverified uploader

Weekly Downloads

Scrollable ECG "paper" widget (25 mm/s, 10 mm/mV) for Flutter. Feed mV samples and sample rate to render clinical-style grids and trace.

Repository (GitHub)
View/report issues

Topics

#ecg #chart #healthcare #visualization #flutter

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on ecg_paper