0

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

  • The easiest way to make the startTimer() to public and use call TimerWidget inside as a refrence and call startTimer(). Just curious are you new to OO language or Flutter? – CbL Jun 22 '23 at 10:56

3 Answers3

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();
      }),
    );
  }
}

car_tor
  • 109
  • 4
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() {
  ...
Ivo
  • 18,659
  • 2
  • 23
  • 35
0

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;
develover
  • 203
  • 2
  • 5