diff --git a/packages/wyatt_ui_kit/lib/src/components/components.dart b/packages/wyatt_ui_kit/lib/src/components/components.dart
index b696808e..191af8e8 100644
--- a/packages/wyatt_ui_kit/lib/src/components/components.dart
+++ b/packages/wyatt_ui_kit/lib/src/components/components.dart
@@ -16,3 +16,4 @@
export './buttons/buttons.dart';
export './cards/cards.dart';
+export './loader/loader.dart';
diff --git a/packages/wyatt_ui_kit/lib/src/components/loader/loader.dart b/packages/wyatt_ui_kit/lib/src/components/loader/loader.dart
new file mode 100644
index 00000000..620b5d70
--- /dev/null
+++ b/packages/wyatt_ui_kit/lib/src/components/loader/loader.dart
@@ -0,0 +1,145 @@
+// Copyright (C) 2023 WYATT GROUP
+// Please see the AUTHORS file for details.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+import 'dart:math';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_animate/flutter_animate.dart';
+import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart';
+import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
+import 'package:wyatt_ui_kit/src/components/loader/loader_theme_resolver.dart';
+import 'package:wyatt_ui_kit/wyatt_ui_kit.dart';
+
+part 'loader.g.dart';
+
+@ComponentCopyWithExtension()
+class Loader extends LoaderComponent with $LoaderCWMixin {
+ const Loader({
+ super.colors,
+ super.radius,
+ super.stroke,
+ super.duration,
+ super.flip,
+ super.themeResolver,
+ super.key,
+ });
+
+ @override
+ LoaderThemeResolver? get themeResolver =>
+ super.themeResolver as LoaderThemeResolver?;
+
+ /// Negotiate the theme to get a complete style.
+ LoaderStyle _resolve(BuildContext context) {
+ final LoaderThemeResolver resolver = themeResolver ??
+ LoaderThemeResolver(
+ computeExtensionValueFn: (
+ context,
+ defaultValue,
+ themeExtension, {
+ extra,
+ }) =>
+ LoaderStyle(
+ colors: themeExtension.colors,
+ stroke: themeExtension.stroke,
+ ),
+ customStyleFn: (context, {extra}) => LoaderStyle(
+ colors: colors,
+ stroke: stroke,
+ ),
+ );
+
+ return resolver.negotiate(context);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final style = _resolve(context);
+ final dimension =
+ (radius != null) ? radius! * 2 : context.buttonTheme.height;
+
+ return SizedBox.square(
+ dimension: dimension,
+ child: RepaintBoundary(
+ child: CustomPaint(
+ painter: _LoaderPainter(
+ style.colors!,
+ dimension / 2,
+ style.stroke!,
+ flip: flip ?? false,
+ ),
+ )
+ .animate(
+ onPlay: (controller) => controller.repeat(),
+ )
+ .rotate(
+ duration: duration ?? 900.ms,
+ begin: (flip ?? false) ? 0 : 1,
+ end: (flip ?? false) ? 1 : 0,
+ ),
+ ),
+ );
+ }
+}
+
+class _LoaderPainter extends CustomPainter {
+ _LoaderPainter(
+ this.colors,
+ this.radius,
+ this.stroke, {
+ required this.flip,
+ });
+
+ final MultiColor colors;
+ final double radius;
+ final double stroke;
+ final bool flip;
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ final center = Offset(size.width / 2, size.height / 2);
+ final circleSurface = Rect.fromCircle(center: center, radius: radius);
+
+ final dotColor = colors.color;
+ final dotCenter =
+ Offset(size.width / 2 + (flip ? -radius : radius), size.height / 2);
+ final gradient =
+ colors.isGradient ? colors.colors : [colors.color, colors.color];
+
+ final gradientCirclePainter = Paint()
+ ..shader = SweepGradient(
+ colors: (flip ? gradient.reversed : gradient).toList(),
+ transform: flip ? const GradientRotation(pi) : null,
+ ).createShader(circleSurface)
+ ..strokeWidth = stroke
+ ..style = PaintingStyle.stroke
+ ..strokeCap = StrokeCap.round;
+
+ final dotPainter = Paint()
+ ..color = dotColor
+ ..style = PaintingStyle.fill
+ ..strokeCap = StrokeCap.round;
+
+ canvas
+ ..drawCircle(center, radius, gradientCirclePainter)
+ ..drawCircle(dotCenter, stroke / 2, dotPainter);
+ }
+
+ @override
+ bool shouldRepaint(_LoaderPainter oldDelegate) => false;
+
+ @override
+ bool shouldRebuildSemantics(_LoaderPainter oldDelegate) => false;
+}
diff --git a/packages/wyatt_ui_kit/lib/src/components/loader/loader.g.dart b/packages/wyatt_ui_kit/lib/src/components/loader/loader.g.dart
new file mode 100644
index 00000000..4f1243de
--- /dev/null
+++ b/packages/wyatt_ui_kit/lib/src/components/loader/loader.g.dart
@@ -0,0 +1,51 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'loader.dart';
+
+// **************************************************************************
+// ComponentCopyWithGenerator
+// **************************************************************************
+
+class $LoaderCWProxyImpl implements $LoaderComponentCWProxy {
+ const $LoaderCWProxyImpl(this._value);
+ final Loader _value;
+ @override
+ Loader colors(MultiColor? colors) => this(colors: colors);
+ @override
+ Loader radius(double? radius) => this(radius: radius);
+ @override
+ Loader stroke(double? stroke) => this(stroke: stroke);
+ @override
+ Loader duration(Duration? duration) => this(duration: duration);
+ @override
+ Loader flip(bool? flip) => this(flip: flip);
+ @override
+ Loader themeResolver(
+ ThemeResolver? themeResolver) =>
+ this(themeResolver: themeResolver);
+ @override
+ Loader key(Key? key) => this(key: key);
+ @override
+ Loader call({
+ MultiColor? colors,
+ double? radius,
+ double? stroke,
+ Duration? duration,
+ bool? flip,
+ ThemeResolver? themeResolver,
+ Key? key,
+ }) =>
+ Loader(
+ colors: colors ?? _value.colors,
+ radius: radius ?? _value.radius,
+ stroke: stroke ?? _value.stroke,
+ duration: duration ?? _value.duration,
+ flip: flip ?? _value.flip,
+ themeResolver: themeResolver ?? _value.themeResolver,
+ key: key ?? _value.key,
+ );
+}
+
+mixin $LoaderCWMixin on Component {
+ $LoaderComponentCWProxy get copyWith => $LoaderCWProxyImpl(this as Loader);
+}
diff --git a/packages/wyatt_ui_kit/lib/src/components/loader/loader_theme_resolver.dart b/packages/wyatt_ui_kit/lib/src/components/loader/loader_theme_resolver.dart
new file mode 100644
index 00000000..60047759
--- /dev/null
+++ b/packages/wyatt_ui_kit/lib/src/components/loader/loader_theme_resolver.dart
@@ -0,0 +1,53 @@
+// Copyright (C) 2023 WYATT GROUP
+// Please see the AUTHORS file for details.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+import 'package:flutter/material.dart';
+import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
+import 'package:wyatt_ui_kit/wyatt_ui_kit.dart';
+
+class LoaderThemeResolver
+ extends ThemeResolver {
+ const LoaderThemeResolver({
+ required this.computeExtensionValueFn,
+ required this.customStyleFn,
+ });
+
+ /// Values taken from
+ @override
+ LoaderStyle computeDefaultValue(
+ BuildContext context, {
+ void extra,
+ }) =>
+ LoaderStyle(
+ colors: MultiColor([
+ Theme.of(context).progressIndicatorTheme.color ??
+ context.colorScheme.primary,
+ context.colorScheme.onPrimary,
+ ]),
+ stroke: 4,
+ );
+
+ @override
+ final LoaderStyle? Function(
+ BuildContext context,
+ LoaderStyle defaultValue,
+ LoaderThemeExtension themeExtension, {
+ void extra,
+ }) computeExtensionValueFn;
+
+ @override
+ final LoaderStyle? Function(BuildContext context, {void extra}) customStyleFn;
+}
diff --git a/packages/wyatt_ui_kit/lib/src/domain/domain.dart b/packages/wyatt_ui_kit/lib/src/domain/domain.dart
index 901c7de9..8ee38b4c 100644
--- a/packages/wyatt_ui_kit/lib/src/domain/domain.dart
+++ b/packages/wyatt_ui_kit/lib/src/domain/domain.dart
@@ -14,5 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-export 'button_theme_extension/button_theme_extension.dart';
+export './button_theme_extension/button_theme_extension.dart';
+export './loader_theme_extension.dart';
export 'card_theme_extension.dart';
diff --git a/packages/wyatt_ui_kit/lib/src/domain/loader_theme_extension.dart b/packages/wyatt_ui_kit/lib/src/domain/loader_theme_extension.dart
new file mode 100644
index 00000000..dc23f0f7
--- /dev/null
+++ b/packages/wyatt_ui_kit/lib/src/domain/loader_theme_extension.dart
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 WYATT GROUP
+// Please see the AUTHORS file for details.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+import 'package:flutter/material.dart';
+import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
+
+abstract class LoaderThemeExtension
+ extends ThemeExtension {
+ const LoaderThemeExtension({
+ this.colors,
+ this.stroke,
+ });
+
+ /// Gradient colors from start to end.
+ final MultiColor? colors;
+
+ /// Loader stroke width
+ final double? stroke;
+}