当前位置: 首页 > article >正文

flutter Timer报错

 报错一:_timer没有被初始化

        需求:按键之后才开始计时,如果一进来就初始化,就会立马计时,不符合要求。所以要按键完之后才初始化_timer。所以就需要用var 或者 dynamic这种不确定类型,来修饰_timer变量,late会报没有初始化

// late Timer _timer;//TODO 这样写会报:没有初始化
  dynamic _timer;//or  var 来修饰_timer;

报错二:

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: setState() called after dispose(): _RecordExeWidgetState#1023f(lifecycle state: defunct, not mounted, ticker inactive) [ ]

E/flutter (17297): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback. [ ]

E/flutter (17297): The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree. [ ]

E/flutter (17297): This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().

         原因:这个错误信息表明在Flutter应用中有一个setState()调用发生在了Widget被卸载之后,具体错误信息如下:
原因:通常这种情况发生在从定时器或动画回调中调用了setState()方法,而此时对应的State对象已经被销毁。
解决办法:
在dispose()方法中取消定时器或停止监听动画。
在调用setState()之前检查当前State对象是否仍然mounted。
如何避免
方法一:在dispose中清理资源
确保在State对象被销毁时释放所有资源,例如取消定时器:

class RecordExeWidget extends StatefulWidget {
  @override
  _RecordExeWidgetState createState() => _RecordExeWidgetState();
}

class _RecordExeWidgetState extends State<RecordExeWidget> {
  Timer _timer;

  @override
  void initState() {
    super.initState();
    _timer = Timer.periodic(Duration(seconds: 1), (timer) {
      if (mounted) {
        // 更新UI
        setState(() {
          // 更新逻辑
        });
      }
    });
  }

  @override
  void dispose() {
    _timer.cancel(); // 取消定时器
    super.dispose();
  }
}

方法二:检查mounted属性
在执行任何可能触发setState()操作的地方,先检查当前State对象是否仍然有效:

if (mounted) {
  setState(() {
    // 更新状态
  });
}

报错三:

Unhandled Exception: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 4833 pos 12: '_lifecycleState != _ElementLifecycle.defunct': is not true.

        原因:这个错误信息表明在Flutter框架内部检测到了一个断言失败,具体信息为 _lifecycleState != _ElementLifecycle.defunct 不成立。这意味着某个Widget的状态已经变为 defunct(即已被销毁),但仍尝试对其进行操作。
解决方案
检查生命周期状态 在调用 setState 之前,确保当前 State 对象仍然有效。
清理资源 确保在 dispose 方法中取消所有定时器和监听器。
示例代码
假设你有一个自定义的Widget,并且在这个Widget中有定时器或其他异步操作,你可以按照以下方式修改代码:
示例代码:

import 'package:flutter/material.dart';

class RecordExeWidget extends StatefulWidget {
  @override
  _RecordExeWidgetState createState() => _RecordExeWidgetState();
}

class _RecordExeWidgetState extends State<RecordExeWidget> {
  Timer _timer;
  int _counter = 0;

  @override
  void initState() {
    super.initState();
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (mounted) {
        setState(() {
          _counter++;
        });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Text('Counter: $_counter');
  }

  @override
  void dispose() {
    _timer?.cancel(); // 取消定时器
    super.dispose();
  }
}

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: Text('Example')),
      body: Center(child: RecordExeWidget()),
    ),
  ));
}

解释
1,检查 mounted 属性

        在 Timer 的回调中,通过检查 mounted 属性来确保当前 State 对象仍然有效。如果 mounted 为 false,则说明 Widget 已经被销毁,不应再调用 setState。


2,清理定时器

        在 dispose 方法中取消定时器,以防止内存泄漏和其他潜在问题。
通过这些步骤,可以有效地避免在 Widget 被销毁后仍尝试进行状态更新的情况。这样可以确保应用运行更加稳定,避免不必要的错误。

错误四:计时器停止后不会再启动

原因:

//如果这么写
// _timer ??= Timer.periodic(const Duration(seconds: 1), (Timer timer) {
//TODO _timer?.cancel(); 执行后 timer里边就会不会再执行了,所以需要重新初始化
/// Example:
/// ```dart
/// final timer =
///     Timer(const Duration(seconds: 5), () => print('Timer finished'));
/// // Cancel timer, callback never called.
/// timer.cancel();
/// ```
//所以应该每次都赋值
_timer = Timer.periodic(const Duration(seconds: 1), (Timer timer){}
//TODO 页面关闭的时候记得 _timer?.cancel();_timer = null;就行

完整代码如下: 

import 'dart:async';
import 'dart:developer';

import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:game_lib/common/common_page.dart';
import 'package:game_lib/common/my_tap.dart';
import 'package:lottie/lottie.dart';

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

  @override
  State<StatefulWidget> createState() {
    return _RecordExeWidgetState();
  }
}

