无法将 onTap 字段传递给无状态小部件

Posted

技术标签:

【中文标题】无法将 onTap 字段传递给无状态小部件【英文标题】:Cannot Pass an onTap Field to a Stateless Widget 【发布时间】:2020-12-24 15:03:41 【问题描述】:

在网格视图中,由于我有类似的卡片项目,我决定创建一个包含每个卡片项目的自定义小部件。自定义小部件是无状态小部件。我遇到的问题是将 onTap 属性传递给类。事实上,我确实通过了并且没有错误,但是 onTap 属性没有正确传播并且它没有显示我想要的 SnackBar。代码如下:

import 'package:flutter/material.dart';

const _padding = EdgeInsets.all(8.0);
const _splashColor = Colors.amber;

class HomeScreen extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(title: Text('Main Page'),),
      drawer: Drawer(
        elevation: 8.0,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
          children: [
           DrawerHeader(
              child: Column(
                children: [
                  Image(image: AssetImage('assets/images/top_picture.png'), fit: BoxFit.scaleDown, width: 100, height: 100),
                  Text('Home', style: Theme.of(context).textTheme.headline6)
                ],
              )),
          ListTile(leading: Icon(Icons.settings), title: Text('Settings')),
          ListTile(leading: Icon(Icons.exit_to_app), title: Text('Quit')),
          AboutListTile(icon: Icon(Icons.info), aboutBoxChildren: [Text('Copyright (C) 2020'), Text('Design And Programming: me')],)
        ],
      )),
      body: HomeScreenBody(),
    );
  




class HomeScreenBody extends StatelessWidget 
  @override
  Widget build(BuildContext context) 

      return Padding (
            padding: const EdgeInsets.all(8.0),
            child: GridView (
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
              children: [
                ItemCard(title: 'Balance', icon: 'assets/images/coins-balance.png', onTap: _comingSoon),
                ItemCard(title: 'Add Funds', icon: 'assets/images/add-money.png', onTap: _comingSoon,),
                ItemCard(title: 'Restaurant', icon: 'assets/images/restaurant.png', onTap: _comingSoon),
              ],
            ),
          );
  

  void _comingSoon(BuildContext context) 
    print('Showing snackbar...');
    final snack = SnackBar(content: Text('Coming soon...'));
    Scaffold.of(context).showSnackBar(snack);
  




class ItemCard extends StatelessWidget
  final String icon;

  final String title;

  final ValueChanged<BuildContext> onTap;

  const ItemCard(this.title, this.icon, this.onTap);

  @override
  Widget build(BuildContext context)
    return Builder(builder:(context) 
      return  Card(
        child: InkWell(
          splashColor: _splashColor,
          onTap: ()=> this.onTap,
          child: Column (
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Image(image: Image.asset(this.icon).image, ),
              Padding(
                padding: _padding,
                child: Text(this.title),
              )
            ],
          ),
        ),
      );
    ,

    );
  

我尝试将 onTap 字段的类型更改为 ValueChanged、ValueChanged、ValueChanged

【问题讨论】:

【参考方案1】:

试试这个

class ItemCard extends StatelessWidget
  final String icon;

  final String title;

  final void Function(BuildContext) onTap; //your function expects a context

  const ItemCard(this.title, this.icon, this.onTap);

  @override
  Widget build(BuildContext context)
    return Builder(builder:(ctx)  //changed to ctx so that contexts don't ***
      return  Card(
        child: InkWell(
          splashColor: _splashColor,
          onTap: ()=> this.onTap(context), //pass context here
          child: Column (
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Image(image: Image.asset(this.icon).image, ),
              Padding(
                padding: _padding,
                child: Text(this.title),
              )
            ],
          ),
        ),
      );
    ,

    );
  



如果这不起作用,请告诉我。还有其他简单的方法。

【讨论】:

有趣。这行得通!似乎是因为我没有在 InkWell 的 onTap 中传递上下文。【参考方案2】:
    为您的onTap 变量使用VoidCallback。 用Builder 小部件包装HomeScreen 小部件树,以便在SnackBar 中使用context

我以你的代码为例添加了一个演示:


这行得通:

class ItemCard extends StatelessWidget
  final String icon;

  final String title;

  final VoidCallback onTap; // use a VoidCallback instead

  const ItemCard(this.title, this.icon, this.onTap);

  @override
  Widget build(BuildContext context)
    return Builder(builder:(context) 
      return  Card(
        child: InkWell(
          splashColor: _splashColor,
          onTap: onTap, // assign the onTap property
          child: Column (
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Image(image: Image.asset(this.icon).image, ),
              Padding(
                padding: _padding,
                child: Text(this.title),
              )
            ],
          ),
        ),
      );
    ,

    );
  
class HomeScreenBody extends StatelessWidget 
  @override
  Widget build(BuildContext context) 

      return Builder(
        builder: (context),
              child: Padding (
              padding: const EdgeInsets.all(8.0),
              child: GridView (
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
                children: [
                  ItemCard(title: 'Balance', icon: 'assets/images/coins-balance.png', onTap: () => _comingSoon(context)),
                  ItemCard(title: 'Add Funds', icon: 'assets/images/add-money.png', onTap: () => _comingSoon(context),),
                  ItemCard(title: 'Restaurant', icon: 'assets/images/restaurant.png', onTap: () => _comingSoon(context)),
                ],
              ),
            );
        ,
      );
  

  void _comingSoon(context) 
    print('Showing snackbar...');
    final snack = SnackBar(content: Text('Coming soon...'));
    Scaffold.of(context).showSnackBar(snack);
  

【讨论】:

谢谢。这行得通。不过,我想知道 InkWell 内部的 onTap: this.onTaponTap:()=&gt;this.onTap 有什么区别。后者不起作用。 VoidCallbackvoid Function(BuildContext) 一样吗? 不,VoidCallbackFunction(BuildContext) 不同。 VoidCallback 不需要参数。 @codezombie【参考方案3】:

将小部件的onTap 参数更改为final Function(BuildContext),然后将onTap: 中的()=&gt; this.onTap 更改为onTap()。那应该这样做。

【讨论】:

其实也需要上下文。没有它就不行。 确实如此。我的错。编辑了我的答案。

以上是关于无法将 onTap 字段传递给无状态小部件的主要内容,如果未能解决你的问题,请参考以下文章

无状态颤振小部件中的非最终字段

Flutter:了解无状态/有状态小部件如何布局其子级?

有状态与无状态的区别如何影响小部件何时重建?

Flutter - 无法从父小部件更改子状态

从无状态小部件调用有状态小部件的 setState

我的无状态小部件触发了我无法分配的错误消息