Flutter - 如何将未来传递给小部件而不执行它?

irs*_*han 3 dart flutter flutter-futurebuilder

我在我的一个小部件中使用 FutureBuilder,它需要一个 future。我通过它的构造函数将 future 传递给小部件。问题是,当将 future 传递给小部件时,它会自动执行。由于 FutureBuilder 只接受 Future 而不是 Future Function() 我被迫初始化一个变量,该变量又调用异步函数。但我不知道如何在不执行 Future 的情况下传递它。

这是完整的工作示例:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  final icecreamSource = DataService.getIcecream();
  final pizzaSource = DataService.getPizza();
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
     
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: Column(
            children: [
              MenuButton(label: 'Ice Cream', dataSource: icecreamSource),
              MenuButton(label: 'Pizza', dataSource: pizzaSource),
            ]
          ),
        ),
      ),
    );
  }
}


class MenuButton extends StatelessWidget {
  final String label;
  final Future<String> dataSource;
  
  const MenuButton({required this.label, required this.dataSource});
  
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: ElevatedButton(
        child: Text(label),
        onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => AnotherPage(label: label, dataSource: dataSource)))
      ),
    );
  }
}


// Mock service to simulate async data sources
class DataService {
  static Future<String> getIcecream() async {
    print('Trying to get ice cream...');
    return await Future.delayed(const Duration(seconds: 3), () => 'You got Ice Cream!');
  }
  
  static Future<String> getPizza() async {
    print('Trying to get pizza...');
    return await Future.delayed(const Duration(seconds: 2), () => 'Yay! You got Pizza!');
  }
}


class AnotherPage extends StatefulWidget {
  final String label;
  final Future<String> dataSource;
  const AnotherPage({required this.label, required this.dataSource});
  @override
  State<AnotherPage> createState() => _AnotherPageState();
}

class _AnotherPageState extends State<AnotherPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.label)),
      body: Center(
        child: FutureBuilder<String>(
        future: widget.dataSource,
        builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
          if(snapshot.hasData) {
            return Text('${snapshot.data}');
          } else if(snapshot.hasError) {
            return Text('Error occurred ${snapshot.error}');
          } else {
            return Text('Fetching ${widget.label}, please wait...');
          }
        }
      ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

预期的行为是,当我按下主页上的“冰淇淋”或“披萨”按钮时,应出现名为“另一页”的小部件/屏幕,并且应执行异步请求,在此期间应显示加载消息。然而,正在发生的情况是,在加载主页时,甚至在按下任何按钮之前,两个异步请求都会被执行。按下任何按钮时,不会出现加载消息,因为请求已经完成,因此它直接显示结果,这是完全不希望的。我现在对 Futures 和 Future Functions 完全感到困惑。有人请帮帮我。

Tri*_*ine 5

Future您可以传递返回 的函数本身,而不是传递Future。您可以在DartPad上尝试这个示例。

你必须MyApp这样修改:

final icecreamSource = DataService.getIcecream; // No () as we want to store the function
final pizzaSource = DataService.getPizza; // Here aswell
Run Code Online (Sandbox Code Playgroud)

MenuButton我们需要AnotherPage

final Future<String> Function() dataSource; // Instead of Future<String> dataSource
Run Code Online (Sandbox Code Playgroud)

不,我们可以将 future 直接传递给,FutureBuilder但让 FutureBuilder 直接执行 future 是不好的做法,因为构建方法被多次调用。相反,我们有这个:

class _AnotherPageState extends State<AnotherPage> {
  late final Future<String> dataSource = widget.dataSource(); // Gets executed right here
  ...
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以把这个未来交给未来的建造者。