In my previous post I created a sliding number widget using Flutter CustomPainter. In order to use this widget in action, there are two more remaining tasks. The first is to group several of the NumberSlideWidgets together in a single widget and the second is to add the timer.
Firstly we’re going to create a new widget called NumberSlideGroupWidget and pass in several parameters. Three of the these parameters you will recognise as being the same ones we passed in to the NumberSlideWidget: magnitude, color and contents. The next two are new. Count determines how many NumberSlide boxes will be in the group, and caption is some text to display underneath.
class NumberSlideGroupWidget extends StatefulWidget {
final double magnitude; // The size of the boxes
final Color color; // The color of the boxes
final String contents; // The string content of the boxes
final int count; // The number of boxes to show
final String caption;
const NumberSlideGroupWidget(
{Key? key,
required int this.count,
required double this.magnitude,
required Color this.color,
required String this.contents,
required String this.caption})
: super(key: key);
@override
_NumberSlideGroupWidgetState createState() => _NumberSlideGroupWidgetState();
}
DartNext we need to create the widget state which will run the animation, however, before we can build the widget we need to break up the incoming content into individual characters, one per number slide. The code below takes the incoming contents, adds spaces to the front of the string if it is shorter than the number of boxes in the group – set above as count. – and then splits the string into individual characters. Note that it doesn’t handle any overflow (I’m not expecting people to stay in my app for long enough!)
class _NumberSlideGroupWidgetState extends State<NumberSlideGroupWidget> {
@override
Widget build(BuildContext context) {
List<String> chars = widget.contents.padLeft(widget.count, " ").split("");
}
}
DartHaving split the incoming number into a list, we can now build widget using a simple for loop to iterate the list and create one NumberSlideWidget per character. It also displays the caption below.
class _NumberSlideGroupWidgetState extends State<NumberSlideGroupWidget> {
@override
Widget build(BuildContext context) {
List<String> chars = widget.contents.padLeft(widget.count, " ").split("");
return Scaffold(
body: Center(
child: Column (
children: [
Row(mainAxisSize: MainAxisSize.min, children: [
for (var i = 0; i < chars.length; i++)
Container(
width: widget.magnitude,
height: widget.magnitude,
child: NumberSlideWidget(
magnitude: widget.magnitude,
color: widget.color,
contents: chars[i],
),
),
],
),
Container(
height: 30,
child: Text(widget.caption),
),
]
),
);
}
}
DartNow we need to set up a timer in the calling object (either our top level App State, or another widget) and declare a few private variables to control it
class MyAppState extends State<MyApp> {
Timer? _timer;
int _timeInSeconds = 0; // The numnber of seconds elapsed since the timer began
bool _timerRunning = false; // To stop us calling timer start multiple times
bool _timerPaused = false; // To allow us to pause the timer
Dart.Next we can create a method to start the timer. This uses Timer.periodic to trigger the timer every second, and uses setState to update _timeInSeconds and force our app to rebuild to reflect the new value of the timer..
void _updateTimer() {
if (!_timerRunning) {
const oneSecond = Duration(seconds: 1);
_timer = Timer.periodic(oneSecond, (Timer timer) {
setState(() {
if (!_timerPaused) _timeInSeconds++;
});
});
_timerRunning = true;
}
}
Dart_updateTimer can be called from either the App constructor, initState or from a button tap to start the timer going.
Now we need to add our NumberSlideGroupWidget to the App scaffold
@override
Widget build(BuildContext context) {
return Scaffold(
child: Container(
width: width,
height: itemHeight,
child: NumberSlideGroupWidget(
caption: "Time",
count: 7, // How many boxes in the NumberSlideGroup
magnitude: 60, // size of the squares in pixels
color: Color.fromRGBO(0, 160, 140, 1),
contents: _timeInSeconds,
),
),
);
}
DartThis of course renders the time as seconds only. If we want it to appear as hours, minutes and seconds we need to format it. There is probably a neater way of doing this, but I wrote a little function to return the time as a string in the format d:hh:mm:ss with leading zeros only when the next larger element is in use – so 5 seconds is just 5, 63 seconds is 1:03 and so on
String formatTime(int seconds) {
final days = (seconds ~/ 86400);
seconds -= days * 86400;
final hours = (seconds ~/ 3600);
seconds -= hours * 3600;
final minutes = (seconds ~/ 60);
seconds -= (minutes * 60);
final List<String> items = [];
if (days != 0) items.add(days.toString());
if (items.isNotEmpty || hours != 0){
if (items.isNotEmpty) items.add(hours.toString().padLeft(2,"0"));
else items.add(hours.toString());
}
if (items.isNotEmpty || minutes != 0) {
if (items.isNotEmpty) items.add(minutes.toString().padLeft(2,"0"));
else items.add(minutes.toString());
}
if (items.isNotEmpty) items.add(seconds.toString().padLeft(2,"0"));
else items.add(seconds.toString());
return items.join(':');
}
DartAnd modified my scaffold to say
contents: formatTime(_timeInSeconds),
DartFlutter for Beginners (Ad)
Leave a Reply