【问题标题】:How to change from Stateless Widget to Stateful Widget?如何从无状态小部件更改为有状态小部件?
【发布时间】:2019-10-06 22:38:34
【问题描述】:

我有一个使用复选框和下拉菜单的应用程序屏幕。但是,我意识到我已经在 StatelessWidget 中对其进行了编码,因此在选择选项时我无法更改状态。我如何使它工作,以便在选中复选框时它将显示为“已选中”而不是空的?

我尝试将我的代码粘贴到一个有状态的小部件中,但是总是遇到错误,因为我不完全确定哪些部分应该放在哪里。

import 'package:carve_brace_app/model/activity.dart';
import 'package:flutter/material.dart';

class DetailPage extends StatelessWidget {
  final Activity activity;
  DetailPage({Key key, this.activity}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final levelIndicator = Container(
      child: Container(
        child: LinearProgressIndicator(
            backgroundColor: Color.fromRGBO(209, 224, 224, 0.2),
            value: 2.0,
            valueColor: AlwaysStoppedAnimation(Colors.green)),
      ),
    );

    final topContentText = Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        SizedBox(height: 120.0),
        Container(
          width: 90.0,
          child: new Divider(color: Colors.green),
        ),
        SizedBox(height: 10.0),
        Text(
          activity.activityName,
          style: TextStyle(color: Colors.white, fontSize: 45.0),
        ),
        SizedBox(height: 30.0),
        Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Expanded(
                flex: 6,
                child: Padding(
                    padding: EdgeInsets.only(left: 10.0),
                    child: Text(
                      "Last Run: 3-2-19\n"+
                      "Last Avg Strain: 34%\n"+
                      "Last Run Time: 00:45:23",
                      style: TextStyle(color: Colors.white),
                    ))),
            // Expanded(flex: 1, child: newRow)
          ],
        ),
      ],
    );

    final topContent = Stack(
      children: <Widget>[
        Container(
          height: MediaQuery.of(context).size.height * 0.45,
          padding: EdgeInsets.all(40.0),
          width: MediaQuery.of(context).size.width,
            decoration: BoxDecoration(
              gradient: LinearGradient(
                begin: Alignment.centerLeft,
                end: Alignment.centerRight,
                colors: [
                  Color.fromRGBO(33, 147, 176, 100),
                  Color.fromRGBO(109, 213, 237, 100)
                ],
              ),
            ),
          child: Center(
            child: topContentText,
          ),
        ),
        Positioned(
          left: 235.0,
          top: 180.0,
          child: InkWell(
            onTap: () {
              Navigator.pop(context);
            },
            child: new CircleAvatar(
                radius: 80.0,
                backgroundImage: NetworkImage(activity.icon),
                backgroundColor: Colors.white,
              ),
          ),
        ),
        Positioned(
          left: 8.0,
          top: 60.0,
          child: InkWell(
            onTap: () {
              Navigator.pop(context);
            },
            child: Icon(Icons.arrow_back, color: Colors.white),
          ),
        )
      ],
    );

    final bottomContentText = Text(
      "Config:",
      style: TextStyle(fontSize: 18.0),
    );
    final mappedCheckbox = CheckboxListTile(
     title: Text("Mapped"),
    value: false,
    onChanged: (newValue) { },
    controlAffinity: ListTileControlAffinity.leading,  //  <-- leading Checkbox
    );
    final rtCheckBox = CheckboxListTile(
     title: Text("Real-time Tracking"),
    value: false,
    onChanged: (newValue) { },
    controlAffinity: ListTileControlAffinity.leading,  //  <-- leading Checkbox
    );
    final descriptionText = Text(
      "Description:",
      style: TextStyle(fontSize: 12.0),
    );
    final description = TextFormField(
      decoration: InputDecoration(
      hintText: 'Enter an activity description',
    ),
  );
    final scheduledFor = Text(
      "Scheduled for:",
      style: TextStyle(fontSize: 12.0),
    );
    final dropdown = new DropdownButton<String>(
  items: <String>['Now (Default)', 'B', 'C', 'D'].map((String value) {
    return new DropdownMenuItem<String>(
      value: value,
      child: new Text(value),
    );
  }).toList(),
  hint: Text("Now (Default)"),
  onChanged: (_) {},
);
    final readButton = Container(
        padding: EdgeInsets.symmetric(vertical: 16.0),
        width: 170,//MediaQuery.of(context).size.width,
        child: RaisedButton(
          onPressed: () => {},
          color: Colors.lightBlue,
          child:
              Text("Start", style: TextStyle(color: Colors.white, fontSize: 20)),
        ));
    final bottomContent = Container(
      width: MediaQuery.of(context).size.width,
      padding: EdgeInsets.all(40.0),
      child: Center(
        child: Column(
          children: <Widget>[bottomContentText, mappedCheckbox, rtCheckBox, descriptionText, description, Text("\n"), scheduledFor, dropdown, readButton],
        ),
      ),
    );

    return Scaffold(
      body: Column(
        children: <Widget>[topContent, bottomContent],
      ),
    );
  }
}

