You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter限制帧率以降低电池消耗的实现方法

Flutter 限制应用帧率到指定值(如30FPS)的可行方案

你提到的重写WidgetBindings的scheduleFrame方法确实能实现帧率限制,但属于底层修改,侵入性较强。下面提供两种更简洁、低侵入的方案,适配不同场景:

方案一:全局帧率限制(适用于整个应用)

通过SchedulerBinding的持久帧回调,计算帧间隔来控制下一次帧调度的时机,实现全局帧率锁定。

import 'dart:async';
import 'package:flutter/scheduler.dart';

class FrameRateLimiter {
  // 目标帧率,可按需修改
  static const targetFps = 30;
  static final _frameInterval = Duration(milliseconds: 1000 ~/ targetFps);
  static DateTime? _lastFrameTime;

  static void init() {
    SchedulerBinding.instance.addPersistentFrameCallback((_) {
      if (_lastFrameTime == null) {
        _lastFrameTime = DateTime.now();
        return;
      }

      final now = DateTime.now();
      final elapsed = now.difference(_lastFrameTime!);
      if (elapsed < _frameInterval) {
        // 延迟到目标间隔后再调度下一帧
        Timer(_frameInterval - elapsed, () {
          SchedulerBinding.instance.scheduleFrame();
        });
        // 取消默认的帧调度,避免重复触发
        final callbackId = SchedulerBinding.instance.scheduleFrameCallback((_) {});
        SchedulerBinding.instance.cancelFrameCallbackWithId(callbackId);
      } else {
        _lastFrameTime = now;
      }
    });
  }
}

// 在应用启动时初始化
void main() {
  FrameRateLimiter.init();
  runApp(const MyApp());
}

方案二:仅限制动画帧率(适用于局部动画场景)

如果只是应用内的动画需要降低帧率,无需全局修改,可以自定义TickerProvider来控制动画的触发间隔。

import 'package:flutter/scheduler.dart';
import 'dart:async';

class LimitedTickerProvider extends TickerProvider {
  static const targetFps = 30;
  final Duration _interval = Duration(milliseconds: 1000 ~/ targetFps);
  Timer? _timer;
  final List<Ticker> _tickers = [];

  @override
  Ticker createTicker(TickerCallback onTick) {
    final ticker = Ticker(onTick, debugLabel: 'LimitedFpsTicker');
    _tickers.add(ticker);
    _startTimerIfNeeded();
    return ticker;
  }

  void _startTimerIfNeeded() {
    if (_timer == null && _tickers.isNotEmpty) {
      _timer = Timer.periodic(_interval, (_) {
        final timestamp = SchedulerBinding.instance.currentFrameTimeStamp;
        for (final ticker in _tickers) {
          if (ticker.isActive) ticker.tick(timestamp);
        }
      });
    }
  }

  @override
  void dispose() {
    _timer?.cancel();
    for (final ticker in _tickers) ticker.dispose();
    super.dispose();
  }
}

// 使用示例
class AnimatedDemo extends StatefulWidget {
  const AnimatedDemo({super.key});

  @override
  State<AnimatedDemo> createState() => _AnimatedDemoState();
}

class _AnimatedDemoState extends State<AnimatedDemo> {
  late AnimationController _controller;
  late LimitedTickerProvider _limitedTicker;

  @override
  void initState() {
    super.initState();
    _limitedTicker = LimitedTickerProvider();
    _controller = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: _limitedTicker,
    )..repeat();
  }

  @override
  void dispose() {
    _controller.dispose();
    _limitedTicker.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RotationTransition(
      turns: _controller,
      child: const FlutterLogo(size: 200),
    );
  }
}

方案对比

  • 全局方案:对整个应用的渲染生效,适合需要统一降低帧率的场景,但可能会影响系统原生组件的渲染节奏。
  • 局部动画方案:仅作用于指定动画,侵入性低,不会干扰应用其他部分的正常渲染。
  • 你提到的重写WidgetBindings方案:虽然有效,但属于框架底层修改,后续Flutter版本更新可能导致兼容性问题,不推荐优先使用。

内容的提问来源于stack exchange,提问作者user3612643

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 17:30:19