Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create StyledImage #185

Merged
merged 8 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/mix.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export 'src/specs/icon/icon_widget.dart';
export 'src/specs/image/image_attribute.dart';
export 'src/specs/image/image_spec.dart';
export 'src/specs/image/image_util.dart';
export 'src/specs/image/image_widget.dart';
export 'src/specs/stack/stack_attribute.dart';
export 'src/specs/stack/stack_spec.dart';
export 'src/specs/stack/stack_util.dart';
Expand Down
41 changes: 39 additions & 2 deletions lib/src/attributes/scalars/scalar_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -443,10 +443,12 @@ class FontFamilyUtility<T extends StyleAttribute>
/// ```
/// See [ImageRepeat] for more information.
class ImageRepeatUtility<T extends StyleAttribute>
extends ScalarUtility<T, ImageRepeat> {
extends MixUtility<T, ImageRepeat> {
const ImageRepeatUtility(super.builder);

T call() => builder(ImageRepeat.repeat);

T noRepeat() => builder(ImageRepeat.noRepeat);
T repeat() => builder(ImageRepeat.repeat);
T repeatX() => builder(ImageRepeat.repeatX);
T repeatY() => builder(ImageRepeat.repeatY);
}
Expand Down Expand Up @@ -782,3 +784,38 @@ class TextAlignUtility<T extends StyleAttribute>
T start() => _builder(TextAlign.start);
T end() => _builder(TextAlign.end);
}

class RectUtility<T extends StyleAttribute> extends ScalarUtility<T, Rect> {
const RectUtility(super.builder);
T largest() => _builder(Rect.largest);
T zero() => _builder(Rect.zero);

T fromCenter({
required Offset center,
required double width,
required double height,
}) =>
_builder(Rect.fromCenter(center: center, width: width, height: height));

T fromLTRB(double left, double top, double right, double bottom) =>
_builder(Rect.fromLTRB(left, top, right, bottom));

T fromLTWH(double left, double top, double width, double height) =>
_builder(Rect.fromLTWH(left, top, width, height));

T fromCircle({required Offset center, required double radius}) =>
_builder(Rect.fromCircle(center: center, radius: radius));

T fromPoints({required Offset a, required Offset b}) =>
_builder(Rect.fromPoints(a, b));
}

class FilterQualityUtility<T extends StyleAttribute>
extends ScalarUtility<T, FilterQuality> {
const FilterQualityUtility(super.builder);

T none() => _builder(FilterQuality.none);
T low() => _builder(FilterQuality.low);
T medium() => _builder(FilterQuality.medium);
T high() => _builder(FilterQuality.high);
}
28 changes: 27 additions & 1 deletion lib/src/specs/image/image_attribute.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@ class ImageSpecAttribute extends SpecAttribute<ImageSpecAttribute, ImageSpec> {
final ColorDto? color;
final ImageRepeat? repeat;
final BoxFit? fit;
final AlignmentGeometry? alignment;
final Rect? centerSlice;
final FilterQuality? filterQuality;
final BlendMode? colorBlendMode;

const ImageSpecAttribute({
this.centerSlice,
this.width,
this.height,
this.color,
this.repeat,
this.fit,
this.alignment,
this.colorBlendMode,
this.filterQuality,
});

@override
Expand All @@ -28,6 +36,10 @@ class ImageSpecAttribute extends SpecAttribute<ImageSpecAttribute, ImageSpec> {
color: color?.resolve(mix),
repeat: repeat,
fit: fit,
alignment: alignment,
centerSlice: centerSlice,
filterQuality: filterQuality,
colorBlendMode: colorBlendMode,
);
}

Expand All @@ -41,9 +53,23 @@ class ImageSpecAttribute extends SpecAttribute<ImageSpecAttribute, ImageSpec> {
color: other.color ?? color,
repeat: other.repeat ?? repeat,
fit: other.fit ?? fit,
alignment: other.alignment ?? alignment,
centerSlice: other.centerSlice ?? centerSlice,
filterQuality: other.filterQuality ?? filterQuality,
colorBlendMode: other.colorBlendMode ?? colorBlendMode,
);
}

@override
get props => [width, height, color, repeat, fit];
get props => [
width,
height,
color,
repeat,
fit,
centerSlice,
alignment,
filterQuality,
colorBlendMode,
];
}
48 changes: 41 additions & 7 deletions lib/src/specs/image/image_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:flutter/widgets.dart';

