skip to Main Content

I want to stop the timer when I navigate to another page.
The following is my code


//imports
late Timer timer;

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    Widget page;
    switch (selectedIndex) {
      case 0:
        page = const MonitorPage();
      case 1:
        page = const ReportPage();
      case 2:
        page = const Placeholder();
      default:
        throw UnimplementedError("not found");
    }
    return LayoutBuilder(builder: (context, constraints) {
      return Scaffold(
        body: Row(
          children: [
            SafeArea(
              child: NavigationRail(
                extended: constraints.maxWidth >= 600,
                destinations: const [
                  NavigationRailDestination(
                    icon: Icon(Icons.home),
                    label: Text('main'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.list),
                    label: Text('report'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.settings),
                    label: Text("setting"),
                  ),
                ],
                selectedIndex: selectedIndex,
                onDestinationSelected: (value) {
                  setState(() {
                    if (selectedIndex == 0) {
                      timer.cancel(); // here I'd like to cancel the timer
                    }
                    selectedIndex = value;
                  });
                },
              ),
            ),
            Expanded(
              child: Container(
                color: Theme.of(context).colorScheme.primaryContainer,
                child: page,
              ),
            )
          ],
        ),
      );
    });
  }
}


class _MonitorPageState extends State<MonitorPage> {
  late String _time;

  @override
  void initState() {
    _time =
        "${DateTime.now().year.toString().padLeft(4, '0')}-${DateTime.now().month.toString().padLeft(2, '0')}-${DateTime.now().day.toString().padLeft(2, '0')} ${DateTime.now().hour.toString().padLeft(2, '0')}:${DateTime.now().minute.toString().padLeft(2, '0')}:${DateTime.now().second.toString().padLeft(2, '0')}";
    timer = Timer.periodic(const Duration(seconds: 1), (timer) { // here i set the timer variable
      _updateTime(); //just updating time
    });
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    ...
}
}

I was expecting to cancel the timer, but it gave me this message.


======== Exception caught by gesture ===============================================================
The following LateError was thrown while handling a gesture:
LateInitializationError: Field 'timer' has not been initialized.

When the exception was thrown, this was the stack: 
#0      timer (package:TPSEM_pocket/main.dart)
#1      _MyHomePageState.build.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:TPSEM_pocket/main.dart:184:23)
#2      State.setState (package:flutter/src/widgets/framework.dart:1203:30)
#3      _MyHomePageState.build.<anonymous closure>.<anonymous closure> (package:TPSEM_pocket/main.dart:182:19)
#4      _NavigationRailState.build.<anonymous closure> (package:flutter/src/material/navigation_rail.dart:463:62)
#5      _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1183:21)
#6      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:315:24)
#7      TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:652:11)
#8      BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:309:5)
#9      BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:242:7)
#10     PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:670:9)
#11     PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:98:12)
#12     PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:143:9)
#13     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:633:13)
#14     PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:141:18)
#15     PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:127:7)
#16     GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:495:19)
#17     GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:475:22)
#18     RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:430:11)
#19     GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:420:7)
#20     GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:383:5)
#21     GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:330:7)
#22     GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:299:9)
#23     _invoke1 (dart:ui/hooks.dart:328:13)
#24     PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:429:7)
#25     _dispatchPointerDataPacket (dart:ui/hooks.dart:262:31)
Handler: "onTap"
Recognizer: TapGestureRecognizer#0c5e3
  debugOwner: GestureDetector
  state: possible
  won arena
  finalPosition: Offset(115.5, 71.5)
  finalLocalPosition: Offset(115.5, 19.5)
  button: 1
  sent tap down
====================================================================================================

this is the point => LateInitializationError: Field 'timer' has not been initialized.

I tried to use a state variable and set it to timer then cancel it in onDestinationSelected(right before changing the selectedIndex variable), butt it didn’t work either.
How could I fix it? Thanks.

3

Answers


  1. Define the timer variable inside the _MyHomePageState class. That will be accessible within the state class and can be canceled.
    Additionally, cancel the timer in the dispose() method to avoid memory leaks & it will stop or cancel the timer.

    Here is the updated code:

    class _MyHomePageState extends State<MyHomePage> {
      late Timer timer; // Define timer variable here
    
      // Rest of your code...
    
      // This will call when the state object is initiate
      @override
      void initState() {
        super.initState();
        // Initialize the timer here
        timer = Timer.periodic(const Duration(seconds: 1), (timer) {
          _updateTime(); //just updating time
        });
      }
    
      // This will call when the state object is removed
      @override
      void dispose() {
        // Cancel the timer when the state is disposed
        timer.cancel();
        super.dispose();
      }
    
      // Rest of your code...
    }
    
    Login or Signup to reply.
  2. In this case you can dispose your timer variable in dispose method like this.

      @override
      void dispose() {
        timer.cancel();
        super.dispose();
      }
    
    Login or Signup to reply.
  3. Try the below code to access the timer, pass the reference object to the Monitor page can use the state management approach for accessing the timer across the pages.

    import 'package:flutter/material.dart';
    import 'dart:async';
    
    class MyHomePage extends StatefulWidget {
      const MyHomePage({super.key, required this.title});
    
      final String title;
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      
      Timer? timer;
      int selectedIndex = 0;
    
      @override
      Widget build(BuildContext context) {
        Widget page;
        switch (selectedIndex) {
          case 0:
            page = MonitorPage(timer);
          case 1:
            page = const ReportPage();
          case 2:
            page = const Placeholder();
          default:
            throw UnimplementedError("not found");
        }
        return LayoutBuilder(builder: (context, constraints) {
          return Scaffold(
            body: Row(
              children: [
                SafeArea(
                  child: NavigationRail(
                    extended: constraints.maxWidth >= 600,
                    destinations: const [
                      NavigationRailDestination(
                        icon: Icon(Icons.home),
                        label: Text('main'),
                      ),
                      NavigationRailDestination(
                        icon: Icon(Icons.list),
                        label: Text('report'),
                      ),
                      NavigationRailDestination(
                        icon: Icon(Icons.settings),
                        label: Text("setting"),
                      ),
                    ],
                    selectedIndex: selectedIndex,
                    onDestinationSelected: (value) {
                      setState(() {
                        if (selectedIndex == 0) {
                          timer?.cancel(); // here I'd like to cancel the timer
                        }
                        selectedIndex = value;
                      });
                    },
                  ),
                ),
                Expanded(
                  child: Container(
                    color: Theme.of(context).colorScheme.primaryContainer,
                    child: page,
                  ),
                )
              ],
            ),
          );
        });
      }
    }
    
    class MonitorPage extends StatefulWidget {
      Timer? timer;
      MonitorPage(this.timer,{super.key});
    
      @override
      State<MonitorPage> createState() => _MonitorPageState();
    }
    
    class _MonitorPageState extends State<MonitorPage> {
      late String _time;
     
    
      @override
      void initState() {
        _time =
            "${DateTime.now().year.toString().padLeft(4, '0')}-${DateTime.now().month.toString().padLeft(2, '0')}-${DateTime.now().day.toString().padLeft(2, '0')} ${DateTime.now().hour.toString().padLeft(2, '0')}:${DateTime.now().minute.toString().padLeft(2, '0')}:${DateTime.now().second.toString().padLeft(2, '0')}";
        widget.timer = Timer.periodic(const Duration(seconds: 1), (timer) { // here i set the timer variable
          _updateTime(); //just updating time
        });
        super.initState();
      }
      @override
      Widget build(BuildContext context) {
        ...
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search