【问题标题】:Updating a Stateless Widget from a Stateful Widget从有状态小部件更新无状态小部件
【发布时间】:2021-06-11 08:42:49
【问题描述】:

Main.dart:

import 'package:flutter/material.dart';
import 'package:mypackage/widgets/home_widget.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "My App",
      theme: ThemeData(primarySwatch: Colors.blueGrey),
      home: Home(),
    );
  }
}

Home.dart:

import 'package:flutter/material.dart';
import 'package:mypackage/widgets/secondary_page.dart';
import 'package:mypackage/widgets/home_view.dart';
import 'package:mypackage/widgets/settings_page.dart';

class Home extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _HomeState();
  }
}

class _HomeState extends State<Home> {
  int currentPageIndex = 0;
  final List<Widget> pageWidgets = [HomeView(), SecondaryPage(), SettingsPage()];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("My App"),
          centerTitle: true,
        ),
        body: pageWidgets[currentPageIndex],
        bottomNavigationBar: BottomNavigationBar(
          onTap: onTabTapped,
          currentIndex: currentPageIndex,
          items: [
            BottomNavigationBarItem(
                icon: new Icon(Icons.home), label: "Tab One"),
            BottomNavigationBarItem(
                icon: new Icon(Icons.account_balance), label: "Tab Two"),
            BottomNavigationBarItem(
                icon: new Icon(Icons.settings), label: "Tab Three"),
          ],
        ),
        floatingActionButton: FloatingActionButton(onPressed: () {
          // Update data from here
          HomeView().updateDisplay();
        }));
  }

  // Changes the index when the tapped page has loaded
  void onTabTapped(int index) {
    setState(() {
      currentPageIndex = index;
    });
  }
}

HomeView.dart:

import 'package:flutter/material.dart';
import 'package:mypackage/models/mydisplay.dart';
import 'package:mypackage/network/mydata.dart';

class HomeView extends StatelessWidget {
  final List<MyDisplay> dataList = [
    new MyDisplay("Sample Display",
        "SampleFile", 0),
  ];

  Future<void> updateDisplay() async {
    MyData myData = new MyData();

    dataList.forEach((d) async {
      d.changePercentage = await myData
          .getDataPercentage("assets/data/" + d.fileName + ".xml");

      print(d.dataName +
          " change percentage: " +
          d.changePercentage.toString() +
          "%");
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: new ListView.builder(
          itemCount: dataList.length,
          itemBuilder: (BuildContext context, int index) =>
              buildCard(context, index)),
    );
  }

  Widget buildCard(BuildContext context, int index) {
    return Container(
        child: Card(
            color: Colors.teal.shade200,
            child: Padding(
                padding: const EdgeInsets.all(25),
                child: Column(children: <Widget>[
                  Text(dataList[index].dataName,
                      style: new TextStyle(
                          fontSize: 20, fontWeight: FontWeight.bold)),
                  Text(dataList[index].changePercentage.toString() + "%")
                ]))));
  }
}

当我从 floatingActionButton 调用 updateDisplay() 方法时,它按预期工作,数据被打印到控制台,但是它似乎并没有更新实际的 UI,卡片的内容,它们只是保持在他们的初始设定值。

澄清一下:如何从有状态小部件更新列表视图内(无状态小部件内)卡片的内容?

很抱歉,如果这是一个重复的问题,我已经查看并继续寻找问题的答案,但我根本无法让它工作。

【问题讨论】:

    标签: flutter dart flutter-state


    【解决方案1】:

    虽然我理解您希望实现的目标,但我相信这将与“Flutter 方式”背道而驰。您可以使用一个键来实现您想要的结果,但我相信将状态提升到小部件树会更容易和更惯用。您可以在文档here 中了解它。

    因此,在您的情况下,您可以将其更改为:

