diff --git a/CHANGELOG.md b/CHANGELOG.md
index f96c401..a84809d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## [0.5.2] - 2020-08-28 18:24:45
+
+- Adding trim paths.
+
 ## [0.5.1] - 2020-08-26 18:09:13
 
 - Bumping version number to match the runtime file version (5.1).
diff --git a/README.md b/README.md
index c2a1943..c755684 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
 ## Add to pubspec.yaml
 ```yaml
 dependencies:
-  rive: ^0.0.1+3
+  rive: ^0.5.2
 ```
 
 ## Example
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 7499034..cb16ca4 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -94,7 +94,7 @@ packages:
       path: ".."
       relative: true
     source: path
-    version: "0.5.1"
+    version: "0.5.2"
   sky_engine:
     dependency: transitive
     description: flutter
diff --git a/lib/src/generated/rive_core_context.dart b/lib/src/generated/rive_core_context.dart
index 660a75c..2ce723d 100644
--- a/lib/src/generated/rive_core_context.dart
+++ b/lib/src/generated/rive_core_context.dart
@@ -38,6 +38,7 @@ import 'package:rive/src/generated/shapes/paint/radial_gradient_base.dart';
 import 'package:rive/src/generated/shapes/paint/shape_paint_base.dart';
 import 'package:rive/src/generated/shapes/paint/solid_color_base.dart';
 import 'package:rive/src/generated/shapes/paint/stroke_base.dart';
+import 'package:rive/src/generated/shapes/paint/trim_path_base.dart';
 import 'package:rive/src/generated/shapes/parametric_path_base.dart';
 import 'package:rive/src/generated/shapes/path_composer_base.dart';
 import 'package:rive/src/generated/shapes/path_vertex_base.dart';
@@ -76,6 +77,7 @@ import 'package:rive/src/rive_core/shapes/paint/linear_gradient.dart';
 import 'package:rive/src/rive_core/shapes/paint/radial_gradient.dart';
 import 'package:rive/src/rive_core/shapes/paint/solid_color.dart';
 import 'package:rive/src/rive_core/shapes/paint/stroke.dart';
+import 'package:rive/src/rive_core/shapes/paint/trim_path.dart';
 import 'package:rive/src/rive_core/shapes/path_composer.dart';
 import 'package:rive/src/rive_core/shapes/points_path.dart';
 import 'package:rive/src/rive_core/shapes/rectangle.dart';
