I am creating Scoreboard and I have a stopwatch that I created, but now I have a problem that the time which is shown where I started the stopwatch must be started on another screen/computer. So when I start the timer, it should also update the time for other users.
So my question is what is the best way to do that?
I have tried to do something but it is not working fluently.
time.dart – here only I see what is going on ( you could say private)
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
final CollectionReference time = FirebaseFirestore.instance.collection(‘game’);
class Time extends StatefulWidget {
@override
State<Time> createState() => TimeState();
}
class TimeState extends State<Time> {
Duration duration = Duration();
Timer? timer;
final myController = TextEditingController();
@override
void initState() {
super.initState();
reset();
}
void reset() {
setState(() => duration = Duration());
}
void addTime() {
const addSeconds = 1;
setState(() {
final seconds = duration.inSeconds + addSeconds;
duration = Duration(seconds: seconds);
});
}
void startTimer({bool resets = true}) {
if (resets) {
reset();
}
timer = Timer.periodic(Duration(seconds: 1), (_) => addTime());
}
void stopTimer({bool resets = true}) {
if (resets) {
reset();
}
setState(() {
timer?.cancel();
});
}
void firstHalfEnd() {
setState(() => duration = Duration(minutes: 45, seconds: 00));
stopTimer(resets: false);
}
void secondHalfEnd() {
setState(() => duration = Duration(minutes: 90, seconds: 00));
stopTimer(resets: false);
}
void addMinute (){
setState(() {
duration = duration + const Duration(minutes: 1);
});
}
void subtractMinute (){
setState(() {
duration = duration - const Duration(minutes: 1);
});
}
@override
Widget build(BuildContext context) => Scaffold(
backgroundColor:
Colors.green,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
buiildTime(),
SizedBox(
height: 20,
),
buildButtons()
],
)),
);
Widget buiildTime() {
String twoDigits(int n) => n.toString().padLeft(
2, '0'); // pretvara jednoznamenkaste brojeve u "dvoznamenkaste" 9 -> 09
final minutes = twoDigits(duration.inMinutes.remainder(100));
final seconds = twoDigits(duration.inSeconds.remainder(60));
setState(() {
time.doc('Time').collection('minutes').doc('minutes').set({'minutes': '$minutes'});
time.doc('Time').update({'seconds': '$seconds'});
});
return Text(
'$minutes:$seconds',
style: TextStyle(fontSize: 20),
);
}
Widget buildButtons() {
bool time_going = timer == null ? false : timer!.isActive;
bool time_stops = duration.inSeconds == 0;
return time_going || !time_stops
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
TextButton(
child:Text(time_going ? 'PAUSE' : 'RESUME'),
onPressed: () {
if (time_going) {
stopTimer(resets: false);
} else {
startTimer(resets: false);
}
}),
SizedBox(
width: 12,
),
TextButton( child: Text( "RESET"), onPressed: reset,),
],
),
SizedBox(
height: 10,
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TextButton(
onPressed: firstHalfEnd,
child: Text('1. Half END'),
style: ButtonStyle(backgroundColor: MaterialStateProperty.all<Color>(Colors.blue) , foregroundColor:MaterialStateProperty.all<Color> (Colors.white) ,),
),
SizedBox(width: 10,),
SizedBox(
child:TextButton(
onPressed: secondHalfEnd,
child: Text('2. Half END'),
style: ButtonStyle(backgroundColor: MaterialStateProperty.all<Color>(Colors.blue) , foregroundColor:MaterialStateProperty.all<Color> (Colors.white) ,),
)),
],
),
Row(
children: [
ElevatedButton(onPressed: addMinute, child: Text('+1 minute')),
SizedBox(width: 10,),
ElevatedButton(onPressed: subtractMinute, child: Text('-1 minute'))
],
)
],
)
: TextButton(
onPressed: startTimer,
child: Text('Start Timer!'),
style: ButtonStyle(backgroundColor: MaterialStateProperty.all<Color>(Colors.black) , foregroundColor:MaterialStateProperty.all<Color> (Colors.white) ,));
}
}
Code from scoreboard.dart – what other users would see( or public)
Container(
height: 100,
width: 100,
child: StreamBuilder(
stream: clubData.doc('Time').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text("Loading");
} else {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return Text(
":${data['seconds']}",
style:
TextStyle(color: Colors.white, fontSize: 20),
);
}
}))
2
Answers
So I found a way to do what I wanted to. @rashidom gave me a hint. So I will post my code so it can help others.
File with controls for time.
File for showing time on other screen.
I am not certain that the approach you are following is the best given that it relies extensively on reading and writing to Firestore, latency on network for some of clients making them fall behind on time or give different feeling of how much time a second will take on the app.
Here is a better alternative on how you would design this.
The Firestore document will contain a server timestamp and the duration of the counter (hours, minutes and seconds, etc…). The count down timer will be implemented in your custom
Time
widget starting from the server timestamp.To give you a feeling on how different this approach will be on the usage of Firestore alone, just assume that you have 20 people setting the same countdown for about 6 minutes. Your approach will require 7200 reading operations (20 clients) * (6 minutes) * (60 seconds). It will also require 360 write operations (6 minutes) * (60 seconds). On the other hand, the suggestion above will require 20 read operations and 2 write operations (first write is for storing the doc in firestore and second for Firestore to issue the timestamp).