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

Flutter FutureBuilder反复重建生成新随机数,如何阻止?

解决Flutter FutureBuilder每次重建都重新生成Future的问题

问题根源

你遇到的问题本质是:在build方法中直接调用fetchRandomNumber()创建Future。每次组件重建(比如点击计数器触发setState)时,build方法会重新执行,导致新的Future被创建,FutureBuilder自然会重新请求数据,生成新的随机数。

两种有效解决方案

方案1:将Future作为State成员变量初始化

把Future存在State类的成员变量里,在initState中初始化,确保只执行一次。需要手动刷新时,再重新赋值这个变量并触发setState。

修改后的完整代码

import 'package:flutter/material.dart';
import 'dart:math';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'FutureBuilder Example',
      home: FutureBuilderExample(),
    );
  }
}

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

  @override
  _FutureBuilderExampleState createState() => _FutureBuilderExampleState();
}

class _FutureBuilderExampleState extends State<FutureBuilderExample> {
  int counter = 0;
  Future<int>? _randomNumberFuture; // 存储Future的成员变量

  Future<int> fetchRandomNumber() async {
    await Future.delayed(const Duration(milliseconds: 500));
    return Random().nextInt(100);
  }

  @override
  void initState() {
    super.initState();
    _randomNumberFuture = fetchRandomNumber(); // 初始化时仅执行一次
  }

  // 手动刷新随机数的方法
  void _refreshRandomNumber() {
    setState(() {
      _randomNumberFuture = fetchRandomNumber();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('FutureBuilder with Random Number')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text('Counter: $counter', style: const TextStyle(fontSize: 20)),
          const SizedBox(height: 20),
          FutureBuilder<int>(
            future: _randomNumberFuture, // 使用成员变量的Future
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return const Center(child: CircularProgressIndicator());
              } else if (snapshot.hasError) {
                return Center(child: Text('Error: ${snapshot.error}'));
              } else {
                return Column(
                  children: [
                    Text('Random Number: ${snapshot.data}', style: const TextStyle(fontSize: 20)),
                    const SizedBox(height: 10),
                    ElevatedButton(
                      onPressed: _refreshRandomNumber,
                      child: const Text('刷新随机数'),
                    ),
                  ],
                );
              }
            },
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            counter++; // 触发重建,但不会重新生成随机数
          });
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

关键改动说明

  • 新增_randomNumberFuture成员变量存储Future对象
  • 在initState中初始化Future,确保组件首次加载时仅执行一次数据请求
  • FutureBuilder的future参数改为使用这个成员变量,避免每次build重新创建Future
  • 添加_refreshRandomNumber方法,手动触发新的Future创建并更新UI

方案2:使用AsyncMemoizer缓存Future结果

Flutter自带的AsyncMemoizer(位于package:flutter/foundation.dart)可以帮助我们缓存Future的执行结果,确保同一Future仅被执行一次,直到手动清除缓存。

修改后的完整代码

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart'; // 引入AsyncMemoizer
import 'dart:math';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'FutureBuilder Example',
      home: FutureBuilderExample(),
    );
  }
}

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

  @override
  _FutureBuilderExampleState createState() => _FutureBuilderExampleState();
}

class _FutureBuilderExampleState extends State<FutureBuilderExample> {
  int counter = 0;
  final _memoizer = AsyncMemoizer<int>(); // 声明AsyncMemoizer实例

  Future<int> fetchRandomNumber() {
    // 使用runOnce确保Future仅执行一次
    return _memoizer.runOnce(() async {
      await Future.delayed(const Duration(milliseconds: 500));
      return Random().nextInt(100);
    });
  }

  void _refreshRandomNumber() {
    setState(() {
      _memoizer.clear(); // 清除缓存,下次调用fetchRandomNumber会重新执行
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('FutureBuilder with Random Number')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text('Counter: $counter', style: const TextStyle(fontSize: 20)),
          const SizedBox(height: 20),
          FutureBuilder<int>(
            future: fetchRandomNumber(), // 直接调用方法,但runOnce会缓存结果
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return const Center(child: CircularProgressIndicator());
              } else if (snapshot.hasError) {
                return Center(child: Text('Error: ${snapshot.error}'));
              } else {
                return Column(
                  children: [
                    Text('Random Number: ${snapshot.data}', style: const TextStyle(fontSize: 20)),
                    const SizedBox(height: 10),
                    ElevatedButton(
                      onPressed: _refreshRandomNumber,
                      child: const Text('刷新随机数'),
                    ),
                  ],
                );
              }
            },
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            counter++;
          });
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

关键改动说明

  • 引入AsyncMemoizer并创建实例
  • 用_memoizer.runOnce()包裹原有的异步逻辑,确保逻辑仅执行一次
  • 手动刷新时调用_memoizer.clear()清除缓存,让下次调用重新执行异步逻辑

方案选择建议

  • 如果需要灵活控制Future的创建时机(比如手动刷新),优先选方案1,逻辑更直观
  • 如果只需要Future执行一次,偶尔需要重置,方案2更简洁,无需额外管理Future变量

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 23:47:34