为什么我的 Flutter TextButton onPressed 函数会生成类型分配不匹配?

Why does my Flutter TextButton onPressed function generate a type assignment mismatch?

我想使用 maps_launcher 包启动 Google 地图。

当我将下面的 TextButton 代码复制到 MaterialApp 主页部分中的一个新项目(或剥离我自己的 main.dart)时,它工作正常。 (编辑:它必须包装在容器或脚手架中才能工作,否则我会收到相同类型不匹配的错误。)

但是,由于某种我不知道的原因,我在我想要的结构中不断遇到以下两个错误之一:

Scenario/Error一个

当我使用这段代码时:

onPressed: MapsLauncher.launchQuery(businesses[index].address)

我收到这个错误:

The argument type 'Future<bool>' can't be assigned to the parameter type 'void Function()?'.

Scenario/ErrorB

当我使用这段代码时:

我收到这个错误:

════════ Exception caught by widgets library ═══════════════════════════════════
The following _TypeError was thrown building:
type 'String' is not a subtype of type 'Widget'

When the exception was thrown, this was the stack
#0      BusinessList.build.<anonymous closure>.<anonymous closure>
#1      SliverChildBuilderDelegate.build
#2      SliverMultiBoxAdaptorElement._build
#3      SliverMultiBoxAdaptorElement.createChild.<anonymous closure>
#4      BuildOwner.buildScope
#5      SliverMultiBoxAdaptorElement.createChild
#6      RenderSliverMultiBoxAdaptor._createOrObtainChild.<anonymous closure>
#7      RenderObject.invokeLayoutCallback.<anonymous closure>
#8      PipelineOwner._enableMutationsToDirtySubtrees
#9      RenderObject.invokeLayoutCallback
#10     RenderSliverMultiBoxAdaptor._createOrObtainChild
#11     RenderSliverMultiBoxAdaptor.addInitialChild
#12     RenderSliverList.performLayout
#13     RenderObject.layout
#14     RenderSliverEdgeInsetsPadding.performLayout
#15     RenderSliverPadding.performLayout
#16     RenderObject.layout
#17     RenderViewportBase.layoutChildSequence
#18     RenderViewport._attemptLayout
#19     RenderViewport.performLayout
#20     RenderObject.layout
#21     RenderProxyBoxMixin.performLayout
#22     RenderObject.layout
#23     RenderProxyBoxMixin.performLayout
#24     RenderObject.layout
#25     RenderProxyBoxMixin.performLayout
#26     RenderObject.layout
#27     RenderProxyBoxMixin.performLayout
#28     RenderObject.layout
#29     RenderProxyBoxMixin.performLayout
#30     RenderObject.layout
#31     RenderProxyBoxMixin.performLayout
#32     RenderObject.layout
#33     RenderProxyBoxMixin.performLayout
#34     RenderObject.layout
#35     RenderProxyBoxMixin.performLayout
#36     RenderCustomPaint.performLayout
#37     RenderObject.layout
#38     RenderProxyBoxMixin.performLayout
#39     RenderObject.layout
#40     MultiChildLayoutDelegate.layoutChild
#41     _ScaffoldLayout.performLayout
#42     MultiChildLayoutDelegate._callPerformLayout
#43     RenderCustomMultiChildLayoutBox.performLayout
#44     RenderObject._layoutWithoutResize
#45     PipelineOwner.flushLayout
#46     RendererBinding.drawFrame
#47     WidgetsBinding.drawFrame
#48     RendererBinding._handlePersistentFrameCallback
#49     SchedulerBinding._invokeFrameCallback
#50     SchedulerBinding.handleDrawFrame
#51     SchedulerBinding._handleDrawFrame
#55     _invoke (dart:ui/hooks.dart:150:10)
#56     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:270:5)
#57     _drawFrame (dart:ui/hooks.dart:114:31)
(elided 3 frames from dart:async)

完整代码

这是产生上述错误的代码(粘贴过程中删除了包导入):

class BusinessList extends StatelessWidget {
  const BusinessList({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Business List')),
      body: StreamBuilder(
        stream: FirebaseFirestore.instance
            .collection('businesses')
            .snapshots()
            .map((snapshot) => snapshot.docs
                .map((doc) => Business.fromJson(doc.data()))
                .toList()),
        builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const CircularProgressIndicator();
          }
          if (snapshot.hasData) {
            if (snapshot.data!.isNotEmpty) {
              final businesses = snapshot.data;
              return ListView.builder(
                itemCount: businesses!.length,
                itemBuilder: (BuildContext context, int index) {
                  return Card(
                    child: Column(
                      children: [
                        ExpansionTile(
                          leading: const Icon(Icons.business),
                          title: Text(businesses[index].name),
                          subtitle: Text(businesses[index].category),
                          children: [
                            ListTile(
                              leading: const Icon(Icons.location_pin),
                              title: TextButton(
                                child: businesses[index].address,
                                onPressed: () => MapsLauncher.launchQuery(
                                    businesses[index].address),
                              ),
                            ),
                          ],
                        ),
                      ],
                    ),
                  );
                },
              );
            }
          }
          return const Text('error');
        },
      ),
    );
  }
}

我的代码(第 42 行)的红色下划线部分是:

MapsLauncher.launchQuery(businesses[index].address)

我认为它是 launchQuery returns 一个 bool,这对编译器来说似乎是,就像你试图给一个想要一个参数的 bool void Function?

我想这会解决问题。

onPressed: () => MapsLauncher.launchQuery(businesses[index].address),

或者您可能需要执行此操作,具体取决于 launchQuery 的工作方式:

onPressed: () async => await MapsLauncher.launchQuery(businesses[index].address),

这解决了类型不匹配问题,因为添加 () => 会告诉编译器您正在为它提供 onPressed 在按下按钮时执行的函数。

免责声明:这是未经测试的代码

好吧,我很高兴也很失望地报告说是初学者的错误导致了那个错误。

问题实际上不在于 onPressed,而在于需要 Widget 包装 String 的子项。

这是一个字符串:

child: businesses[index].address

所以我所要做的就是这样包装它:

child: Text(businesses[index].address)

当错误似乎出现在其他地方而不是实际位置时,这真是令人沮丧。我希望有一天这对某人有所帮助...