Created with <3 with dartpad.dev.
Last active
August 29, 2022 06:47
-
-
Save jogboms/1ee7ec3f6d77eb09c75b2abde33ec72d to your computer and use it in GitHub Desktop.
Simple price widget
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:flutter/rendering.dart'; | |
void main() { | |
runApp(const App()); | |
} | |
class App extends StatelessWidget { | |
const App({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
themeMode: ThemeMode.light, | |
home: Scaffold( | |
body: Center( | |
child: Row( | |
mainAxisSize: MainAxisSize.min, | |
crossAxisAlignment: CrossAxisAlignment.center, | |
children: <Widget>[ | |
const PriceWidget(20.5), | |
const SizedBox(width: 20), | |
PriceWidget( | |
20.5, | |
decoration: (canvas, size) { | |
canvas.drawLine( | |
Offset.zero, | |
Offset(size.width, size.height), | |
Paint()..strokeWidth = 2, | |
); | |
}, | |
), | |
const SizedBox(width: 20), | |
PriceWidget( | |
20.5, | |
decoration: (canvas, size) { | |
canvas.drawLine( | |
size.centerLeft(Offset.zero), | |
size.centerRight(Offset.zero), | |
Paint()..strokeWidth = 2, | |
); | |
}, | |
) | |
], | |
), | |
), | |
), | |
); | |
} | |
} | |
typedef PriceWidgetDecoration = void Function(Canvas, Size); | |
class PriceWidget extends StatelessWidget { | |
const PriceWidget( | |
this.price, { | |
this.fontSizeLarge = 32.0, | |
this.fontSizeSmall = 16.0, | |
this.decoration, | |
super.key, | |
}); | |
final double price; | |
final double fontSizeLarge; | |
final double fontSizeSmall; | |
final PriceWidgetDecoration? decoration; | |
@override | |
Widget build(BuildContext context) { | |
final textTheme = Theme.of(context).textTheme; | |
final wholeTextStyle = textTheme.displayMedium?.copyWith( | |
fontSize: fontSizeLarge, | |
color: Colors.black, | |
height: 1, | |
); | |
return PriceRenderObjectWidget( | |
whole: Text( | |
price.floor().toString(), | |
style: wholeTextStyle, | |
), | |
fraction: Text( | |
price.toStringAsFixed(2).split('.').last, | |
style: textTheme.bodyLarge?.copyWith(fontSize: fontSizeSmall), | |
), | |
dot: Text('.', style: wholeTextStyle), | |
decoration: decoration, | |
); | |
} | |
} | |
enum PriceWidgetSlots { whole, fraction, dot } | |
class PriceRenderObjectWidget extends RenderObjectWidget | |
with SlottedMultiChildRenderObjectWidgetMixin<PriceWidgetSlots> { | |
const PriceRenderObjectWidget({ | |
super.key, | |
required this.whole, | |
required this.fraction, | |
required this.dot, | |
required this.decoration, | |
}); | |
final Widget whole; | |
final Widget fraction; | |
final Widget dot; | |
final PriceWidgetDecoration? decoration; | |
@override | |
Widget? childForSlot(PriceWidgetSlots slot) { | |
switch (slot) { | |
case PriceWidgetSlots.whole: | |
return whole; | |
case PriceWidgetSlots.fraction: | |
return fraction; | |
case PriceWidgetSlots.dot: | |
return dot; | |
} | |
} | |
@override | |
Iterable<PriceWidgetSlots> get slots => PriceWidgetSlots.values; | |
@override | |
SlottedContainerRenderObjectMixin<PriceWidgetSlots> createRenderObject(BuildContext context) => | |
RenderPriceWidget(decoration: decoration); | |
@override | |
void updateRenderObject(BuildContext context, covariant RenderPriceWidget renderObject) { | |
renderObject.decoration = decoration; | |
} | |
} | |
class RenderPriceWidget extends RenderBox with SlottedContainerRenderObjectMixin<PriceWidgetSlots> { | |
RenderPriceWidget({PriceWidgetDecoration? decoration}) : _decoration = decoration; | |
static const double padding = 2.0; | |
PriceWidgetDecoration? _decoration; | |
PriceWidgetDecoration? get decoration => _decoration; | |
set decoration(PriceWidgetDecoration? decoration) { | |
if (_decoration != decoration) { | |
_decoration = decoration; | |
markNeedsPaint(); | |
} | |
} | |
RenderBox get whole => childForSlot(PriceWidgetSlots.whole)!; | |
RenderBox get fraction => childForSlot(PriceWidgetSlots.fraction)!; | |
RenderBox get dot => childForSlot(PriceWidgetSlots.dot)!; | |
@override | |
void performLayout() { | |
whole.layout(constraints, parentUsesSize: true); | |
_positionChild(whole, Offset.zero); | |
final fractionOffset = Offset(whole.size.width + padding, 0); | |
fraction.layout(constraints, parentUsesSize: true); | |
_positionChild(fraction, fractionOffset); | |
dot.layout(constraints, parentUsesSize: true); | |
_positionChild(dot, fractionOffset); | |
size = Size(whole.size.width + fraction.size.width, whole.size.height); | |
} | |
@override | |
void paint(PaintingContext context, Offset offset) { | |
visitChildren((child) => _paintChild(context, offset, child)); | |
context.pushTransform( | |
needsCompositing, | |
offset, | |
Matrix4.translationValues(offset.dx, offset.dy, 0.0), | |
(context, _) => decoration?.call(context.canvas, size), | |
); | |
} | |
void _positionChild(RenderObject child, Offset offset) { | |
final BoxParentData parentData = child.parentData! as BoxParentData; | |
parentData.offset = offset; | |
} | |
void _paintChild(PaintingContext context, Offset offset, RenderObject child) { | |
final BoxParentData parentData = child.parentData! as BoxParentData; | |
context.paintChild(child, parentData.offset + offset); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment