skip to Main Content

I’m a beginner in flutter and I’m trying to make new project for fun: A chess clock.
Once one of the buttons is pressed it should start running down but it doesn’t. Please take a look and tell me what I’m doing wrong. Try to just point it out and not code it because I don’t want to just copy and paste but figuring things out. I guess it must be something related to the variable scope…
Here is the main.dart file:

import 'package:flutter/material.dart';
import 'Clock.dart';
void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
   const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            body: SafeArea(
                child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        Expanded(
          child: Chessclock(),
        ),
        Expanded(
          child: Chessclock(),
        )
      ]
    )))
    );
  }
}

and here is the Clock.dart file:

import 'dart:io';
import 'package:flutter/material.dart';

class Chessclock extends StatefulWidget {
   final tim;
   Chessclock({Key? key,required this.tim}): super({});
   var minutes  = this.tim~/60;
   var seconds = this.tim % 60;
   var inGame = false;
   var dur = const Duration(seconds: 1);
   void runtime(t,b){

     if(b){
       sleep(dur);
       _tim--;
     }else{
       return;
     }
   }
   bool inswitch(inGam){
     return !inGam;
   }

  @override
  State<Chessclock> createState() => new _ChessclockState();
}

class _ChessclockState extends State<Chessclock>{


  @override
  // TODO: implement runtimeType
  Widget build(BuildContext context) {
   runtime(tim,inGame);
    return TextButton(
      child: Text('$minutes:$seconds',
      style: TextStyle(fontSize: 30)
      ),
      onPressed: () {
        setState(() {
          inGame = inswitch(inGame);
        });
    },
      style: TextButton.styleFrom(
          foregroundColor: Colors.white,
          backgroundColor: (inGame? Colors.green : Colors.grey),

      )

    );
  }
}

I know you will be nice because Dart coders are great guys…. 😀

2

Answers


  1. All this stuff should be in _ChessclockState:

       var minutes  = widget.tim~/60;
       var seconds = widget.tim % 60;
       var inGame = false;
       var dur = const Duration(seconds: 1);
       void runtime(t,b){
    
         if(b){
           sleep(dur);
           _tim--;
         }else{
           return;
         }
       }
       bool inswitch(inGam){
         return !inGam;
       }
    

    Moreover, you may need use state lifecycle method that will triggered first time and once after widget is created:

      @override
      void initState() {
        super.initState();
        ..do initialization here..
      }
    

    You should look deeper at state lyfecycle
    https://flutterbyexample.com/lesson/stateful-widget-lifecycle#3-initstate

    Login or Signup to reply.
  2. First of all, it’s not reasonable to claim tim as a final since you need to update it’s value in your case.

    If I don’t misunderstand your meaning, then you actually want to set a timer (in your case is the ChessClock which can countdown the time when triggered)

    For the countdown logic,we can directly use Timer.periodic class in Dart instead of sleep() , also notice the dart doc of sleep()

    Use this with care, as no asynchronous operations can be processed in a isolate while it is blocked in a sleep call.

    Besides, in your code you want to call runtime() method in the state class, which in my opinion will not work and IDE should report error, if you really want to access property of the widget class from it’s state (in your example is accessing runtime() in Chessclock inside the state class _ChessclockState, you should use the keyword widget,for example widget.runtime() in your state class)

    Also, in this part of your code

    onPressed: () {
            setState(() {
              inGame = inswitch(inGame);
            });
        },
    

    You use setState to call inswitch() method, which should handle the countdown logic and decrese tim in ChessClock every sec, as I mention above, the countdown logic in your code may cause some problem because of using sleep()

    Let’s assume that you have modified the code to use Timer and the countdown logic works well, but the UI will still not change every sec as intend in this time. This is because you have only call setState() one time when the button was pressed in onPressed callback, however the tim actually is changing continuously.

    For this problem, there has one solution that you move the countdown logic into state class, then you can do something like:

    class _ClockExampleState extends State<ClockExample> {
      Timer? countingTimer = null;
      void triggerCounting() {
        if (countingTimer == null) {
          countingTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
            setState(() {
              if (widget.tim > 0) {
                --widget.tim;
              }
            });
          });
        } else {
          countingTimer!.cancel();
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            /// ...
            /// Button
            ElevatedButton(
                onPressed: () {
                  triggerCounting();
                },
                child: Text('Trigger'))
          ],
        );
      }
    }
    
    

    Since you can access setState() inside the state class, now you can create a method called triggerCounting which calls setState() every time the widget.tim has been updated, this time the UI will be supposed to update every sec

    Here is the full code (Notice my code is completely a new app which created just for example)

    main.dart

    import 'package:flutter/material.dart';
    import './clock.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      const MyHomePage({super.key});
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) {
        return ClockExample();
      }
    }
    

    clock.dart

    import 'dart:async';
    import 'dart:io';
    
    import 'package:flutter/material.dart';
    
    class ClockExample extends StatefulWidget {
      ClockExample({super.key, this.tim = 300});
    
      int tim;
    
      int get min {
        return tim ~/ 60;
      }
    
      int get sec {
        return tim % 60;
      }
    
      @override
      State<ClockExample> createState() => _ClockExampleState();
    }
    
    class _ClockExampleState extends State<ClockExample> {
      Timer? countingTimer = null;
      void triggerCounting() {
        if (countingTimer == null) {
          countingTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
            setState(() {
              print('tim: ${widget.tim}');
              if (widget.tim > 0) {
                --widget.tim;
              }
            });
          });
        } else {
          countingTimer!.cancel();
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Center(
              child: Text('${widget.min} : ${widget.sec}'),
            ),
            Center(
              child: Text('${widget.tim}'),
            ),
            ElevatedButton(
                onPressed: () {
                  triggerCounting();
                },
                child: Text('Trigger'))
          ],
        );
      }
    }
    

    I’m also learning Flutter now and maybe my solution is not the best one, and actually my solution still has some problems, like that the parent widget can not directly access the value of the timer, notice that not only ClockExample need to access and show the tim info, the other part of the app may also need to know the value of the countdown, for example, the app may need to stop the game and show dialog when times ran out, so you may need to condsider pulling up the state to the widget above all widgets which need to access the state in the widgets tree, or you could consider using some state management package like provider (Provider on pub.dev)

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