无法将 onTap 字段传递给无状态小部件
Cannot Pass an onTap Field to a Stateless Widget
在网格视图中,由于我有类似的卡片项目,我决定创建一个包含每个卡片项目的自定义小部件。自定义小部件是无状态小部件。我遇到的问题是将 onTap 属性 传递给 class。事实上,我确实通过了并且没有错误,但是 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
试试这个
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 clash
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),
)
],
),
),
);
},
);
}
}
如果这不起作用,请告诉我。还有其他简单的方法。
将小部件的 onTap
参数更改为 final Function(BuildContext)
,然后在 onTap:
中将 ()=> this.onTap
更改为 onTap()
。应该这样做。
- 为您的
onTap
变量使用 VoidCallback
。
- 用
Builder
小部件包裹 HomeScreen
小部件树,以便 context
可以在 SnackBar
中使用。
我以您的代码为例添加了一个演示:
这个有效:
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);
}
}
在网格视图中,由于我有类似的卡片项目,我决定创建一个包含每个卡片项目的自定义小部件。自定义小部件是无状态小部件。我遇到的问题是将 onTap 属性 传递给 class。事实上,我确实通过了并且没有错误,但是 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
试试这个
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 clash
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),
)
],
),
),
);
},
);
}
}
如果这不起作用,请告诉我。还有其他简单的方法。
将小部件的 onTap
参数更改为 final Function(BuildContext)
,然后在 onTap:
中将 ()=> this.onTap
更改为 onTap()
。应该这样做。
- 为您的
onTap
变量使用VoidCallback
。 - 用
Builder
小部件包裹HomeScreen
小部件树,以便context
可以在SnackBar
中使用。
我以您的代码为例添加了一个演示:
这个有效:
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);
}
}