【问题讨论】:

  • 抛出哪种类型的错误?

标签: dart flutter


【解决方案1】:

将鼠标悬停在StatelessWidget 类上并使用

Android 工作室:

  • Mac选项 + 输入

  • Windowsalt + enter

Visual Studio 代码:

  • Maccmd + .

  • Windowsctrl + .


输出(您的解决方案):

这是工作代码。

class DetailPage extends StatefulWidget {
  final Activity activity;

  DetailPage({Key key, this.activity}) : super(key: key);

  @override
  _DetailPageState createState() => _DetailPageState();
}

class _DetailPageState extends State<DetailPage> {
  bool _tracking = false, _mapped = false; // you need this
  String _schedule;

  @override
  Widget build(BuildContext context) {
    final levelIndicator = Container(
      child: Container(
        child: LinearProgressIndicator(backgroundColor: Color.fromRGBO(209, 224, 224, 0.2), value: 2.0, valueColor: AlwaysStoppedAnimation(Colors.green)),
      ),
    );

    final topContentText = Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        SizedBox(height: 120.0),
        Container(
          width: 90.0,
          child: Divider(color: Colors.green),
        ),
        SizedBox(height: 10.0),
        Text(
          widget.activity.activityName,
          style: TextStyle(color: Colors.white, fontSize: 45.0),
        ),
        SizedBox(height: 30.0),
        Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Expanded(
                flex: 6,
                child: Padding(
                    padding: EdgeInsets.only(left: 10.0),
                    child: Text(
                      "Last Run: 3-2-19\n" + "Last Avg Strain: 34%\n" + "Last Run Time: 00:45:23",
                      style: TextStyle(color: Colors.white),
                    ))),
            // Expanded(flex: 1, child: newRow)
          ],
        ),
      ],
    );

    final topContent = Stack(
      children: <Widget>[
        Container(
          height: MediaQuery.of(context).size.height * 0.45,
          padding: EdgeInsets.all(40.0),
          width: MediaQuery.of(context).size.width,
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.centerLeft,
              end: Alignment.centerRight,
              colors: [Color.fromRGBO(33, 147, 176, 100), Color.fromRGBO(109, 213, 237, 100)],
            ),
          ),
          child: Center(
            child: topContentText,
          ),
        ),
        Positioned(
          left: 235.0,
          top: 180.0,
          child: InkWell(
            onTap: () {
              Navigator.pop(context);
            },
            child: CircleAvatar(
              radius: 80.0,
              backgroundColor: Colors.white,
            ),
          ),
        ),
        Positioned(
          left: 8.0,
          top: 60.0,
          child: InkWell(
            onTap: () {
              Navigator.pop(context);
            },
            child: Icon(Icons.arrow_back, color: Colors.white),
          ),
        )
      ],
    );

    final bottomContentText = Text(
      "Config:",
      style: TextStyle(fontSize: 18.0),
    );
    final mappedCheckbox = CheckboxListTile(
      title: Text("Mapped"),
      value: _mapped,
      onChanged: (newValue) => setState(() => _mapped = newValue),
      controlAffinity: ListTileControlAffinity.leading, //  <-- leading Checkbox
    );
    final rtCheckBox = CheckboxListTile(
      title: Text("Real-time Tracking"),
      value: _tracking,
      onChanged: (newValue) => setState(() => _tracking = newValue),
      controlAffinity: ListTileControlAffinity.leading, //  <-- leading Checkbox
    );
    final descriptionText = Text(
      "Description:",
      style: TextStyle(fontSize: 12.0),
    );
    final description = TextFormField(
      decoration: InputDecoration(
        hintText: 'Enter an activity description',
      ),
    );
    final scheduledFor = Text(
      "Scheduled for:",
      style: TextStyle(fontSize: 12.0),
    );
    final dropdown = DropdownButton<String>(
      value: _schedule,
      items: <String>['Now (Default)', 'B', 'C', 'D'].map((String value) {
        return DropdownMenuItem<String>(
          value: value,
          child: Text(value),
        );
      }).toList(),
      hint: Text("Now (Default)"),
      onChanged: (newValue) {
        setState(() {
          _schedule = newValue;
        });
      },
    );
    final readButton = Container(
        padding: EdgeInsets.symmetric(vertical: 16.0),
        width: 170, //MediaQuery.of(context).size.width,
        child: RaisedButton(
          onPressed: () => {},
          color: Colors.lightBlue,
          child: Text("Start", style: TextStyle(color: Colors.white, fontSize: 20)),
        ));
    final bottomContent = Container(
      width: MediaQuery.of(context).size.width,
      padding: EdgeInsets.all(40.0),
      child: Center(
        child: Column(
          children: <Widget>[bottomContentText, mappedCheckbox, rtCheckBox, descriptionText, description, Text("\n"), scheduledFor, dropdown, readButton],
        ),
      ),
    );

    return Scaffold(
      body: Column(
        children: <Widget>[topContent, bottomContent],
      ),
    );
  }
}

