Meggy
Meggy

Reputation: 1681

Convert Milliseconds to Duration to String?

I have a min and max parameter which each require a double.

  min: 0, 
  max: _duration!.inMilliseconds

These parameters are then converted to strings for use as dynamic text on top of the thumb ball of a slider.

But for the max value I'd like to use the actual duration instead;

IE: 1:34:30 instead of 94,500.

The final product required is a String.

So how do I get the Duration to a string while preserving the Duration formatting?

Keep in mind it is passed initially as a double.

Here's the full class for the thumb ball;

class CustomSliderThumbCircle extends SliderComponentShape {
  final double thumbRadius;
  final int min;
  final int max;

  const CustomSliderThumbCircle({
    required this.thumbRadius,
    this.min = 0,
    this.max = 10,
  });

  @override
  Size getPreferredSize(bool isEnabled, bool isDiscrete) {
    return Size.fromRadius(thumbRadius);
  }

  @override
  void paint(
    PaintingContext context,
    Offset center, {
    Animation<double>? activationAnimation,
    Animation<double>? enableAnimation,
    bool? isDiscrete,
    TextPainter? labelPainter,
    RenderBox? parentBox,
    SliderThemeData? sliderTheme,
    TextDirection? textDirection,
    double? value,
    double? textScaleFactor,
    Size? sizeWithOverflow,
  }) {
    final Canvas canvas = context.canvas;

    final paint = Paint()
      ..color = Colors.white //Thumb Background Color
      ..style = PaintingStyle.fill;

    TextSpan span = new TextSpan(
      style: new TextStyle(
        fontSize: thumbRadius,
        //fontWeight: FontWeight.w700,
        color: Colors.white, //Text Color of Value on Thumb
      ),
      text: getValue(value!),
    );

    TextPainter tp = new TextPainter(
        text: span,
        textAlign: TextAlign.center,
        textDirection: TextDirection.ltr);
    tp.layout();
    Offset textCenter =
        Offset(center.dx - (tp.width / 2), center.dy - (tp.height / 2));

    final rect = Rect.fromCircle(center: center, radius: thumbRadius);

    final rrect = RRect.fromRectAndRadius(
      Rect.fromPoints(
        Offset(rect.left - 10, rect.top),
        Offset(rect.right + 10, rect.bottom),
      ),
      Radius.elliptical(20, 100),
    );

    final fillPaint = Paint()
      ..color = sliderTheme!.activeTrackColor!
      ..style = PaintingStyle.fill;

    final borderPaint = Paint()
      ..color = Colors.black
      ..strokeWidth = 2.8
      ..style = PaintingStyle.stroke;

    canvas.drawRRect(rrect, fillPaint);
    canvas.drawRRect(rrect, borderPaint);

    tp.paint(canvas, textCenter);
  }

  String getValue(double value) {
    return (min + (max - min) * value).round().toString();
  }
}

Upvotes: 0

Views: 496

Answers (2)

jamesdlin
jamesdlin

Reputation: 90015

If CustomSliderThumbCircle is your own class, I would change it to take a callback function to format the value to a String (falling back to just .toString() if no callback is specified), something like:

class CustomSliderThumbCircle extends SliderComponentShape {
  final String Function(int value)? formatValue;
  ...

  const CustomSliderThumbCircle({
    required this.thumbRadius,
    this.min = 0,
    this.max = 10,
    this.formatValue,
  });


  String getValue(double value) {
    var clamped = (min + (max - min) * value).round();
    return (formatValue == null) ? clamped.toString() : formatValue!(clamped);
  }
}

and then your caller could pass a callback:

CustomSliderThumbCircle(
  thumbRadius: ...,
  ...,
  formatValue: (value) => Duration(milliseconds: value).toString(),
)

This also would allow you to use a function that formats Durations as, say, 1m34.5s instead of as 0:01:34.500000. I generally dislike representing durations with colons because it can be ambiguous if fractional seconds are omitted. For example, using a different function to format Durations:

CustomSliderThumbCircle(
  thumbRadius: ...,
  ...,
  formatValue: (value) => prettyDuration(Duration(milliseconds: value)),
)

If CustomSliderThumbCircle is not under your control, then you could extend it with your own class that overrides getValue.

Upvotes: 1

Meggy
Meggy

Reputation: 1681

I doubt this is better than jamesdlin's solution but I don't know how to format it the way I'd like using the call back. Plus I realised I only need seconds and milliseconds as the audio is limited to 60 seconds anyway. So here's what I did;

String getValue(double value) {
double seconds = value * (max/1000);
String secstring = secs.truncate().toString();
var milliseconds = value * (max.remainder(1000));
String millistring = milliseconds.truncate().toString();
String stringDuration = secstring + ':' + millistring;
return stringDuration ;

}

Upvotes: 0

Related Questions