skip to Main Content

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


  1. Chosen as BEST ANSWER

    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.

    import 'package:cloud_firestore/cloud_firestore.dart';
    import 'package:flutter/material.dart';
    import 'package:semafor/colors.dart';
    
    int liveTime = 0, normalTime = 0;
    int onPressedTime = 0;
    int timeControlls = 0;
    
    final CollectionReference saljemVrijeme =
        FirebaseFirestore.instance.collection('game');
    
    class ClockControlls extends StatefulWidget {
      const ClockControlls({Key? key}) : super(key: key);
    
      @override
      State<ClockControlls> createState() => _ClockControllsState();
    }
    
    class _ClockControllsState extends State<ClockControlls> {
    
    
    @override
      build(BuildContext context) {
        return Container(
          height: 200,
          width: 580,
          decoration: BoxDecoration(
            color: kOpenScoreboardGreyDark,
            shape: BoxShape.rectangle,
            border: Border.all(
              color: kOpenScoreboardBlue,
              width: 3,
            ),
            borderRadius: BorderRadius.circular(20.0),
          ),
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                const Text(
                  "Scoreboard",
                  style: TextStyle(
                    fontSize: 30,
                    color: Colors.white,
                  ),
                ),
    
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    ElevatedButton(
                      style: ElevatedButton.styleFrom(
                            backgroundColor:  kOpenScoreboardGreyDarker,
                            foregroundColor: Colors.white
                          ),
                      onPressed: () {
                              setState(() {
                                if (timeControlls == 0) {
                                  onPressedTime =
                                      DateTime.now().millisecondsSinceEpoch;
                                  timeControlls = 1;
                                } else if (timeControlls == 2) {
                                  onPressedTime = DateTime.now().millisecondsSinceEpoch - 2700000;
                                  timeControlls = 1;
                                }
                                   saljemVrijeme.doc('Time').update({'index': 1, });
                                  saljemVrijeme.doc('Time').update({'onClick': onPressedTime, });
                              
                              });
                            },
                      child: const Text('Start'),
                    ),
                    ElevatedButton(
                      style: ElevatedButton.styleFrom(
                            backgroundColor:  kOpenScoreboardGreyDarker,
                            foregroundColor: Colors.white,
                          ),
                                              onPressed: () {
                              setState(() {
                                timeControlls = 0;
                                saljemVrijeme.doc('Time').update({'index': 0, });
                                     
    
                              });
                            },
    
                      child: const Text('Reset Game Clock'),
                    ),
                    ElevatedButton(
                      style: ElevatedButton.styleFrom(
                            backgroundColor:  kOpenScoreboardGreyDarker,
                            foregroundColor: Colors.white,
                          ),
                     onPressed: () {
                              setState(() {
                                timeControlls = 2;
                                saljemVrijeme.doc('Time').update({'index': 2, });
                              });
                            },
    
                      child: const Text('End of FIRST HALF'),
                    ),
                    ElevatedButton(
                      style: ElevatedButton.styleFrom(
                            backgroundColor:  kOpenScoreboardGreyDarker,
                            foregroundColor: Colors.white,
                          ),
                      onPressed: () {
                              setState(() {
                                timeControlls = 3;
                                saljemVrijeme.doc('Time').update({'index': 3, });
                              });
                            },
    
                      child: const Text('End of GAME'),
                    ),
                  ]
                ),
              ],
            ),
          )
        );
      }
    }
    

    File for showing time on other screen.

    import 'package:cloud_firestore/cloud_firestore.dart';
    import 'package:flutter/material.dart';
    import 'package:intl/intl.dart';
    
    final CollectionReference primamVrijeme =
        FirebaseFirestore.instance.collection('game');
    
    
    int liveTime = 0, normalTime = 0;
    int onPressedTime = 0;
    int timeControlls = 0;
    
    class ShowTime extends StatefulWidget {
      const ShowTime({Key? key}) : super(key: key);
    
      @override
      State<ShowTime> createState() => _ShowTimeState();
    }
    
    class _ShowTimeState extends State<ShowTime> {
    
    
      Future<int?> getTime() async {
        var a = await primamVrijeme.doc('Time').get();
    
        setState(() {
          onPressedTime = a['onClick'];
          timeControlls= a['index'];
        });
        return liveTime;
      }
    
    
    
    
      String _stringTime() {
        liveTime = DateTime.now().millisecondsSinceEpoch - onPressedTime;
        var minutesString =
            _getMinutes(liveTime).remainder(100).toString().padLeft(2, '0');
    
        var secondsString = _getSeconds(liveTime).toString().padLeft(2, '0');
    
        return "$minutesString:$secondsString";
      }
    
      @override
    
      Widget build(BuildContext context) {
          getTime();
        return Scaffold(
          body: StreamBuilder(
            stream: Stream.periodic(const Duration(seconds: 1)),
            builder: (context, snapshot) {
              return Row(
                children: [
                  Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Row(
                        children: [
                          IndexedStack(index: timeControlls, children: <Widget>[
                            Container(
                                width: 100,
                                height: 100,
                                color: Colors.black,
                                child: Center(
                                  child: Text(
                                    '00:00',
                                    style: TextStyle(
                                        fontSize: 25, color: Colors.white),
                                  ),
                                )),
                            Container(
                                width: 100,
                                height: 100,
                                color: Colors.black,
                                child: Center(
                                  child: Text(
                                    _stringTime(),
                                    style: TextStyle(
                                        fontSize: 25, color: Colors.white),
                                  ),
                                )),
                            Container(
                                width: 100,
                                height: 100,
                                color: Colors.black,
                                child: Center(
                                  child: Text(
                                    '45:00',
                                    style: TextStyle(
                                        fontSize: 25, color: Colors.white),
                                  ),
                                )),
                                 Container(
                              width: 100,
                              height: 100,
                              color: Colors.black,
                              child: Center(
                                child: Text(
                                  'KRAJ',
                                  style:
                                      TextStyle(fontSize: 25, color: Colors.white),
                                ),
                              )),
                          Text(
                            _stringTime(),
                            style: TextStyle(fontSize: 25, color: Colors.white),
                          )
                          ]),
                         
                        ],
                      ),
                    
                    ],
                  ),
                ],
              );
            },
          ),
        );
      }
    
      int _getMinutes(int milliseconds) {
        return (milliseconds ~/ (60 * 1000));
      }
    
      int _getSeconds(int milliseconds) {
        return (milliseconds - (_getMinutes(milliseconds) * 60 * 1000)) ~/ 1000;
      }
    }
    

  2. 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).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search