如何将 FAB 与 FutureBuilder 结合使用

How to use FAB with FutureBuilder

我想要实现的目标:仅当网页响应状态为 200 时才显示 FAB。

以下是我的代码的必要部分,我使用异步方法检查网页:

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  late Future<Widget> futureWidget;
 
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
 
  @override
  void initState() {
    super.initState();
    futureWidget = _getFAB();
  }
 
  Future<Widget> _getFAB() async {
    final response = await http
        .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
 
    if (response.statusCode == 200) {
      // If the server did return a 200 OK response,
      // return something to create FAB
      return const Text('something');
    } else {
      // If the server did not return a 200 OK response,
      // then throw an exception.
      throw Exception('Failed to load url');
    }
  }

如果快照有数据,我可以使用以下 FutureBuilder 获得结果:

        body: Center(
          child: FutureBuilder<Widget>(
            future: futureWidget,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return FloatingActionButton(
                    backgroundColor: Colors.deepOrange[800],
                    child: Icon(Icons.add_shopping_cart),
                    onPressed:
                        null); // navigate to webview, will be created later
              } else if (snapshot.hasError) {
                return Text('${snapshot.error}');
              }
 
              // By default, show a loading spinner.
              return const CircularProgressIndicator();
            },
          )

我的问题是我想在这里使用它,作为一个 floatingActionButton 小部件:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
[further coding...]
      ),
      body: // Indexed Stack to keep data
      IndexedStack(
          index: _selectedIndex,
          children: _pages,
        ),
        floatingActionButton: _getFAB(),
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>
[further coding...]

但在这种情况下,Flutter 会抛出错误 无法将参数类型 'Future' 分配给参数类型 'Widget?'

当然可以,因为我没有以这种方式使用 FutureBuilder。但是当我像上面的代码一样使用 FutureBuilder 时,Flutter 需要更多的位置参数,例如 column。这以完全不同的视图结束,因为 FAB 不再以典型的 FAB 位置放置在索引堆栈上。

我搜索了几个小时类似的问题,但一无所获。也许我的代码太复杂了,但 Flutter 对我来说还是个新手。如果有人能帮助我就太好了:)

您可以使用 just _getFAB() 方法来完成。您不能将 _getFab() 方法的 return 值分配给任何小部件,因为它具有 return 类型的 Future。而且,当您尝试从 FutureBuilder return FAB 时,它将 return FAB inside the Scaffold body。

因此,我建议您从 _getFAB() 方法获取数据并将这些数据分配给 class 级别变量。它可以是 bool、map 或 model class 等。您必须在小部件树中放置条件语句以填充数据获取之前和数据获取之后的状态。然后调用 setState((){}) ,它将用新数据重建小部件树。下面是一个简单的例子。

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class FabFuture extends StatefulWidget {
  const FabFuture({Key? key}) : super(key: key);

  @override
  State<FabFuture> createState() => _FabFutureState();
}

class _FabFutureState extends State<FabFuture> {
  bool isDataLoaded = false;

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

  Future<void> _getFAB() async {
    final response = await http
        .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));

    if (response.statusCode == 200) {
      isDataLoaded = true;
      setState(() {});
    } else {
      isDataLoaded = false;
      //TODO: handle error
      setState(() {});
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: const Center(
        child: Text('Implemet body here'),
      ),
      floatingActionButton: isDataLoaded
          ? FloatingActionButton(
              backgroundColor: Colors.deepOrange[800],
              child: const Icon(Icons.add_shopping_cart),
              onPressed: null)
          : const SizedBox(),
    );
  }
}

这里我使用了一个简单的 bool 值来确定我是否应该显示 FAB。 FAB 只会在数据成功获取后显示。 在练习这些方法并且您对它们有信心之后,我想建议学习状态管理解决方案来处理这些类型的工作。