class _RecordExeWidgetState extends State<RecordExeWidget>
    with RecordGuideVoice, SingleTickerProviderStateMixin {
  bool recording = false;
  late final AnimationController animationController =
      AnimationController(vsync: this, duration: 1.seconds);

  int _seconds = 0;
  // late Timer _timer;//TODO 这样写会报:没有初始化
  dynamic _timer;

  void _startTimer() {
    recording = true;
    // _timer ??= Timer.periodic(const Duration(seconds: 1), (Timer timer) {
    //TODO _timer?.cancel(); 执行后 timer里边就会不会再执行了,所以需要重新初始化
    /// Example:
    /// ```dart
    /// final timer =
    ///     Timer(const Duration(seconds: 5), () => print('Timer finished'));
    /// // Cancel timer, callback never called.
    /// timer.cancel();
    /// ```
    //TODO 页面关闭的时候记得 _timer?.cancel();_timer = null;就行
    _timer = Timer.periodic(const Duration(seconds: 1), (Timer timer) {
      log("===== record_ext_widget recording:$recording ,mounted:$mounted");
      if (recording && mounted) {
        log("===== record_ext_widget 开始计时");
        setState(() {
          _seconds += 1;
        });
      }
    });
  }

  void _stopTimer() {
    if (recording && mounted) {
      log("===== record_ext_widget 停止计时");
      setState(() {
        _seconds = 0;
      });
    }
    recording = false;
    _timer?.cancel();
    _timer = null;
  }

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

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

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        //声音波浪
        Image(
          image: const AssetImage(
              "assets/images/ic_sound_wave.png"),
          width: 164.w,
          height: 76.h,
          fit: BoxFit.cover,
        ),
        SizedBox(
            width: 164.w,
            height: 76.h,
            child: Lottie.asset("assets/images/wave.zip",
                package: "game_lib",
                controller: animationController,
                width: 164.w,
                height: 76.h,
                fit: BoxFit.fill)),
        SizedBox(
          height: 10.h,
        ),
        //倒数秒
        Row(
          verticalDirection: VerticalDirection.down,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              "$_seconds",
              style: TextStyle(
                color: const Color(0xff26AFC7),
                fontFamily: "FZ-B",
                fontSize: 40.sp,
              ),
            ),
            Text(
              "s",
              style: TextStyle(
                color: const Color(0xff26AFC7),
                fontFamily: "FZ-B",
                fontSize: 36.sp,
              ),
            ),
          ],
        ),
        //麦克风图片,可以点击,点击有缩放效果
        MyTap(
          onTap: () {
            //TODO 开始播放动画(波浪+麦克风),开始录音
            //TODO 录音结束,提交后台,动画也要结束
            if (!recording) {
              _startTimer();
            } else {
              _stopTimer();
            }
            BotToast.showText(text: recording ? "开始录音了..." : "停止录音了...");
          },
          child: Image(
            image: const AssetImage(
                "assets/images/ic_microphone.png"),
            width: 324.w,
            height: 324.h,
            fit: BoxFit.cover,
          ),
        ),
      ],
    );
  }
}


http://www.kler.cn/a/292434.html

相关文章:

  • python抓取工具【pyppeteer】用法 直接运行 无错
  • H.265流媒体播放器EasyPlayer.js H.264/H.265播放器chrome无法访问更私有的地址是什么原因
  • 【服务器】本地安装X11 服务器-Windows
  • sql分区
  • Redis知识点整理 - 脑图
  • 认证鉴权框架SpringSecurity-1--概念和原理篇
  • IMU助力JAXA空间站机器人
  • 【Linux】gcc/g++ 、make/Makefile、git、gdb 的使用
  • 3276. 选择矩阵中单元格的最大得分
  • Qt 创建一个json数组对象写入文档并从文档读出q
  • /单元测试
  • 前端Worker 的应用
  • 3. GIS后端工程师岗位职责、技术要求和常见面试题
  • 羽毛球运动员的运动姿势-标准动作识别判断
  • 无人机遥控器的材料组成!!!
  • 06_TensorFlow2数学计算大揭秘:让AI也学会‘加减乘除’,笑料不断,干货满满!
  • U盘未初始化困境与数据拯救
  • 骨传导耳机哪个牌子好用?精选五款黄金畅销骨传导机型测评
  • 1、.Net UI框架:Avalonia UI - .Net宣传系列文章
  • 为基于物联网的监测应用构建边缘云连续体架构
  • 学习node.js十三,文件的上传于下载
  • C++:STL简介
  • GFP-GAN容器构建说明
  • 数据分析面试常见50个问题及解答要点(五)
  • Curl命令详解
  • 【全志H616】【开源】 ARM-Linux 智能分拣项目:阿里云、网络编程、图像识别