skip to Main Content

I’m new in Flutter, and recently I have encountered with a challange. I have the following files:

  1. main.dart
import 'package:chatgpt_chat/testpage.dart';
import 'package:flutter/material.dart';

void main() => runApp(const TestAPP());

class TestAPP extends StatelessWidget {
  const TestAPP({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: TestScaffold(),
    );
  }
}

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

  @override
  State<TestScaffold> createState() => _TestScaffoldState();
}

class _TestScaffoldState extends State<TestScaffold> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Timer Example'),
      ),
      body: const Center(
        child: TimerWidget(),
      ),
      floatingActionButton: FloatingActionButton(onPressed: (){}),
    );
  }
}
  1. timerWidget.dart:
import 'dart:async';

import 'package:flutter/material.dart';

class TimerWidget extends StatefulWidget {
  const TimerWidget({Key? key}) : super(key: key);

  @override
  State<TimerWidget> createState() => _TimerWidgetState();
}

class _TimerWidgetState extends State<TimerWidget> {
  int _seconds = 0;
  late Timer _timer;

  @override
  void initState() {
    super.initState();
    _startTimer();
  }

  void _startTimer() {
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      setState(() {
        _seconds++;
      });
    });
  }

  void _stopTimer() {
    _timer.cancel();
  }

  void _resetTimer() {
    setState(() {
      _seconds = 0;
    });
  }

  @override
  void dispose() {
    _stopTimer();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    String formattedTime = _formatTime(_seconds);

    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          formattedTime,
          style: const TextStyle(fontSize: 24),
        ),
        const SizedBox(height: 16),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: _stopTimer,
              child: const Text('Stop'),
            ),
            const SizedBox(width: 16),
            ElevatedButton(
              onPressed: _resetTimer,
              child: const Text('Reset'),
            ),
          ],
        ),
      ],
    );
  }

  String _formatTime(int seconds) {
    int minutes = seconds ~/ 60;
    int remainingSeconds = seconds % 60;
    return '$minutes:${remainingSeconds.toString().padLeft(2, '0')}';
  }
}

Could you please help me out by showing how I can call startTimer method from TimerWidget in FloatingActionButton? I have a problem in different program, and solving this particular problem will help me out. I really appreciate it if you provide your solutions.

I looked for similar posts, however they do not fully solve my problem. I just have to use startTimer method in floatingActionButton

3

