我刚接触flutter/dart和响应式编程,我已经尝试了一段时间了。我在DartPad中构建了附加的测试代码,以确定我想要在我的应用程序中做什么。我已经创建了一个有状态小部件(DataRow),并且在它的State对象(_dataRowState)中有一个函数(setAndRefresher)。我正在尝试从小部件外部从另一个小部件(在ElevatedButton的onPressed:中)访问此函数:
datums.setAndRefresh!(datums.count + 1.0);
我已经构建了一个数据对象的全局列表,我称之为Datum,它有一个名为setAndRefresh的回调成员。在创建Stateful Widget时,我将列表中特定Datum的索引传递到Stateful Widget中,并尝试将回调函数存储到该数据对象中:
datums_datumIndex.setAndRefresh = setAndRefresher;
我确定我错过了什么,但不知道是什么。我想要做的事情是被阻止的吗?如果是,为什么?我既在努力解决这个问题,也在努力学习这门语言。
import 'package:flutter/material.dart';
class Datum extends Object {
/// data storage object
double count = 0;
String word = 'Uninitialized';
Function? setAndRefresh;
void prt() {
print('word: $word – count: $count – callback: $setAndRefresh');
}
void incrementCount() {
print('incrementCount() -> $count');
count = count + 1;
print(' -> $count');
}
}
List<Datum> datums = [];
/// =============================================
void main() {
// Create three datum in a list
datums.add(Datum());
datums.add(Datum());
datums.add(Datum());
for (Datum dt in datums) {
dt.prt();
}
datums[0].word = 'All buttons total';
datums[1].word = 'Button 1 count';
datums[2].word = 'Button 2 count';
for (Datum dt in datums) {
dt.prt();
}
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Testing',
theme: ThemeData(
// primarySwatch: Colors.blue,
backgroundColor: Colors.green,
),
home: Scaffold(
appBar: AppBar(
brightness: Brightness.dark,
backgroundColor: Colors.green,
),
body: Column(
// mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RichText(
text: const TextSpan(
text: 'Test Buttons',
),
),
const DataRow(datumIndex: 0),
const DataRow(datumIndex: 1),
const DataRow(datumIndex: 2),
],
),
),
);
}
}
/// =============================================
class DataRow extends StatefulWidget {
const DataRow({required this.datumIndex});
final int datumIndex;
@override
_DataRowState createState() => _DataRowState();
}
class _DataRowState extends State<DataRow> {
late int _datumIndex;
double count = 0;
@override
void initState() {
super.initState();
_datumIndex = widget.datumIndex;
print('Init setAndRefresh for $_datumIndex – callback: ${datums[_datumIndex].setAndRefresh}');
datums[_datumIndex].setAndRefresh = setAndRefresher;
print('Done init setAndRefresh for $_datumIndex – callback: ${datums[_datumIndex].setAndRefresh}');
}
void setAndRefresher({required double count}) {
print('Did set $count and refresh.');
setState(() {
datums[_datumIndex].count = count;
this.count = count;
});
}
@override
dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return lineItem(
name: '${datums[_datumIndex].word} is now: ${datums[_datumIndex].count} ',
color: Colors.orange,
);
}
Widget lineItem({required String name, required Color color}) {
return Container(
width: 500,
height: 50,
color: color,
child: Row(
children: <Widget>[
RichText(
text: TextSpan(text: name),
),
ElevatedButton(
onPressed: () {
print('Pressed Button $_datumIndex – callback: ${datums[_datumIndex].setAndRefresh}');
datums[_datumIndex].incrementCount();
setState(() {
count = datums[_datumIndex].count;
});
if (_datumIndex != 0 && datums[0].setAndRefresh != null) {
print('Now, increase All buttons total!()'); // This does print
datums[0].setAndRefresh!(datums[0].count + 1.0); // This is problem line?
print('Did it'); // this never prints
}
},
child: Text('Button ${widget.datumIndex}'),
),
],
),
);
}
}
这个测试代码创建了三个按钮。单击第一个按钮似乎可以工作,但单击其他两个按钮中的任何一个按钮都可以工作,然后该按钮似乎会冻结。以下是单击Button1、Button0,然后再次单击Button0所产生的控制台输出:
word: Uninitialized – count: 0 – callback: null
word: Uninitialized – count: 0 – callback: null
word: Uninitialized – count: 0 – callback: null
word: All buttons total – count: 0 – callback: null
word: Button 1 count – count: 0 – callback: null
word: Button 2 count – count: 0 – callback: null
Init setAndRefresh for 0 – callback: null
Done init setAndRefresh for 0 – callback: Closure: ({required double count}) => void from: function setAndRefresher() {
[native code]
}
Init setAndRefresh for 1 – callback: null
Done init setAndRefresh for 1 – callback: Closure: ({required double count}) => void from: function setAndRefresher() {
[native code]
}
Init setAndRefresh for 2 – callback: null
Done init setAndRefresh for 2 – callback: Closure: ({required double count}) => void from: function setAndRefresher() {
[native code]
}
Script error.
Pressed Button 1 – callback: Closure: ({required double count}) => void from: function setAndRefresher() {
[native code]
}
incrementCount() -> 0
-> 1
Now, increase All buttons total!()
Script error.
Pressed Button 0 – callback: Closure: ({required double count}) => void from: function setAndRefresher() {
[native code]
}
incrementCount() -> 0
-> 1
Pressed Button 0 – callback: Closure: ({required double count}) => void from: function setAndRefresher() {
[native code]
}
incrementCount() -> 1
-> 2
发布于 2021-08-22 19:04:04
而不是使用全局变量来做这些事情。Flutter为你提供了一个关键的类,可以用来做你想做的事情。
import 'package:flutter/material.dart';
class Datum extends Object {
/// data storage object
double count = 0;
String word = 'Uninitialized';
Function? setAndRefresh;
void prt() {
print('word: $word – count: $count – callback: $setAndRefresh');
}
void incrementCount() {
print('incrementCount() -> $count');
count = count + 1;
print(' -> $count');
}
}
List<Datum> datums = [];
/// =============================================
void main() {
// Create three datum in a list
datums.add(Datum());
datums.add(Datum());
datums.add(Datum());
for (Datum dt in datums) {
dt.prt();
}
datums[0].word = 'All buttons total';
datums[1].word = 'Button 1 count';
datums[2].word = 'Button 2 count';
for (Datum dt in datums) {
dt.prt();
}
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final zeroKey = GlobalKey<_DataRowState>();
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Testing',
theme: ThemeData(
// primarySwatch: Colors.blue,
backgroundColor: Colors.green,
),
home: Scaffold(
appBar: AppBar(
brightness: Brightness.dark,
backgroundColor: Colors.green,
),
body: Column(
// mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RichText(
text: const TextSpan(
text: 'Test Buttons',
),
),
DataRow(datumIndex: 0, key: zeroKey),
DataRow(datumIndex: 1, zeroKey: zeroKey),
DataRow(datumIndex: 2, zeroKey: zeroKey),
],
),
),
);
}
}
/// =============================================
class DataRow extends StatefulWidget {
final int datumIndex;
final GlobalKey<_DataRowState>? zeroKey;
const DataRow({Key? key, this.zeroKey, required this.datumIndex}) : super(key: key);
@override
_DataRowState createState() => _DataRowState();
}
class _DataRowState extends State<DataRow> {
late int _datumIndex;
double count = 0;
@override
void initState() {
super.initState();
_datumIndex = widget.datumIndex;
print('Init setAndRefresh for $_datumIndex – callback: ${datums[_datumIndex].setAndRefresh}');
datums[_datumIndex].setAndRefresh = setAndRefresher;
print('Done init setAndRefresh for $_datumIndex – callback: ${datums[_datumIndex].setAndRefresh}');
}
void setAndRefresher({required double count}) {
print('Did set $count and refresh.');
setState(() {
datums[_datumIndex].count = count;
this.count = count;
});
}
@override
dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return lineItem(
name: '${datums[_datumIndex].word} is now: ${datums[_datumIndex].count} ',
color: Colors.orange,
);
}
Widget lineItem({required String name, required Color color}) {
return Container(
width: 500,
height: 50,
color: color,
child: Row(
children: <Widget>[
RichText(
text: TextSpan(text: name),
),
ElevatedButton(
onPressed: () {
print('Pressed Button $_datumIndex – callback: ${datums[_datumIndex].setAndRefresh}');
datums[_datumIndex].incrementCount();
setState(() {
count = datums[_datumIndex].count;
});
if(widget.zeroKey != null) {
print('Now, increase All buttons total!()'); // This does print
widget.zeroKey?.currentState?.setAndRefresher(count: datums[0].count + 1.0); // This is problem line?
print('Did it');
}
// if (_datumIndex != 0 && datums[0].setAndRefresh != null) {
// // this never prints
// }
},
child: Text('Button ${widget.datumIndex}'),
),
],
),
);
}
}
传递给第一个DataRow()的零键将充当控制该小部件状态的键。然后,对于第二个和第三个DataRow(),我们再次传递zeroKey,但作为一个不同的参数,表明我们不想控制它们的状态。然后,如果_DataRowState()中的全局变量不为空,我们可以使用zeroKey,而不是使用全局变量。
注意zeroKey是如何以不同方式传递的:
[
...
DataRow(datumIndex: 0, key: zeroKey),
DataRow(datumIndex: 1, zeroKey: zeroKey),
DataRow(datumIndex: 2, zeroKey: zeroKey),
...
]
DataRow(datumIndex: 0, key: zeroKey)
第一个DataRow的关键参数是将zeroKey绑定到第一个小部件。
DataRow(datumIndex: 1,zeroKey: zeroKey)
第二个和第三个的zeroKey参数表明它只是另一个参数,zeroKey不应该绑定到这些小部件的状态。
发布于 2021-08-24 19:11:02
另一种方法是:可以通过将状态提升到其父状态来实现相同的功能,这在这种情况下要方便得多。
import 'package:flutter/material.dart';
class Datum extends Object {
/// data storage object
double count = 0;
String word = 'Uninitialized';
Function? setAndRefresh;
void prt() {
print('word: $word – count: $count – callback: $setAndRefresh');
}
void incrementCount() {
print('incrementCount() -> $count');
count = count + 1;
print(' -> $count');
}
}
List<Datum> datums = [];
/// =============================================
void main() {
// Create three datum in a list
datums.add(Datum());
datums.add(Datum());
datums.add(Datum());
for (Datum dt in datums) {
dt.prt();
}
datums[0].word = 'All buttons total';
datums[1].word = 'Button 1 count';
datums[2].word = 'Button 2 count';
for (Datum dt in datums) {
dt.prt();
}
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Testing',
theme: ThemeData(
// primarySwatch: Colors.blue,
backgroundColor: Colors.green,
),
home: Home(),
);
}
}
class Home extends StatefulWidget {
@override
State<Home> createState() => HomeState();
}
class HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
brightness: Brightness.dark,
backgroundColor: Colors.green,
),
body: Column(
// mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RichText(
text: const TextSpan(
text: 'Test Buttons',
),
),
...List.generate(datums.length, (index) => DataRow(
currentDatum: datums[index],
index: index,
onTap: () {
setState(() {
datums[index].incrementCount();
if(index != 0) datums[0].incrementCount();
});
}
)),
],
),
);
}
}
/// =============================================
class DataRow extends StatelessWidget {
final Datum currentDatum;
final int index;
final void Function()? onTap;
const DataRow({Key? key,required this.index,required this.onTap, required this.currentDatum}) : super(key: key);
@override
Widget build(BuildContext context) {
return lineItem(
name: '${currentDatum.word} is now: ${currentDatum.count} ',
color: Colors.orange,
);
}
Widget lineItem({required String name, required Color color}) {
return Container(
width: 500,
height: 50,
color: color,
child: Row(
children: <Widget>[
RichText(
text: TextSpan(text: name),
),
ElevatedButton(
onPressed: onTap,
child: Text('Button $index'),
),
],
),
);
}
}
在本例中,呈现按钮的小部件是dump,并且所有数据和操作都由其父组件处理。
但这种方法仍然存在一些问题,因为正在使用的List<Datum>
是全局的。我们也可以为List<Datum>
创建本地作用域。
但就我们的目的而言,上面的代码将在没有任何键的麻烦或任何时髦的逻辑的情况下完成任务。这也解决了你的问题,任何更新任何其他小部件,而不是一个键的麻烦。
然而,缺点是这将更新整个父窗口小部件本身,但这是一个合理的权衡。
https://stackoverflow.com/questions/68886673
复制相似问题