activity_files

Pub Package Pub Points Pub Likes codecov License GitHub Stars GitHub Forks GitHub Issues

Licensed under the BSD 3-Clause License. See LICENSE for details.

A pure Dart toolkit for reading, editing, validating, and writing workoutactivity files. activity_files provides format-agnostic models, robust GPX, TCX and FIT parsers/encoders, transformation utilities, and a CLI for quick conversionsor validation.

Highlights

  • Format-agnostic RawActivity model with builders/editors for GPS points, laps, channels, and device metadata.
  • Ergonomic ActivityFiles facade plus CLI that load, normalize, validate, and export GPX/TCX/FIT payloads in a handful of calls.
  • Stream-aware builders (builderFromStreams, convertAndExport) so servers can feed timestamp/value tuples directly without manual model wiring.
  • Diagnostics-first results with sport mappers, validation stats, and namespace-tolerant parsers that never throw on untrusted files.
  • Channel cursors, resampling helpers, and encoder options keep exports fast while letting you control tolerances/precision.
  • Flexible export targets: emit GPX 1.0/1.1 and TCX v1/v2 via EncoderOptions or CLI flags; FIT encoding focuses on core workout fields (developer fields and full SDK coverage intentionally out of scope today).

See the usage guide for the full feature tour and performance notes.

Getting started

Add the package to pubspec.yaml:

dependencies:
  activity_files: ^0.4.0

Then install dependencies:

dart pub get

See example/main.dart for a complete sample or jump straight into the facade:

import 'package:activity_files/activity_files.dart';

Future<void> convertGpxToFit(Uint8List bytes) async {
  // 1) Load + detect format; throws if format cannot be inferred.
  final load = await ActivityFiles.load(
    bytes,
    useIsolate: true,
  );
  if (load.hasErrors) {
    throw StateError('Load failed:\n${load.diagnosticsSummary()}');
  }

  // 2) Normalize (sort/dedup + trim invalid points) before exporting.
  final normalized = ActivityFiles.normalizeActivity(load.activity);

  // 3) Export with validation so warnings/errors surface alongside the payload.
  final export = await ActivityFiles.export(
    activity: normalized,
    to: ActivityFileFormat.fit,
    runValidation: true,
  );
  if (export.hasErrors) {
    throw StateError('Export failed:\n${export.diagnosticsSummary()}');
  }

  // 4) Use the payload. FIT is binary; GPX/TCX use `asString()`.
  final fitBytes = export.asBytes();
  // upload(fitBytes);
}

Need to handle large uploads? The loader caps inline payloads/streams at 64MB (ActivityFiles.defaultMaxPayloadBytes). For bigger files, stream and convert without buffering everything:

Future<ActivityExportResult> streamConvert(File input) {
  return ActivityFiles.convertAndExportStream(
    source: input.openRead(),
    from: ActivityFileFormat.gpx,
    to: ActivityFileFormat.tcx,
    parseInIsolate: true,
    exportInIsolate: true,
    runValidation: true,
  );
}

The usage guide now hosts the detailed Flutter widget, streaming, CLI, and isolate walkthroughs that previously lived in this README. For a complete, runnable walkthrough (load → normalize → validate → export), see example/main.dart.

Source inputs & isolates

  • String inputs are treated as inline payloads. Pass a File (preferred) or set allowFilePaths: true when you explicitly trust the string to reference local storage.
  • useIsolate / exportInIsolate offload work when isolates are available. Gate both flags with !kIsWeb for Flutter web builds.
  • Stream-backed loads keep a replayable buffer so bytesPayload remains usable even after parsing completes.
  • Payload limits: inline strings/bytes and buffered streams are capped at 64MB (ActivityFiles.defaultMaxPayloadBytes). Oversized inputs throw FormatException (and the CLI rejects them) to avoid unbounded memory use; split very large uploads before parsing.

Diagnostics-first workflows

Parsing, conversion, and export helpers never throw for malformed files—they surface issues via ParseDiagnostics on the result. Always check hasErrors, diagnosticsSummary, or the diagnostics list before trusting the returned RawActivity. The error-handling section shows ready-to-copy patterns for both load and export flows.

Format limitations

  • GPX 1.0/1.1 are both parsed/encoded, including metadata/track names, descriptions, and extensions. The parser flattens all tracks/segments into a single stream and ignores waypoints/routes, so files that rely on multiple tracks or saved waypoints will lose that structure on load/export.
  • TCX: only the first <Activity> is parsed. Additional activities in a single file are skipped.

If you need full fidelity for multi-activity or waypoint-heavy files, split inputs before loading or extend the parsers to keep those constructs.

Async export & streaming

ActivityExportRequest, convertAndExport, and exportAsync share the same builder API for raw location/channel streams plus isolate toggles so you can pin heavy work off the UI thread. See doc/usage_guide.md#async-export--streaming for streamed conversions, CLI pipelines, and memory caveats.

For advanced editing pipelines, parser/encoder samples, and CLI walkthroughs, see the usage guide, which retains the detailed examples previously listed here.

Contributing

Issues and pull requests are welcome, especially for additional format fixtures. The package is released under the BSD 3-Clause license.

Libraries

activity_files
Activity files parsing, editing, and encoding toolkit.