import '../../core/attribute.dart';
import '../../factory/mix_provider_data.dart';
import '../../helpers/lerp_helpers.dart';
import 'image_attribute.dart';

@immutable
Expand All @@ -13,26 +14,37 @@ class ImageSpec extends Spec<ImageSpec> {
final Color? color;
final ImageRepeat? repeat;
final BoxFit? fit;
final AlignmentGeometry? alignment;
final Rect? centerSlice;
final FilterQuality? filterQuality;
final BlendMode? colorBlendMode;

const ImageSpec({
required this.width,
required this.height,
required this.color,
required this.repeat,
required this.fit,
required this.alignment,
required this.centerSlice,
required this.filterQuality,
required this.colorBlendMode,
});

const ImageSpec.empty()
: width = null,
height = null,
color = null,
repeat = null,
alignment = null,
centerSlice = null,
filterQuality = FilterQuality.none,
colorBlendMode = BlendMode.clear,
fit = null;

static ImageSpec resolve(MixData mix) {
final recipe = mix.attributeOf<ImageSpecAttribute>()?.resolve(mix);

return recipe ?? const ImageSpecAttribute().resolve(mix);
static ImageSpec of(MixData mix) {
return mix.attributeOf<ImageSpecAttribute>()?.resolve(mix) ??
const ImageSpec.empty();
}

@override
Expand All @@ -41,8 +53,12 @@ class ImageSpec extends Spec<ImageSpec> {
width: lerpDouble(width, other?.width, t),
height: lerpDouble(height, other?.height, t),
color: Color.lerp(color, other?.color, t),
repeat: t < 0.5 ? repeat : other?.repeat,
fit: t < 0.5 ? fit : other?.fit,
centerSlice: lerpSnap(centerSlice, other?.centerSlice, t),
repeat: lerpSnap(repeat, other?.repeat, t),
fit: lerpSnap(fit, other?.fit, t),
filterQuality: lerpSnap(filterQuality, other?.filterQuality, t),
colorBlendMode: lerpSnap(colorBlendMode, other?.colorBlendMode, t),
alignment: AlignmentGeometry.lerp(alignment, other?.alignment, t),
);
}

Expand All @@ -54,16 +70,34 @@ class ImageSpec extends Spec<ImageSpec> {
Color? color,
ImageRepeat? repeat,
BoxFit? fit,
AlignmentGeometry? alignment,
Rect? centerSlice,
FilterQuality? filterQuality,
BlendMode? colorBlendMode,
}) {
return ImageSpec(
width: width ?? this.width,
height: height ?? this.height,
color: color ?? this.color,
repeat: repeat ?? this.repeat,
fit: fit ?? this.fit,
centerSlice: centerSlice ?? this.centerSlice,
alignment: alignment ?? this.alignment,
filterQuality: filterQuality ?? this.filterQuality,
colorBlendMode: colorBlendMode ?? this.colorBlendMode,
);
}

@override
get props => [width, height, color, repeat, fit];
get props => [
width,
height,
color,
repeat,
fit,
centerSlice,
alignment,
filterQuality,
colorBlendMode,
];
}
25 changes: 25 additions & 0 deletions lib/src/specs/image/image_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,21 @@ class ImageUtility extends SpecUtility<ImageSpecAttribute> {
ColorDto? color,
ImageRepeat? repeat,
BoxFit? fit,
AlignmentGeometry? alignment,
Rect? centerSlice,
BlendMode? blendMode,
FilterQuality? filterQuality,
}) {
return ImageSpecAttribute(
width: width,
height: height,
color: color,
repeat: repeat,
fit: fit,
alignment: alignment,
centerSlice: centerSlice,
colorBlendMode: blendMode,
filterQuality: filterQuality,
);
}

