【问题标题】:Implement setstat and bind variables实现 setstat 和绑定变量
【发布时间】:2026-02-05 13:05:01
【问题描述】:

我正在尝试从另一个 dart 文件 ("int myId" 和 "String myMenu") 获取并显示 2 个变量值,这些变量随每个“onTap”小部件更新,我的代码有效,但前提是我做一个“热重载”,我认为我需要在某个地方放置一个“setstate”,但我很难实现它。

我认为问题出在这里,我的小部件文本返回“null”给我,但如果我点击菜单按钮并执行“热重载”,就可以了。

displayText.dart

import 'package:flutter/material.dart';
import './menu.dart';

class display extends StatefulWidget {
  int myId;
  String myMenu;
  display(this.myId, this.myMenu);
  @override
  _displayState createState() => _displayState();
}

class _displayState extends State<display> {
  Future myVarUsed() async {
    //Each press on the button return the value
    setState(() {
      print('myIdDsiplay: ${widget.myId}'); // null
      print('myMenuDisplay : ${widget.myMenu}'); // null
    });
  }

  @override
  void initState() {
    super.initState();
    myVarUsed();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.blue,
      height: 250,
      width: 250,
      child: Row(
        children: [
          Text('My ID is : ${widget.myId}'),
          Text('My menu is : ${widget.myMenu}'),
        ],
      ),
    );
  }
}

此文件包含滚动条内的菜单,每个按钮返回 ID 和(按钮的)名称,并将其存储在我要传递的 2 个变量(“int myId”和“String myMenu”)中。

menu.dart

import 'package:flutter/material.dart';
import './mylist.dart';
import './displayText.dart';

class Menu extends StatefulWidget {
  static int myId;
  static String myMenu;
  @override
  _MenuState createState() => _MenuState();
}

class _MenuState extends State<Menu> {
  Container scrollList() {
    final PageController controller = PageController(initialPage: 1, keepPage: true, viewportFraction: 0.35);

    return Container(
      color: Colors.red,
      height: 90,
      child: PageView.builder(
        scrollDirection: Axis.horizontal,
        controller: controller,
        itemCount: listdata.length,
        physics: BouncingScrollPhysics(),
        itemBuilder: (BuildContext context, int index) {
          return Container(
            child: gestureDetector_Ontap(index),
          );
        },
      ),
    );
  }

  GestureDetector gestureDetector_Ontap(int index) {
    return GestureDetector(
      onTap: () {
        Menu.myId = listdata[index].id;
        Menu.myMenu = listdata[index].menuObj;
        display(Menu.myId, Menu.myMenu);

        print('myIDMenu ${Menu.myId}');
        print('myMenuMenu ${Menu.myMenu}');
      },

      child: Container(
        alignment: AlignmentDirectional.center,
        child: Text(
          '${listdata[index].menuObj}',
        ),
      ),
    );
  }

  Widget build(BuildContext context) {
    return Container(
      child: scrollList(),
    );
  }
}

此文件包含我的列表和他的班级

mylist.dart

class listModel {
  int id;
  String menuObj;
  listModel(this.id, this.menuObj);
}

List listdata = [
  listModel(0, 'Menu01'),
  listModel(1, 'Menu02'),
  listModel(2, 'Menu03'),
  listModel(3, 'Menu04'),
  listModel(4, 'Menu05')
];

还有容器

ma​​in.dart

import 'package:flutter/material.dart';
import './menu.dart';
import './displayText.dart';
import './mylist.dart';

void main() {
  runApp(MyHomePage());
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: Column(
            children: <Widget>[
              Menu(),
              display(Menu.myId, Menu.myMenu),
            ],
          ),
        ),
      ),
    );
  }
}