@@ -115,6 +117,8 @@ class RiveCoreContext {
         return SolidColor();
       case GradientStopBase.typeKey:
         return GradientStop();
+      case TrimPathBase.typeKey:
+        return TrimPath();
       case FillBase.typeKey:
         return Fill();
       case NodeBase.typeKey:
@@ -348,6 +352,26 @@ class RiveCoreContext {
           object.position = value;
         }
         break;
+      case TrimPathBase.startPropertyKey:
+        if (object is TrimPathBase && value is double) {
+          object.start = value;
+        }
+        break;
+      case TrimPathBase.endPropertyKey:
+        if (object is TrimPathBase && value is double) {
+          object.end = value;
+        }
+        break;
+      case TrimPathBase.offsetPropertyKey:
+        if (object is TrimPathBase && value is double) {
+          object.offset = value;
+        }
+        break;
+      case TrimPathBase.modeValuePropertyKey:
+        if (object is TrimPathBase && value is int) {
+          object.modeValue = value;
+        }
+        break;
       case FillBase.fillRulePropertyKey:
         if (object is FillBase && value is int) {
           object.fillRule = value;
@@ -653,6 +677,7 @@ class RiveCoreContext {
       case ComponentBase.parentIdPropertyKey:
       case StrokeBase.capPropertyKey:
       case StrokeBase.joinPropertyKey:
+      case TrimPathBase.modeValuePropertyKey:
       case FillBase.fillRulePropertyKey:
       case DrawableBase.drawOrderPropertyKey:
       case DrawableBase.blendModeValuePropertyKey:
@@ -682,6 +707,9 @@ class RiveCoreContext {
       case LinearGradientBase.opacityPropertyKey:
       case StrokeBase.thicknessPropertyKey:
       case GradientStopBase.positionPropertyKey:
+      case TrimPathBase.startPropertyKey:
+      case TrimPathBase.endPropertyKey:
+      case TrimPathBase.offsetPropertyKey:
       case TransformComponentBase.rotationPropertyKey:
       case TransformComponentBase.scaleXPropertyKey:
       case TransformComponentBase.scaleYPropertyKey:
@@ -772,6 +800,8 @@ class RiveCoreContext {
         return (object as StrokeBase).cap;
       case StrokeBase.joinPropertyKey:
         return (object as StrokeBase).join;
+      case TrimPathBase.modeValuePropertyKey:
+        return (object as TrimPathBase).modeValue;
       case FillBase.fillRulePropertyKey:
         return (object as FillBase).fillRule;
       case DrawableBase.drawOrderPropertyKey:
@@ -838,6 +868,12 @@ class RiveCoreContext {
         return (object as StrokeBase).thickness;
       case GradientStopBase.positionPropertyKey:
         return (object as GradientStopBase).position;
+      case TrimPathBase.startPropertyKey:
+        return (object as TrimPathBase).start;
+      case TrimPathBase.endPropertyKey:
+        return (object as TrimPathBase).end;
+      case TrimPathBase.offsetPropertyKey:
+        return (object as TrimPathBase).offset;
       case TransformComponentBase.rotationPropertyKey:
         return (object as TransformComponentBase).rotation;
       case TransformComponentBase.scaleXPropertyKey:
@@ -1001,6 +1037,9 @@ class RiveCoreContext {
       case StrokeBase.joinPropertyKey:
         (object as StrokeBase).join = value;
         break;
+      case TrimPathBase.modeValuePropertyKey:
+        (object as TrimPathBase).modeValue = value;
+        break;
       case FillBase.fillRulePropertyKey:
         (object as FillBase).fillRule = value;
         break;
@@ -1092,6 +1131,15 @@ class RiveCoreContext {
       case GradientStopBase.positionPropertyKey:
         (object as GradientStopBase).position = value;
         break;
+      case TrimPathBase.startPropertyKey:
+        (object as TrimPathBase).start = value;
+        break;
+      case TrimPathBase.endPropertyKey:
+        (object as TrimPathBase).end = value;
+        break;
+      case TrimPathBase.offsetPropertyKey:
+        (object as TrimPathBase).offset = value;
+        break;
       case TransformComponentBase.rotationPropertyKey:
         (object as TransformComponentBase).rotation = value;
         break;
diff --git a/lib/src/generated/shapes/paint/trim_path_base.dart b/lib/src/generated/shapes/paint/trim_path_base.dart
new file mode 100644
index 0000000..4b10e46
--- /dev/null
+++ b/lib/src/generated/shapes/paint/trim_path_base.dart
@@ -0,0 +1,90 @@
+/// Core automatically generated
+/// lib/src/generated/shapes/paint/trim_path_base.dart.
+/// Do not modify manually.
+
+import 'package:rive/src/generated/component_base.dart';
+import 'package:rive/src/rive_core/component.dart';
+
+abstract class TrimPathBase extends Component {
+  static const int typeKey = 47;
+  @override
+  int get coreType => TrimPathBase.typeKey;
+  @override
+  Set<int> get coreTypes => {TrimPathBase.typeKey, ComponentBase.typeKey};
+
+  /// --------------------------------------------------------------------------
+  /// Start field with key 114.
+  double _start = 0;
+  static const int startPropertyKey = 114;
+  double get start => _start;
+
+  /// Change the [_start] field value.
+  /// [startChanged] will be invoked only if the field's value has changed.
+  set start(double value) {
+    if (_start == value) {
+      return;
+    }
+    double from = _start;
+    _start = value;
+    startChanged(from, value);
+  }
+
+  void startChanged(double from, double to);
+
+  /// --------------------------------------------------------------------------
+  /// End field with key 115.
+  double _end = 0;
+  static const int endPropertyKey = 115;
+  double get end => _end;
+
+  /// Change the [_end] field value.
+  /// [endChanged] will be invoked only if the field's value has changed.
+  set end(double value) {
+    if (_end == value) {
+      return;
+    }
+    double from = _end;
+    _end = value;
+    endChanged(from, value);
+  }
+
+  void endChanged(double from, double to);
+
+  /// --------------------------------------------------------------------------
+  /// Offset field with key 116.
+  double _offset = 0;
+  static const int offsetPropertyKey = 116;
+  double get offset => _offset;
+
+  /// Change the [_offset] field value.
+  /// [offsetChanged] will be invoked only if the field's value has changed.
+  set offset(double value) {
+    if (_offset == value) {
+      return;
+    }
+    double from = _offset;
+    _offset = value;
+    offsetChanged(from, value);
+  }
+
+  void offsetChanged(double from, double to);
+
+  /// --------------------------------------------------------------------------
+  /// ModeValue field with key 117.
+  int _modeValue = 0;
+  static const int modeValuePropertyKey = 117;
+  int get modeValue => _modeValue;
+
+  /// Change the [_modeValue] field value.
+  /// [modeValueChanged] will be invoked only if the field's value has changed.
+  set modeValue(int value) {
+    if (_modeValue == value) {
+      return;
+    }
+    int from = _modeValue;
+    _modeValue = value;
+    modeValueChanged(from, value);
+  }
+
+  void modeValueChanged(int from, int to);
+}
diff --git a/lib/src/rive_core/animation/interpolator.dart b/lib/src/rive_core/animation/interpolator.dart
index 7ec2734..88d4e4c 100644
--- a/lib/src/rive_core/animation/interpolator.dart
+++ b/lib/src/rive_core/animation/interpolator.dart
@@ -1,4 +1,3 @@
-
 abstract class Interpolator {
   int get id;
   double transform(double value);
diff --git a/lib/src/rive_core/artboard.dart b/lib/src/rive_core/artboard.dart
index 81a0813..7f08b8e 100644
--- a/lib/src/rive_core/artboard.dart
+++ b/lib/src/rive_core/artboard.dart
@@ -82,6 +82,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
   @override
   void heightChanged(double from, double to) {
     addDirt(ComponentDirt.worldTransform);
+    invalidateStrokeEffects();
   }
 
   void onComponentDirty(Component component) {
@@ -122,6 +123,7 @@ class Artboard extends ArtboardBase with ShapePaintContainer {
   @override
   void widthChanged(double from, double to) {
     addDirt(ComponentDirt.worldTransform);
+    invalidateStrokeEffects();
   }
 
   @override
diff --git a/lib/src/rive_core/bones/skin.dart b/lib/src/rive_core/bones/skin.dart
index 1f70afb..c081a22 100644
--- a/lib/src/rive_core/bones/skin.dart
+++ b/lib/src/rive_core/bones/skin.dart
@@ -3,7 +3,6 @@ import 'package:rive/src/rive_core/bones/skinnable.dart';
 import 'package:rive/src/rive_core/bones/tendon.dart';
 import 'package:rive/src/rive_core/component.dart';
 import 'package:rive/src/rive_core/math/mat2d.dart';
-import 'package:rive/src/rive_core/math/transform_components.dart';
 import 'package:rive/src/rive_core/shapes/path_vertex.dart';
 import 'package:rive/src/generated/bones/skin_base.dart';
 export 'package:rive/src/generated/bones/skin_base.dart';
@@ -35,8 +34,6 @@ class Skin extends SkinBase {
     for (final tendon in _tendons) {
       var boneWorld = tendon.bone.worldTransform;
       var wt = Mat2D.multiply(temp, boneWorld, tendon.inverseBind);
-      var tc = TransformComponents();
-      Mat2D.decompose(boneWorld, tc);
       _boneTransforms[bidx++] = wt[0];
       _boneTransforms[bidx++] = wt[1];
       _boneTransforms[bidx++] = wt[2];
diff --git a/lib/src/rive_core/component_dirt.dart b/lib/src/rive_core/component_dirt.dart
index 6d93a8d..4af0a6a 100644
--- a/lib/src/rive_core/component_dirt.dart
+++ b/lib/src/rive_core/component_dirt.dart
@@ -9,4 +9,5 @@ class ComponentDirt {
   static const int paint = 1 << 7;
   static const int stops = 1 << 8;
   static const int clip = 1 << 9;
+  static const int blendMode = 1 << 10;
 }
diff --git a/lib/src/rive_core/runtime/runtime_header.dart b/lib/src/rive_core/runtime/runtime_header.dart
index ca3c829..8cf26af 100644
--- a/lib/src/rive_core/runtime/runtime_header.dart
+++ b/lib/src/rive_core/runtime/runtime_header.dart
@@ -5,7 +5,7 @@ import 'exceptions/rive_format_error_exception.dart';
 
 class RuntimeHeader {
   static const int majorVersion = 5;
-  static const int minorVersion = 1;
+  static const int minorVersion = 2;
   static const String fingerprint = 'RIVE';
   final int ownerId;
   final int fileId;
diff --git a/lib/src/rive_core/shapes/paint/stroke.dart b/lib/src/rive_core/shapes/paint/stroke.dart
index c6923eb..5929462 100644
--- a/lib/src/rive_core/shapes/paint/stroke.dart
+++ b/lib/src/rive_core/shapes/paint/stroke.dart
@@ -1,11 +1,25 @@
 import 'package:flutter/material.dart';
 import 'package:rive/src/rive_core/component_dirt.dart';
+import 'package:rive/src/rive_core/shapes/paint/stroke_effect.dart';
 import 'package:rive/src/rive_core/shapes/shape.dart';
 import 'package:rive/src/rive_core/shapes/shape_paint_container.dart';
 import 'package:rive/src/generated/shapes/paint/stroke_base.dart';
 export 'package:rive/src/generated/shapes/paint/stroke_base.dart';
 
 class Stroke extends StrokeBase {
+  StrokeEffect _effect;
+  StrokeEffect get effect => _effect;
+  // ignore: use_setters_to_change_properties
+  void addStrokeEffect(StrokeEffect effect) {
+    _effect = effect;
+  }
+
+  void removeStrokeEffect(StrokeEffect effect) {
+    if (effect == _effect) {
+      _effect = null;
+    }
+  }
+
   @override
   Paint makePaint() => Paint()
     ..style = PaintingStyle.stroke
@@ -52,11 +66,15 @@ class Stroke extends StrokeBase {
     }
   }
 
+  void invalidateEffects() {
+    _effect?.invalidateEffect();
+  }
+
   @override
   void draw(Canvas canvas, Path path) {
     if (!isVisible) {
       return;
     }
-    canvas.drawPath(path, paint);
+    canvas.drawPath(_effect?.effectPath(path) ?? path, paint);
   }
 }
diff --git a/lib/src/rive_core/shapes/paint/stroke_effect.dart b/lib/src/rive_core/shapes/paint/stroke_effect.dart
new file mode 100644
index 0000000..eeb9823
--- /dev/null
+++ b/lib/src/rive_core/shapes/paint/stroke_effect.dart
@@ -0,0 +1,6 @@
+import 'dart:ui';
+
+abstract class StrokeEffect {
+  Path effectPath(Path source);
+  void invalidateEffect();
+}
diff --git a/lib/src/rive_core/shapes/paint/trim_path.dart b/lib/src/rive_core/shapes/paint/trim_path.dart
new file mode 100644
index 0000000..87116ae
--- /dev/null
+++ b/lib/src/rive_core/shapes/paint/trim_path.dart
@@ -0,0 +1,82 @@
+import 'dart:ui';
+import 'package:rive/src/rive_core/component_dirt.dart';
+import 'package:rive/src/rive_core/shapes/paint/stroke.dart';
+import 'package:rive/src/rive_core/shapes/paint/stroke_effect.dart';
+import 'package:rive/src/rive_core/shapes/paint/trim_path_drawing.dart';
+import 'package:rive/src/generated/shapes/paint/trim_path_base.dart';
+export 'package:rive/src/generated/shapes/paint/trim_path_base.dart';
+
+enum TrimPathMode { none, sequential, synchronized }
+
+class TrimPath extends TrimPathBase implements StrokeEffect {
+  final Path _trimmedPath = Path();
+  Path _renderPath;
+  @override
+  Path effectPath(Path source) {
+    if (_renderPath != null) {
+      return _renderPath;
+    }
+    _trimmedPath.reset();
+    var isSequential = mode == TrimPathMode.sequential;
+    double renderStart = start.clamp(0, 1).toDouble();
+    double renderEnd = end.clamp(0, 1).toDouble();
+    bool inverted = renderStart > renderEnd;
+    if ((renderStart - renderEnd).abs() != 1.0) {
+      renderStart = (renderStart + offset) % 1.0;
+      renderEnd = (renderEnd + offset) % 1.0;
+      if (renderStart < 0) {
+        renderStart += 1.0;
+      }
+      if (renderEnd < 0) {
+        renderEnd += 1.0;
+      }
+      if (inverted) {
+        final double swap = renderEnd;
+        renderEnd = renderStart;
+        renderStart = swap;
+      }
+      if (renderEnd >= renderStart) {
+        updateTrimPath(
+            source, _trimmedPath, renderStart, renderEnd, false, isSequential);
+      } else {
+        updateTrimPath(
+            source, _trimmedPath, renderEnd, renderStart, true, isSequential);
+      }
+    } else {
+      return _renderPath = source;
+    }
+    return _renderPath = _trimmedPath;
+  }
+
+  Stroke get stroke => parent as Stroke;
+  TrimPathMode get mode => TrimPathMode.values[modeValue];
+  set mode(TrimPathMode value) => modeValue = value.index;
+  @override
+  void invalidateEffect() {
+    _renderPath = null;
+    stroke?.shapePaintContainer?.addDirt(ComponentDirt.paint);
+  }
+
+  @override
+  void endChanged(double from, double to) => invalidateEffect();
+  @override
+  void modeValueChanged(int from, int to) => invalidateEffect();
+  @override
+  void offsetChanged(double from, double to) => invalidateEffect();
+  @override
+  void startChanged(double from, double to) => invalidateEffect();
+  @override
+  void update(int dirt) {}
+  @override
+  void onAdded() {
+    super.onAdded();
+    stroke?.addStrokeEffect(this);
+    _renderPath = null;
+  }
+
+  @override
+  void onRemoved() {
+    stroke?.removeStrokeEffect(this);
+    super.onRemoved();
+  }
+}
diff --git a/lib/src/rive_core/shapes/paint/trim_path_drawing.dart b/lib/src/rive_core/shapes/paint/trim_path_drawing.dart
new file mode 100644
index 0000000..680e281
--- /dev/null
+++ b/lib/src/rive_core/shapes/paint/trim_path_drawing.dart
@@ -0,0 +1,94 @@
+import 'dart:ui';
+
+double _appendPathSegmentSequential(Iterator<PathMetric> metricsIterator,
+    Path to, double offset, double start, double stop) {
+  double nextOffset = offset;
+  do {
+    PathMetric metric = metricsIterator.current;
+    nextOffset = offset + metric.length;
+    if (start < nextOffset) {
+      Path extracted = metric.extractPath(start - offset, stop - offset);
+      if (extracted != null) {
+        to.addPath(extracted, Offset.zero);
+      }
+      if (stop < nextOffset) {
+        break;
+      }
+    }
+    // ignore: parameter_assignments
+    offset = nextOffset;
+  } while (metricsIterator.moveNext());
+  return offset;
+}
+
+void _appendPathSegmentSync(
+    PathMetric metric, Path to, double offset, double start, double stop) {
+  double nextOffset = offset + metric.length;
+  if (start < nextOffset) {
+    Path extracted = metric.extractPath(start - offset, stop - offset);
+    if (extracted != null) {
+      to.addPath(extracted, Offset.zero);
+    }
+  }
+}
+
+void _trimPathSequential(
+    Path path, Path result, double startT, double stopT, bool complement) {
+  PathMetrics metrics = path.computeMetrics();
+  double totalLength = 0.0;
+  for (final PathMetric metric in metrics) {
+    totalLength += metric.length;
+  }
+  metrics = path.computeMetrics();
+  double trimStart = totalLength * startT;
+  double trimStop = totalLength * stopT;
+  double offset = 0.0;
+  Iterator<PathMetric> metricsIterator = metrics.iterator;
+  metricsIterator.moveNext();
+  if (complement) {
+    if (trimStart > 0.0) {
+      offset = _appendPathSegmentSequential(
+          metricsIterator, result, offset, 0.0, trimStart);
+    }
+    if (trimStop < totalLength) {
+      offset = _appendPathSegmentSequential(
+          metricsIterator, result, offset, trimStop, totalLength);
+    }
+  } else {
+    if (trimStart < trimStop) {
+      offset = _appendPathSegmentSequential(
+          metricsIterator, result, offset, trimStart, trimStop);
+    }
+  }
+}
+
+void _trimPathSync(
+    Path path, Path result, double startT, double stopT, bool complement) {
+  final PathMetrics metrics = path.computeMetrics();
+  for (final PathMetric metric in metrics) {
+    double length = metric.length;
+    double trimStart = length * startT;
+    double trimStop = length * stopT;
+    if (complement) {
+      if (trimStart > 0.0) {
+        _appendPathSegmentSync(metric, result, 0.0, 0.0, trimStart);
+      }
+      if (trimStop < length) {
+        _appendPathSegmentSync(metric, result, 0.0, trimStop, length);
+      }
+    } else {
+      if (trimStart < trimStop) {
+        _appendPathSegmentSync(metric, result, 0.0, trimStart, trimStop);
+      }
+    }
+  }
+}
+
+void updateTrimPath(Path path, Path result, double startT, double stopT,
+    bool complement, bool isSequential) {
+  if (isSequential) {
+    _trimPathSequential(path, result, startT, stopT, complement);
+  } else {
+    _trimPathSync(path, result, startT, stopT, complement);
+  }
+}
diff --git a/lib/src/rive_core/shapes/shape.dart b/lib/src/rive_core/shapes/shape.dart
index bab18d7..d562795 100644
--- a/lib/src/rive_core/shapes/shape.dart
+++ b/lib/src/rive_core/shapes/shape.dart
@@ -33,16 +33,18 @@ class Shape extends ShapeBase with ShapePaintContainer {
     return paths.add(path);
   }
 
-  void pathChanged(Path path) {
+  void _markComposerDirty() {
     _pathComposer?.addDirt(ComponentDirt.path);
+    invalidateStrokeEffects();
   }
 
+  void pathChanged(Path path) => _markComposerDirty();
   void paintChanged() {
     addDirt(ComponentDirt.path);
     for (final d in dependents) {
       d.addDirt(ComponentDirt.worldTransform);
     }
-    _pathComposer?.addDirt(ComponentDirt.path);
+    _markComposerDirty();
   }
 
   @override
@@ -60,7 +62,7 @@ class Shape extends ShapeBase with ShapePaintContainer {
   @override
   void update(int dirt) {
     super.update(dirt);
-    if (dirt & ComponentDirt.paint != 0) {
+    if (dirt & ComponentDirt.blendMode != 0) {
       for (final fill in fills) {
         fill.blendMode = blendMode;
       }
@@ -152,7 +154,7 @@ class Shape extends ShapeBase with ShapePaintContainer {
     }
   }
 
-  void _markBlendModeDirty() => addDirt(ComponentDirt.paint);
+  void _markBlendModeDirty() => addDirt(ComponentDirt.blendMode);
   @override
   void onPaintMutatorChanged(ShapePaintMutator mutator) {
     paintChanged();
diff --git a/lib/src/rive_core/shapes/shape_paint_container.dart b/lib/src/rive_core/shapes/shape_paint_container.dart
index 4ab1028..d24944e 100644
--- a/lib/src/rive_core/shapes/shape_paint_container.dart
+++ b/lib/src/rive_core/shapes/shape_paint_container.dart
@@ -14,6 +14,12 @@ abstract class ShapePaintContainer {
   void onFillsChanged();
   @protected
   void onStrokesChanged();
+  void invalidateStrokeEffects() {
+    for (final stroke in strokes) {
+      stroke.invalidateEffects();
+    }
+  }
+
   bool addFill(Fill fill) {
     if (fills.add(fill)) {
       onFillsChanged();
diff --git a/pubspec.yaml b/pubspec.yaml
index ed4770e..9a8eaab 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
 name: rive
 description: Rive 2 Flutter Runtime
-version: 0.5.1
+version: 0.5.2
 repository: https://github.com/rive-app/rive-flutter
 homepage: https://rive.app