    class Home extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return _HomeState();
      }
    }
    
    class _HomeState extends State<Home> {
      int currentPageIndex = 0;
      // Added this line
      List<MyDisplay> dataList = [
        new MyDisplay("Sample Display",
            "SampleFile", 0),
      ];
    
      final List<Widget> pageWidgets; 
    
      // moved the update function from HomeView to here
      Future<void> updateDisplay() async {
        MyData myData = new MyData();
    
        dataList.forEach((d) async {
          d.changePercentage = await myData
              .getDataPercentage("assets/data/" + d.fileName + ".xml");
    
          print(d.dataName +
              " change percentage: " +
              d.changePercentage.toString() +
              "%");
        });
        setState((){});
      }
    
      @override
      Widget build(BuildContext context) {
        pageWidgets = [HomeView(dataList: dataList), SecondaryPage(), SettingsPage()]
    
        return Scaffold(
            appBar: AppBar(
              title: Text("My App"),
              centerTitle: true,
            ),
            body: pageWidgets[currentPageIndex],
            bottomNavigationBar: BottomNavigationBar(
              onTap: onTabTapped,
              currentIndex: currentPageIndex,
              items: [
                BottomNavigationBarItem(
                    icon: new Icon(Icons.home), label: "Tab One"),
                BottomNavigationBarItem(
                    icon: new Icon(Icons.account_balance), label: "Tab Two"),
                BottomNavigationBarItem(
                    icon: new Icon(Icons.settings), label: "Tab Three"),
              ],
            ),
            floatingActionButton: FloatingActionButton(onPressed: () {
              updateDisplay();
            }));
      }
    
      // Changes the index when the tapped page has loaded
      void onTabTapped(int index) {
        setState(() {
          currentPageIndex = index;
        });
      }
    }
    

    然后为您的 HomeView 小部件:

    class HomeView extends StatelessWidget {
      HomeView({this.dataList});
    
      final List<MyDisplay> dataList;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: new ListView.builder(
              itemCount: dataList.length,
              itemBuilder: (BuildContext context, int index) =>
                  buildCard(context, index)),
        );
      }
    
      Widget buildCard(BuildContext context, int index) {
        return Container(
            child: Card(
                color: Colors.teal.shade200,
                child: Padding(
                    padding: const EdgeInsets.all(25),
                    child: Column(children: <Widget>[
                      Text(dataList[index].dataName,
                          style: new TextStyle(
                              fontSize: 20, fontWeight: FontWeight.bold)),
                      Text(dataList[index].changePercentage.toString() + "%")
                    ],
                  ),
                ),
              ),
            );
      }
    }
    

    我没有测试这是否有效。我可能会晚一点。

    【讨论】:

      【解决方案2】:

      您需要使用有状态小部件,因为您无法更新无状态小部件。检查this 链接以获取更多信息。下一个问题,调用它,可以使用globalKeys 修复。唯一奇怪的是您必须将 Home 有状态小部件和 HomeView 有状态小部件保存在同一个 dart 文件中。 .试试这个代码让它工作。

      首页视图

      import 'package:flutter/material.dart';
      import 'package:mypackage/widgets/secondary_page.dart';
      import 'package:mypackage/widgets/settings_page.dart';
      import 'package:mypackage/models/mydisplay.dart';
      import 'package:mypackage/network/mydata.dart';
      
      class Home extends StatefulWidget {
        @override
        State<StatefulWidget> createState() {
          return _HomeState();
        }
      }
      
      class _HomeState extends State<Home> {
        int currentPageIndex = 0;
        final List<Widget> pageWidgets = [HomeView(), SecondaryPage(), SettingsPage()];
        GlobalKey<_HomeViewState> _myKey = GlobalKey();// We declare a key here
        @override
        Widget build(BuildContext context) {
          return Scaffold(
              appBar: AppBar(
                title: Text("My App"),
                centerTitle: true,
              ),
              body: pageWidgets[currentPageIndex],
              bottomNavigationBar: BottomNavigationBar(
                onTap: onTabTapped,
                currentIndex: currentPageIndex,
                items: [
                  BottomNavigationBarItem(
                      icon: new Icon(Icons.home), label: "Tab One"),
                  BottomNavigationBarItem(
                      icon: new Icon(Icons.account_balance), label: "Tab Two"),
                  BottomNavigationBarItem(
                      icon: new Icon(Icons.settings), label: "Tab Three"),
                ],
              ),
              floatingActionButton: FloatingActionButton(onPressed: () {
                // Update data from here
                _myKey.currentState.updateDisplay();//This is how we call the function
              }));
        }
      
        // Changes the index when the tapped page has loaded
        void onTabTapped(int index) {
          setState(() {
            currentPageIndex = index;
          });
        }
      }
      
      class HomeView extends StatefulWidget {
        Function updateDisplay;
        HomeView({Key key}): super(key: key);//This key is what we use
        @override
        _HomeViewState createState() => _HomeViewState();
      }
      
      class _HomeViewState extends State<HomeView> {
        final List<MyDisplay> dataList = [
          new MyDisplay("Sample Display",
              "SampleFile", 0),
        ];
      
      @override
        void initState(){
          super.initState();
          widget.updateDisplay = () async {
            MyData myData = new MyData();
      
            dataList.forEach((d) async {
              d.changePercentage = await myData
                  .getDataPercentage("assets/data/" + d.fileName + ".xml");
              print(d.dataName +
                  " change percentage: " +
                  d.changePercentage.toString() +
                  "%");
            });
            setState((){});//This line rebuilds the scaffold
          };
        }
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            body: new ListView.builder(
                itemCount: dataList.length,
                itemBuilder: (BuildContext context, int index) =>
                    buildCard(context, index)),
          );
        }
      
        Widget buildCard(BuildContext context, int index) {
          return Container(
              child: Card(
                  color: Colors.teal.shade200,
                  child: Padding(
                      padding: const EdgeInsets.all(25),
                      child: Column(children: <Widget>[
                        Text(dataList[index].dataName,
                            style: new TextStyle(
                                fontSize: 20, fontWeight: FontWeight.bold)),
                        Text(dataList[index].changePercentage.toString() + "%")
                      ]))));
        }
      }
      
      

      我们使用 setState((){}) 来重建小部件。只要记住一个想法。无论您在哪里使用 updateDisplay 方法,都不要让它在重建后循环运行。例如。您在孩子内部的未来构建器中使用它。这样它会不断重建

      编辑

      我们向 HomeView 添加了一个密钥,现在我们可以使用该密钥,例如 _myKey.currentState.yourFunction。希望我有所帮助

      【讨论】:

      • 将 HomeView 转换为有状态小部件后,我无法再从 home_widget.dart 调用 updateDisplay()
      • 哦。让我想办法解决这个问题。我会添加另一条评论,让你知道我什么时候完成了
      • @AdamBrodin 我已经做到了。请检查它是否有效。我正在添加解释
      • 它不起作用,说“方法调用在 null 上调用”
      • 哦。我认为 initState() 在这样调用时不会运行。还有另一种使用键的方法,但它很复杂,所以我会在弄清楚时发布它
      【解决方案3】:

      您需要将您的HomeView 转换为StatefulWidget

      之后你需要将updateDisplay修改成这样:

      Future<void> updateDisplay() async {
          MyData myData = new MyData();
      
          dataList.forEach((d) async {
            d.changePercentage = await myData
                .getDataPercentage("assets/data/" + d.fileName + ".xml");
      
            print(d.dataName +
                " change percentage: " +
                d.changePercentage.toString() +
                "%");
          });
          
          setState(() {});
        }
      

      通过调用setState (() {});,您可以调用StatefulWidget 中的构建方法。

      编辑

      像这样更改您的HomeView

      class HomeView extends StatefulWidget {
        final List<MyDisplay> dataList = [
          new MyDisplay("Sample Display",
              "SampleFile", 0),
        ];
      
        Future<void> updateDisplay() async {
          MyData myData = new MyData();
      
          dataList.forEach((d) async {
            d.changePercentage = await myData
                .getDataPercentage("assets/data/" + d.fileName + ".xml");
      
            print(d.dataName +
                " change percentage: " +
                d.changePercentage.toString() +
                "%");
          });
        }
        @override
        _HomeViewState createState() => _HomeViewState();
      }
      
      class _HomeViewState extends State<HomeView> {
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            body: new ListView.builder(
                itemCount: widget.dataList.length,
                itemBuilder: (BuildContext context, int index) =>
                    buildCard(context, index)),
          );
        }
      
        Widget buildCard(BuildContext context, int index) {
          return Container(
              child: Card(
                  color: Colors.teal.shade200,
                  child: Padding(
                      padding: const EdgeInsets.all(25),
                      child: Column(children: <Widget>[
                        Text(widget.dataList[index].dataName,
                            style: new TextStyle(
                                fontSize: 20, fontWeight: FontWeight.bold)),
                        Text(widget.dataList[index].changePercentage.toString() + "%")
                      ]))));
        }
      }
      

      你的Home 是这样的:

      class Home extends StatefulWidget {
        @override
        State<StatefulWidget> createState() {
          return _HomeState();
        }
      }
      
      class _HomeState extends State<Home> {
        int currentPageIndex = 0;
        HomeView _homeView = HomeView();
        List<Widget> pageWidgets = [_homeView, SecondaryPage(), SettingsPage()];
      
        @override
        Widget build(BuildContext context) {
          return Scaffold(
              appBar: AppBar(
                title: Text("My App"),
                centerTitle: true,
              ),
              body: pageWidgets[currentPageIndex],
              bottomNavigationBar: BottomNavigationBar(
                onTap: onTabTapped,
                currentIndex: currentPageIndex,
                items: [
                  BottomNavigationBarItem(
                      icon: new Icon(Icons.home), label: "Tab One"),
                  BottomNavigationBarItem(
                      icon: new Icon(Icons.account_balance), label: "Tab Two"),
                  BottomNavigationBarItem(
                      icon: new Icon(Icons.settings), label: "Tab Three"),
                ],
              ),
              floatingActionButton: FloatingActionButton(onPressed: () {
                // Update data from here
                _homeView.updateDisplay();
              }));
        }
      
        // Changes the index when the tapped page has loaded
        void onTabTapped(int index) {
          setState(() {
            currentPageIndex = index;
          });
        }
      }
      

      【讨论】:

      • 将 HomeView 转换为有状态小部件后,我无法再从 home_widget.dart 调用 updateDisplay()
      • 嗯,我认为你需要静态方法,检查我的编辑!
      • 静态不起作用。我们需要使用小部件。就像我在回答中使用的那样
      • @SiddharthAgrawal 这是真的。我写了一个新的解决方案!
      • 那是我的解决方案。你不能复制别人的解决方案。顺便说一句,根据亚当布罗丁的说法,它不起作用。我有一个使用密钥的新解决方案
      【解决方案4】:

      如下更新你的 home.dart。

      import 'package:flutter/material.dart';
      import 'package:mypackage/widgets/secondary_page.dart';
      import 'package:mypackage/widgets/home_view.dart';
      import 'package:mypackage/widgets/settings_page.dart';
      
      class Home extends StatefulWidget {
        @override
        State<StatefulWidget> createState() {
          return _HomeState();
        }
      }
      
      class _HomeState extends State<Home> {
        int currentPageIndex = 0;
        Widget _homeView = HomeView();
        final List<Widget> pageWidgets = [_homeView, SecondaryPage(), SettingsPage()];
      
        @override
        Widget build(BuildContext context) {
          return Scaffold(
              appBar: AppBar(
                title: Text("My App"),
                centerTitle: true,
              ),
              body: pageWidgets[currentPageIndex],
              bottomNavigationBar: BottomNavigationBar(
                onTap: onTabTapped,
                currentIndex: currentPageIndex,
                items: [
                  BottomNavigationBarItem(
                      icon: new Icon(Icons.home), label: "Tab One"),
                  BottomNavigationBarItem(
                      icon: new Icon(Icons.account_balance), label: "Tab Two"),
                  BottomNavigationBarItem(
                      icon: new Icon(Icons.settings), label: "Tab Three"),
                ],
              ),
              floatingActionButton: FloatingActionButton(onPressed: () {
                // Update data from here
                _homeView.updateDisplay();
              }));
        }
      
        // Changes the index when the tapped page has loaded
        void onTabTapped(int index) {
          setState(() {
            currentPageIndex = index;
          });
        }
      }
      

      home_view.dart如下

      import 'package:flutter/material.dart';
      import 'package:mypackage/models/mydisplay.dart';
      import 'package:mypackage/network/mydata.dart';
      
      class HomeView extends StatelessWidget {
        final List<MyDisplay> dataList = [
          new MyDisplay("Sample Display",
              "SampleFile", 0),
        ];
      
        Future<void> updateDisplay() async {
          MyData myData = new MyData();
      
          dataList.forEach((d) async {
            d.changePercentage = await myData
                .getDataPercentage("assets/data/" + d.fileName + ".xml");
      
            print(d.dataName +
                " change percentage: " +
                d.changePercentage.toString() +
                "%");
          });
           setState((){});
        }
      
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            body: new ListView.builder(
                itemCount: dataList.length,
                itemBuilder: (BuildContext context, int index) =>
                    buildCard(context, index)),
          );
        }
      
        Widget buildCard(BuildContext context, int index) {
          return Container(
              child: Card(
                  color: Colors.teal.shade200,
                  child: Padding(
                      padding: const EdgeInsets.all(25),
                      child: Column(children: <Widget>[
                        Text(dataList[index].dataName,
                            style: new TextStyle(
                                fontSize: 20, fontWeight: FontWeight.bold)),
                        Text(dataList[index].changePercentage.toString() + "%")
                      ]))));
        }
      }
      

      【讨论】:

      • 您在 pageWidgets 和 floatingActionButton 中创建了两个 HomeView 实例。
      【解决方案5】:

      你需要在HomeView().updateDisplay();之后调用setState(() {});

      【讨论】:

      • 现在试了,还是不更新界面
      猜你喜欢
      • 2018-09-01
      • 2022-11-12
      • 2019-10-06
      • 2020-10-19
      • 2021-12-15
      • 2021-02-16
      • 2021-07-25
      • 1970-01-01
      • 2021-08-29
      相关资源
      最近更新 更多