【问题讨论】:

    标签: flutter dart setstate


    【解决方案1】:

    问题

    你是这样定义Menu的:

    class Menu extends StatefulWidget {
      static int myId;
      static String myMenu;
    
      @override
      _MenuState createState() => _MenuState();
    }
    

    当您的应用启动时,myIdmyMenu 是未初始化的变量,因此它们被隐式设置为 null。


    _MyHomePageState,你调用

    display(Menu.myId, Menu.myMenu)
    

    由于您还没有初始化 Menu.myIdMenu.myMenu,所以它们仍然为空。


    当您点击 GestureDetector 时,您会以这种方式初始化 Menu.myIdMenu.myMenu

    Menu.myId = listdata[index].id;
    Menu.myMenu = listdata[index].menuObj;
    display(Menu.myId, Menu.myMenu);
    
    print('myIDMenu ${Menu.myId}');
    print('myMenuMenu ${Menu.myMenu}');
    

    现在,Menu.myIdMenu.myMenu 被定义为非空值。不过这个不会更新Container的display(Menu.myId, Menu.myMenu),所以还是null,需要自己更新。

    解决办法

    我已经通过代码添加了cmets,指点一个更好的方法:

    import 'package:flutter/material.dart';
    
    // Avoid displaying the warning "Name types using UpperCamelCase."
    class Display extends StatefulWidget {
      // Make these fields final and the constructor const
      final int myId;
      final String myMenu;
      
      const Display(this.myId, this.myMenu);
      
      @override
      _DisplayState createState() => _DisplayState();
    }
    
    // Avoid displaying the warning "Name types using UpperCamelCase."
    class _DisplayState extends State<Display> {
      // You don't need this Future nor this initState
      // 
      // Future myVarUsed() async {
      //   setState(() {
      //     print('myIdDsiplay: ${widget.myId}'); // null
      //     print('myMenuDisplay : ${widget.myMenu}'); // null
      //   });
      //  }
      // 
      // @override
      // void initState() {
      //   super.initState();
      //   myVarUsed();
      // }
    
      @override
      Widget build(BuildContext context) {
        return Container(
          color: Colors.blue,
          height: 250,
          width: 250,
          child: Row(
            children: [
              Text('My ID is : ${widget.myId}'),
              Text('My menu is : ${widget.myMenu}'),
            ],
          ),
        );
      }
    }
    
    class Menu extends StatefulWidget {
      // Avoid using mutable static fields
      // static int myId;
      // static String myMenu;
      
      // To simplify, you can add a onChanged callback to 
      // be triggered whenever you change `myId` and `myMenu`
      final void Function(int myId, String myMenu) onChanged;
      
      const Menu({this.onChanged});
      
      @override
      _MenuState createState() => _MenuState();
    }
    
    class _MenuState extends State<Menu> {
      Container scrollList() {
        final PageController controller = PageController(initialPage: 1, keepPage: true, viewportFraction: 0.35);
    
        return Container(
          color: Colors.red,
          height: 90,
          child: PageView.builder(
            scrollDirection: Axis.horizontal,
            controller: controller,
            itemCount: listdata.length,
            physics: BouncingScrollPhysics(),
            itemBuilder: (BuildContext context, int index) {
              return Container(
                child: gestureDetectorOntap(index),
              );
            },
          ),
        );
      }
    
      // Avoid displaying the warning "Name non-constant identifiers using lowerCamelCase."
      GestureDetector gestureDetectorOntap(int index) {
        return GestureDetector(
          onTap: () {
            // Make these local variables
            int myId = listdata[index].id;
            String myMenu = listdata[index].menuObj;
            
            // Call the `onChanged` callback
            widget.onChanged(myId, myMenu);
            
            // This widget is being thrown away
            // display(Menu.myId, Menu.myMenu);
    
            print('myIDMenu $myId');
            print('myMenuMenu $myMenu');
          },
    
          child: Container(
            alignment: AlignmentDirectional.center,
            child: Text(
              '${listdata[index].menuObj}',
            ),
          ),
        );
      }
    
      Widget build(BuildContext context) {
        return Container(
          child: scrollList(),
        );
      }
    }
    
    // Avoid the warning "Name types using UpperCamelCase."
    class ListModel {
      // You can make these fields final and the constructor const
      final int id;
      final String menuObj;
      
      const ListModel(this.id, this.menuObj);
    }
    
    // You can make this list const to avoid modifying it unintentionally later
    const List<ListModel> listdata = [
      ListModel(0, 'Menu01'),
      ListModel(1, 'Menu02'),
      ListModel(2, 'Menu03'),
      ListModel(3, 'Menu04'),
      ListModel(4, 'Menu05')
    ];
    
    void main() {
      runApp(MyHomePage());
    }
    
    class MyHomePage extends StatefulWidget {
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      // Create fields to store the current `myId` and current `myMenu`
      int myId;
      String myMenu;
      
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            body: Container(
              child: Column(
                children: <Widget>[
                  // Add the `onChanged` callback here, updating this widget state
                  Menu(
                    onChanged: (newMyId, newMyMenu) {
                      setState(() {
                        myId = newMyId;
                        myMenu = newMyMenu;
                      });
                    }
                  ),
                  // Access the current values here
                  Display(myId, myMenu),
                ],
              ),
            ),
          ),
        );
      }
    }
    

    【讨论】:

    • 非常感谢您的代码和 Enzo 的这些很好的解释。