Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >是否可以从另一个小部件访问Stateful widget中的实例函数?

是否可以从另一个小部件访问Stateful widget中的实例函数?
EN

Stack Overflow用户
提问于 2021-08-22 18:30:55
回答 2查看 42关注 0票数 0

我刚接触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;

我确定我错过了什么,但不知道是什么。我想要做的事情是被阻止的吗?如果是,为什么?我既在努力解决这个问题,也在努力学习这门语言。

代码语言:javascript
运行
AI代码解释
复制
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所产生的控制台输出:

代码语言:javascript
运行
AI代码解释
复制
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
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-08-22 19:04:04

而不是使用全局变量来做这些事情。Flutter为你提供了一个关键的类,可以用来做你想做的事情。

代码语言:javascript
运行
AI代码解释
复制
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是如何以不同方式传递的:

代码语言:javascript
运行
AI代码解释
复制
[
  ...
  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不应该绑定到这些小部件的状态。

票数 0
EN

Stack Overflow用户

发布于 2021-08-24 19:11:02

另一种方法是:可以通过将状态提升到其父状态来实现相同的功能,这在这种情况下要方便得多。

代码语言:javascript
运行
AI代码解释
复制
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>创建本地作用域。

但就我们的目的而言,上面的代码将在没有任何键的麻烦或任何时髦的逻辑的情况下完成任务。这也解决了你的问题,任何更新任何其他小部件,而不是一个键的麻烦。

然而,缺点是这将更新整个父窗口小部件本身,但这是一个合理的权衡。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68886673

复制
相关文章
Flutter 中 stateless 和 stateful widget 的区别[Flutter专题9]
Flutter 中 stateless 和 stateful widget 的区别
徐建国
2021/11/30
2.6K0
我可以在一个构造函数中调用另一个构造函数么
但在 C++11 版本之前是不可以的,不过你可以通过两种方式来模拟实现(可以参见 the C++ FAQ entry),
ClearSeve
2022/02/10
4K0
Genesis框架从入门到精通(13): 小部件函数
Genesis Explained系列中的上一篇文章是关于配置项函数的,并演示了一些用于从数据库中检索自定义字段和主题选项的重要函数。本文是关于 genesis/lib/functions/widgetize.php 文件的。 以下是文件中所有函数的列表:
丘壑
2019/03/13
1.2K0
go 中 struct 是否可以比较?
今天来水一篇,最近比较忙,一直没有时间写 go 相关的,今天从一个小问题入手,来说说 struct 的比较问题。
LinkinStar
2022/09/01
1K0
flutter 起步
Flutter是谷歌的移动UI框架,可以运行在ios与android系统上,可以以完成app的开发,
ronixiao
2022/09/21
5.3K0
Notion的页面插入小控件Widget
Widget 不是一个小型的 App,它是一种新的桌面内容展现形式,主要是用于弥补主应用程序无法及时展示用户所关心的数据。
后端云
2022/01/20
2.7K0
Notion的页面插入小控件Widget
起飞了!Git新开源高星《Flutter跨平台开发入门与实战笔记》安卓高阶必备
有了Flutter,就有了几乎无穷无尽的可能性,因此即使是体量巨大的App也可以轻松地被创建出来。如果你是做移动App开发的并且尚未尝试过Flutter,我强烈建议你试一下,因为我相信你也会爱上它的。
用户1907613
2021/09/29
1.5K0
起飞了!Git新开源高星《Flutter跨平台开发入门与实战笔记》安卓高阶必备
php判断访问者是否手机客户端实例
上面的方法也存在一些小问题,这里我根据自己的经验来告诉大我们可以使用屏幕宽度来实现再加机器类型了,因为有时HTTP_USER_AGENT信息在我们上面并未定义过了,不过上面实现几乎兼容了主流手机了。 我们还可以使用js
用户8099761
2023/05/10
2.1K0
java构造函数调用另一个构造函数_java中的构造函数
* 构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法
用户7886150
2021/04/29
5.9K0
C# 判断指定URL地址是否可以正常访问
C# 判断指定URL地址是否可以正常访问 如果只是判断url是否存在,不需要抓取整个url,只需要通过head方式请求即可
用户7705674
2021/11/02
3.5K0
从外部访问Kubernetes中的Pod
本文主要讲解访问kubernetes中的Pod和Serivce的几种方式,包括如下几种:
我的小碗汤
2019/07/30
3.5K0
Flutter中是使用RxDart代替Stateful
其中_currentIndex记录了当前tab的索引,onTap中调用setState来更新视图,没毛病。
我不是码神
2022/07/28
1.2K2
可以提高你Python效率的几个小函数!
这篇文章我们来看几个很有用的 Python 内置函数 。这些函数简直是屌爆了,我认为每个 Pythoner 都应该知道这些函数。
1480
2019/08/05
7350
Flutter | 基础Widget
在 Fluter 中,几乎所有的都是一个 widget ,与原生开发不同的是,widget 的范围更加广阔,他不仅可以表示 UI 元素,也可以表示一些功能的组件,如手势检测的 widget,用于主题数据传递的 Theme 等等。所以,在大多数时候,可以认为 widget 就是一个控件,不必纠结于概念
345
2022/02/11
1.4K0
Flutter | 基础Widget
Flutter一切皆widget但是不要将所有东西放入一个widget
作为 Flutter 开发人员,我相信您在您的开发生活中至少听说过这句流行的句子:“**一切都是widget”。这是 Flutter 的口头禅,它揭示了这个非常好的 SDK 的内在力量!
徐建国
2021/11/30
1.4K0
Flutter一切皆widget但是不要将所有东西放入一个widget
Flutter Widget框架之旅 顶
Flutter小部件采用现代反应式框架构建,从React中获得灵感。 中心思想是你从小部件中构建你的UI。 小组件描述了他们的视图在给定其当前配置和状态时应该看起来像什么。 当小部件的状态发生变化时,小部件会重新构建它的描述,该描述与前面的描述不同,以确定底层渲染树从一个状态转换到下一个状态所需的最小更改。
南郭先生
2018/08/14
7.3K0
Flutter UI原理
Widgets是Flutter App用户交互的基础构成,每个widget代表的是用户交互的一部分(不可变的),不像其他frameworks会分开views,viewControllers,layout或者其他属性,Flutter有一个统一的对象模型:widget。
Helloted
2022/06/08
3.8K0
Flutter UI原理
点击加载更多

相似问题

setState不更新StateFul Widget

11

Flutter Stateful Widget设置构造函数默认值

1137

是否可以从Dashboard小部件中创建新的小部件实例?

60

列表更新Stateful Widget中的变量以及局部变量

18

无法使用Flutter Stateful小部件

116
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档