Expand All @@ -45,4 +53,21 @@ class ImageUtility extends SpecUtility<ImageSpecAttribute> {
DoubleUtility<ImageSpecAttribute> get height {
return DoubleUtility((height) => _only(height: height));
}

AlignmentUtility<ImageSpecAttribute> get alignment {
return AlignmentUtility((alignment) => _only(alignment: alignment));
}

RectUtility<ImageSpecAttribute> get centerSlice {
return RectUtility((rect) => _only(centerSlice: rect));
}

FilterQualityUtility<ImageSpecAttribute> get filterQuality {
return FilterQualityUtility(
(filterQuality) => _only(filterQuality: filterQuality));
}

BlendModeUtility<ImageSpecAttribute> get blendMode {
return BlendModeUtility((blendMode) => _only(blendMode: blendMode));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also add a call() method. One to keep in mind, though, is the call() method takes the actual Flutter core classes, not Dtos, if they exist.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will call() become the same as _only() In this case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One convention that I have been falling is called (), which is similar to _only or only, but it does not replace. call() has the Flutter core classes, and only has things like DTOs and etc.

}
95 changes: 95 additions & 0 deletions lib/src/specs/image/image_widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import 'package:flutter/material.dart';

import '../../core/styled_widget.dart';
import '../../factory/mix_provider.dart';
import '../../factory/mix_provider_data.dart';
import '../../utils/helper_util.dart';
import 'image_spec.dart';

class StyledImage extends StyledWidget {
final ImageProvider<Object> image;
final ImageFrameBuilder? frameBuilder;
final ImageLoadingBuilder? loadingBuilder;
final ImageErrorWidgetBuilder? errorBuilder;
final String? semanticLabel;
final bool excludeFromSemantics;

const StyledImage({
super.key,
super.style,
super.inherit = true,
this.frameBuilder,
this.loadingBuilder,
this.errorBuilder,
this.semanticLabel,
this.excludeFromSemantics = false,
required this.image,
});

@override
Widget build(BuildContext context) {
return withMix(context, (mix) {
return MixedImage(
image: image,
errorBuilder: errorBuilder,
excludeFromSemantics: excludeFromSemantics,
frameBuilder: frameBuilder,
loadingBuilder: loadingBuilder,
semanticLabel: semanticLabel,
);
});
}
}

class MixedImage extends StatelessWidget {
const MixedImage({
super.key,
this.decoratorOrder = const [],
this.mix,
required this.image,
this.frameBuilder,
this.loadingBuilder,
this.errorBuilder,
this.semanticLabel,
this.excludeFromSemantics = false,
});

final MixData? mix;
final ImageProvider<Object> image;
final ImageFrameBuilder? frameBuilder;
final ImageLoadingBuilder? loadingBuilder;
final ImageErrorWidgetBuilder? errorBuilder;
final String? semanticLabel;
final bool excludeFromSemantics;
final List<Type> decoratorOrder;

@override
Widget build(BuildContext context) {
final mix = this.mix ?? MixProvider.of(context);
final spec = ImageSpec.of(mix);

final current = Image(
image: image,
frameBuilder: frameBuilder,
loadingBuilder: loadingBuilder,
errorBuilder: errorBuilder,
semanticLabel: semanticLabel,
excludeFromSemantics: excludeFromSemantics,
width: spec.width,
height: spec.height,
color: spec.color,
repeat: spec.repeat ?? ImageRepeat.noRepeat,
fit: spec.fit,
alignment: spec.alignment ?? Alignment.center,
centerSlice: spec.centerSlice,
filterQuality: spec.filterQuality ?? FilterQuality.none,
colorBlendMode: spec.colorBlendMode ?? BlendMode.clear,
);

return shouldApplyDecorators(
mix: mix,
orderOfDecorators: decoratorOrder,
child: current,
);
}
}
2 changes: 1 addition & 1 deletion pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -529,4 +529,4 @@ packages:
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.2.0 <4.0.0"
dart: ">=3.2.0-194.0.dev <4.0.0"
3 changes: 3 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ dev_dependencies:
mockito: ^5.4.2
meta: ^1.9.1

flutter:
assets:
- test_resources/
4 changes: 2 additions & 2 deletions test/src/attributes/scalars/scalar_util_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,11 @@ void main() {
const utility = ImageRepeatUtility(UtilityTestAttribute.new);
test('Properties are initialized correctly', () {
expect(utility.noRepeat().value, isA<ImageRepeat>());
expect(utility.repeat().value, isA<ImageRepeat>());
expect(utility().value, isA<ImageRepeat>());
expect(utility.repeatX().value, isA<ImageRepeat>());
expect(utility.repeatY().value, isA<ImageRepeat>());
expect(utility.noRepeat().value, ImageRepeat.noRepeat);
expect(utility.repeat().value, ImageRepeat.repeat);
expect(utility().value, ImageRepeat.repeat);
expect(utility.repeatX().value, ImageRepeat.repeatX);
expect(utility.repeatY().value, ImageRepeat.repeatY);
});
Expand Down
Loading
Loading