Answers


  1. Change your _TestScaffoldStateto

    class _TestScaffoldState extends State<TestScaffold> {
      GlobalKey<TimerWidgetState> key = GlobalKey<TimerWidgetState>();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Timer Example'),
          ),
          body: Center(
            child: TimerWidget(key: key),
          ),
          floatingActionButton: FloatingActionButton(onPressed: (){
            key.currentState?.startTimer();
          }),
        );
      }
    }
    

    Furthermore you need to make startTimer and TimerWidgetState public by removing the underscore so they can be used. So like:

    class TimerWidget extends StatefulWidget {
      const TimerWidget({Key? key}) : super(key: key);
    
      @override
      State<TimerWidget> createState() => TimerWidgetState();
    }
    
    class TimerWidgetState extends State<TimerWidget> {
      int _seconds = 0;
      late Timer _timer;
    
      @override
      void initState() {
        super.initState();
        startTimer();
      }
    
      void startTimer() {
      ...
    
    Login or Signup to reply.
  2. You could create a custom TimerWidgetController that TimerWidget requires as parameter, when built, in order to assign the reference of the function _startTime to it.

    By doing this, you don’t need to make anything public in the TimerWidget.

    The function can now be called from other widgets just by using an instance of TimerWidgetController, the same one passed to TimeWidget of course.

    Please check the code:

    • timer_widget_controller.dart
    
        class TimerWidgetController {
          late Function? startTimer;
        }
    
    
    • timer_widget.dart
    import 'dart:async';
    
    import 'package:flutter/material.dart';
    
    import 'timer_widget_controller.dart';
    
    class TimerWidget extends StatefulWidget {
      final TimerWidgetController controller;
      const TimerWidget({
        Key? key,
        required this.controller,
      }) : super(key: key);
    
      @override
      State<TimerWidget> createState() => _TimerWidgetState();
    }
    
    class _TimerWidgetState extends State<TimerWidget> {
      int _seconds = 0;
      late Timer _timer;
    
      @override
      void initState() {
        super.initState();
        widget.controller.startTimer = _startTimer;
        _startTimer();
      }
    
      void _startTimer() {
        _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
          setState(() {
            _seconds++;
          });
        });
      }
    
      void _stopTimer() {
        _timer.cancel();
      }
    
      void _resetTimer() {
        setState(() {
          _seconds = 0;
        });
      }
    
      @override
      void dispose() {
        _stopTimer();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        String formattedTime = _formatTime(_seconds);
    
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              formattedTime,
              style: const TextStyle(fontSize: 24),
            ),
            const SizedBox(height: 16),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: _stopTimer,
                  child: const Text('Stop'),
                ),
                const SizedBox(width: 16),
                ElevatedButton(
                  onPressed: _resetTimer,
                  child: const Text('Reset'),
                ),
              ],
            ),
          ],
        );
      }
    
      String _formatTime(int seconds) {
        int minutes = seconds ~/ 60;
        int remainingSeconds = seconds % 60;
        return '$minutes:${remainingSeconds.toString().padLeft(2, '0')}';
      }
    }
    
    
    • main.dart
    import 'package:flutter/material.dart';
    
    import 'timer_widget_controller.dart';
    import 'timer_widget.dart';
    
    void main() => runApp(const TestAPP());
    
    class TestAPP extends StatelessWidget {
      const TestAPP({super.key});
    
      @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          home: TestScaffold(),
        );
      }
    }
    
    class TestScaffold extends StatefulWidget {
      const TestScaffold({super.key});
    
      @override
      State<TestScaffold> createState() => _TestScaffoldState();
    }
    
    class _TestScaffoldState extends State<TestScaffold> {
      late final TimerWidgetController _timerWidgetController;
    
      @override
      void initState() {
        super.initState();
        _timerWidgetController = TimerWidgetController();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Timer Example'),
          ),
          body: Center(
            child: TimerWidget(
              controller: _timerWidgetController,
            ),
          ),
          floatingActionButton: FloatingActionButton(onPressed: () {
            _timerWidgetController.startTimer?.call();
          }),
        );
      }
    }
    
    
    Login or Signup to reply.
  3. I don’t know why you’re trying to run the timer(call startTimer method) in a difficult way, but I’ll do what you want for now.

    Since other people mentioned above how to use GlobalKey and how to create and solve a controller, I’ll make it a more primitive way.

    I’ll make a simple example code and show it to you.

    -main.dart

    import 'dart:async';
    import 'package:flutter/material.dart';
    
    class TestScaffold extends StatefulWidget {
      const TestScaffold({
        Key? key,
      }) : super(key: key);
    
      @override
      State<TestScaffold> createState() => _TestScaffoldState();
    }
    
    class _TestScaffoldState extends State<TestScaffold> {
      bool timer = false;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Timer Example'),
          ),
          body: Center(child: TimerWidget(timerBool: timer)),
          floatingActionButton: FloatingActionButton(
            onPressed: _callTimer,
            child: const Icon(Icons.play_arrow_rounded),
          ),
        );
      }
    
      void _callTimer() {
        setState(() {
          timer = true;
        });
      }
     }
    

    -timerWidget.dart

    class TimerWidget extends StatefulWidget {
      bool timerBool;
    
      TimerWidget({
        Key? key,
        required this.timerBool,
      }) : super(key: key);
    
      @override
      State<TimerWidget> createState() => _TimerWidgetState();
    }
    
    class _TimerWidgetState extends State<TimerWidget> {
      int _seconds = 0;
      Timer _timer = Timer(const Duration(seconds: 1), () {});
      String timerCheck = 'ready';
    
      @override
      void initState() {
        super.initState();
      }
    
      void _startTimer() {
        timerCheck = 'startinggg';
        _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
          setState(() {
            _seconds++;
          });
        });
        print('startTimer ${_timer.hashCode}');
      }
    
      @override
      Widget build(BuildContext context) {
        String formattedTime = _formatTime(_seconds);
        if (widget.timerBool && !_timer.isActive) {
          _startTimer();
        }
    
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              timerCheck,
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            const SizedBox(height: 20),
            Text(
              formattedTime,
              style: const TextStyle(fontSize: 24),
            ),
            const SizedBox(height: 16),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: _stopTimer,
                  child: const Text('Stop'),
                ),
                const SizedBox(width: 16),
                ElevatedButton(
                  onPressed: _resetTimer,
                  child: const Text('Reset'),
                ),
              ],
            ),
          ],
        );
      }
    
      void _stopTimer() {
        if (!widget.timerBool) return;
        setState(() {
          timerCheck = 'stopppp';
          widget.timerBool = false;
        });
        _timer.cancel();
        print('cancel ${_timer.hashCode}');
      }
    
      void _resetTimer() {
        setState(() {
          _seconds = 0;
        });
      }
    
      String _formatTime(int seconds) {
        int minutes = seconds ~/ 60;
        int remainingSeconds = seconds % 60;
        return '$minutes:${remainingSeconds.toString().padLeft(2, '0')}';
      }
    
      @override
      void dispose() {
        _stopTimer();
        super.dispose();
      }
    }
    

    For your information, there is a part that the two of you who answered above missed.
    Pressing FloatingActionButton continuously results in multiple timers,
    and only the last generated one can be canceled().

    Therefore, you need to put one condition in startTimer() as below.

    if( (your timer).isActive ) return;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search