【讨论】:

  • mac 上的安卓工作室?
  • @neobie 第一行回答了它。我也在 Mac 上使用 Androids Studio
【解决方案2】:

对于 VSCode (Visual Studio Code) 使用 ctrl + '.'键同时将光标放在无状态小部件上以将其转换为有状态小部件。

【讨论】:

    【解决方案3】:

    如您所知,在 Flutter 中,所有 UI 组件都称为小部件。包含应用程序单个屏幕代码的小部件可以只有两种类型-

    1. 有状态
    2. 无状态

    无状态

    无状态小部件不需要可变状态,即它是不可变的。 简单来说,Stateless 小部件在应用运行期间无法更改其状态,这意味着小部件在应用运行时无法重绘。

    有状态

    有状态小部件具有可变状态,即它们是可变的,并且可以在其生命周期内多次绘制。

    它们是可以多次更改其状态的小部件,并且可以在应用运行时在屏幕上多次重绘。

    这就是启动有状态小部件所需的全部内容!

    在创建新类时,只需键入 stful 并按 Tab 键即可创建最少的有状态小部件。已经有一个无状态小部件并希望将其转换为有状态小部件?

    您可以借助一个小快捷键来完成,只需将光标放在 StatelessWidget 上,在 Android Studio 上按 Alt + Enter 或在 VS 代码上按 ctrl + . > 并点击转换为 StatefulWidget。所有的代码 sn -p 都会自动为你转换:

    【讨论】:

    • 你应该写一本关于 Stackoverflow 问题的书。这些长答案非常适合博客文章。您再次复制了原始答案并添加了一些模糊的段落。
    【解决方案4】:

    您可以使用 intellij 或 vscode 快捷方式,方法是在光标位于无状态小部件名称时按 alt + enter 或选择灯泡图标,然后选择转换为有状态小部件

    【讨论】:

      【解决方案5】:

      为 Android Studio 添加解决方案,因为我在这里没有找到。

      1. 将标记放在无状态小部件名称上:

      1. 按 Alt+Enter

      2. 选择转换为有状态小部件

      3. 根据您的要求配置有状态小部件

      【讨论】:

        猜你喜欢
        • 2021-06-11
        • 2018-09-01
        • 2021-02-16
        • 2021-09-21
        • 2020-10-19
        • 2021-12-15
        • 2022-11-12
        • 1970-01-01
        • 2021-05-19
        相关资源
        最近更新 更多