如何在 Flutter 中保存来自有状态小部件的数据?

How do I save data from a stateful widget in Flutter?

我已经实现了一个允许用户在我的 flutter 应用程序中输入的 TextField。现在,我想在按下按钮时将用户输入数据保存到一个变量中,以便以后使用。如我所见,为了实现这一点,我必须创建有状态小部件及其状态的特定实例,这样当我调用小部件以提取变量时,它不会创建新版本的小部件空文本字段。有问题的文本字段是 ProjectNameField 和 ProjectDescriptionField。 这是我的实现:

ProjectDescriptionField savedDescription = ProjectDescriptionField();
ProjectNameField savedName = ProjectNameField();
AddPage savedPage = AddPage();
_AddPageState savedPageState = _AddPageState();
List<String> image_list_encoded = [];

class AddPage extends StatefulWidget {
  final _AddPageState _addPageState = _AddPageState();
  AddPage({Key? key}) : super(key: key);

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

class _AddPageState extends State<AddPage> {
  List<Asset> images = <Asset>[];
  String _error = NOERROR;
  @override
  Widget build(BuildContext context) {
    if (images.isEmpty)
      return AppLayout(logicalHeight * 0.15);
    else
      return AppLayout(logicalHeight * 0.01);
  }

  List<Asset> getImages() {
    return images;
  }

  Widget AppLayout(double distanceFromImages) {
    return Scaffold(
      appBar: AppBar(
        leading: BackButton(color: Colors.white),
        title: Text(CREATEPROJECT),
        actions: <Widget>[
          IconButton(
            icon: Icon(
              Icons.check,
              color: Colors.white,
            ),
            onPressed: () {
              SavedData _savedData = SavedData(
                  savedName._nameFieldState.myController.value.text,
                  savedDescription
                      ._descriptionFieldState.myController.value.text,
                  savedPage._addPageState.getImages());
              print(_savedData.saved_Project_Description);
              print(_savedData.saved_Project_Name);
              print(_savedData.saved_images.toString());
              saveNewProject(_savedData);
            },
          ),
        ],
      ),
      body: Column(children: <Widget>[
        Padding(
          padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.02, 0, 0),
          child: Center(
              child: Container(
                  height: logicalHeight * 0.05,
                  width: logicalWidth * 0.9,
                  child: savedName)),
        ),
        Padding(
            padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
            child: ElevatedButton(
              child: Text(PICKIMAGES),
              onPressed: loadAssets,
            )),
        Center(
            child: Padding(
                padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
                child: getWidget())),
        Padding(
          padding: EdgeInsets.fromLTRB(
              logicalWidth * 0.05, distanceFromImages, logicalWidth * 0.05, 0),
          child: Center(child: Container(child: savedDescription)),
        ),
      ]),
    );
  }

  Widget getWidget() {
    if (images.length > 0) {
      return Container(
          height: logicalHeight * 0.4,
          width: logicalWidth * 0.8,
          child: ImagePages());
    } else {
      return Padding(
          padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.15, 0, 0),
          child: Container(child: Text(NOPICTURESSELECTED)));
    }
  }

  PageView ImagePages() {
    final PageController controller = PageController(initialPage: 0);
    List<Widget> children = [];
    images.forEach((element) {
      children.add(Padding(
          padding: EdgeInsets.fromLTRB(
              logicalWidth * 0.01, 0, logicalWidth * 0.01, 0),
          child: AssetThumb(
            asset: element,
            width: 1000,
            height: 1000,
          )));
    });
    return PageView(
      scrollDirection: Axis.horizontal,
      controller: controller,
      children: children,
    );
  }
}

class ProjectNameField extends StatefulWidget {
  ProjectNameFieldState _nameFieldState = ProjectNameFieldState();
  @override
  ProjectNameFieldState createState() {
    return _nameFieldState;
  }
}

class ProjectNameFieldState extends State<ProjectNameField> {
  final myController = TextEditingController();
  @override
  void dispose() {
    // Clean up the controller when the widget is disposed.
    myController.dispose();
    super.dispose();
  }

  Widget build(BuildContext context) {
    return TextField(
      controller: myController,
      decoration: InputDecoration(
        border: UnderlineInputBorder(),
        hintText: PROJECTNAME,
      ),
      maxLength: 30,
    );
  }
}

class ProjectDescriptionField extends StatefulWidget {
  ProjectDescriptionFieldState _descriptionFieldState =
      ProjectDescriptionFieldState();
  @override
  ProjectDescriptionFieldState createState() {
    return _descriptionFieldState;
  }
}

class ProjectDescriptionFieldState extends State<ProjectDescriptionField> {
  final myController = TextEditingController();

  @override
  void dispose() {
    // Clean up the controller when the widget is disposed.
    myController.dispose();
    super.dispose();
  }

  Widget build(BuildContext context) {
    return TextField(
      controller: myController,
      decoration: InputDecoration(
        border: UnderlineInputBorder(),
        hintText: PROJECTDESCRIPTION,
      ),
      minLines: 1,
      maxLines: 5,
      maxLength: 5000,
    );
  }
}

class SavedData {
  String saved_Project_Name = "";
  String saved_Project_Description = "";
  List<Asset> saved_images = [];

  SavedData(String saved_Project_Name, String saved_Project_Description,
      List<Asset> saved_images) {
    this.saved_Project_Name = saved_Project_Name;
    this.saved_Project_Description = saved_Project_Description;
    this.saved_images = saved_images;
  }
}

我认为问题出在这里(ProjectNameField 和 ProjectDescriptionField 本质上是相同的,只有细微差别):

class ProjectNameField extends StatefulWidget {
  ProjectNameFieldState _nameFieldState = ProjectNameFieldState();
  @override
  ProjectNameFieldState createState() {
    return _nameFieldState;
  }
}

class ProjectNameFieldState extends State<ProjectNameField> {
  final myController = TextEditingController();
  @override
  void dispose() {
    // Clean up the controller when the widget is disposed.
    myController.dispose();
    super.dispose();
  }

  Widget build(BuildContext context) {
    return TextField(
      controller: myController,
      decoration: InputDecoration(
        border: UnderlineInputBorder(),
        hintText: PROJECTNAME,
      ),
      maxLength: 30,
    );
  }
}

当我让 createState() return _nameFieldState 而不是 ProjectNameFieldState() 时,出现以下错误:

The createState function for ProjectDescriptionField returned an old or invalid state instance: ProjectDescriptionField, which is not null, violating the contract for createState.
'package:flutter/src/widgets/framework.dart':
Failed assertion: line 4681 pos 7: 'state._widget == null'

但是,当我 return ProjectNameFieldState() 时,则此代码

onPressed: () {
              SavedData _savedData = SavedData(
                  savedName._nameFieldState.myController.value.text,
                  savedDescription
                      ._descriptionFieldState.myController.value.text,
                  savedPage._addPageState.getImages());
              print(_savedData.saved_Project_Description);
              print(_savedData.saved_Project_Name);
              print(_savedData.saved_images.toString());
              saveNewProject(_savedData);
            }

不保存项目名称。 我怎样才能摆脱这个错误并保存项目名称和项目描述? 谢谢!

您可以使用 FormTextFormFiled 小部件轻松保存变量中的值并验证表单字段。

这是代码片段:

Form 小部件声明一个 GlobalKey

final _formKey = GlobalKey<FormState>();

在您要放置 TextFormFiledScaffold 正文中,添加 Form 小部件作为所有字段的父级

Form(
    key: _formKey,
    child: Column(children: <Widget>[
      Padding(
        padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.02, 0, 0),
        child: Center(
            child: Container(
                height: logicalHeight * 0.05,
                width: logicalWidth * 0.9,
                child: TextFormField(
                controller: projectNameController,
                decoration: InputDecoration(
                    hintText: PROJECTNAME,
                     border: UnderlineInputBorder()),
                     maxLength: 30
                validator: (value) {
                  if (value.isEmpty) {
                    return 'This is a required field';
                  }
                  return null;
                },
                onSaved: (value) => savedProjectName = value),
            )),
      ),
      Padding(
          padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
          child: ElevatedButton(
            child: Text(PICKIMAGES),
            onPressed: loadAssets,
          )),
      Center(
          child: Padding(
              padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
              child: getWidget())),
      Padding(
        padding: EdgeInsets.fromLTRB(
            logicalWidth * 0.05, distanceFromImages, logicalWidth * 0.05, 0),
        child: Center(child: Container(child: TextFormField(
                controller:projectDescriptionController ,
                decoration: InputDecoration(
                    hintText: PROJECTDESCRIPTION,
                     border: UnderlineInputBorder()),
                      minLines: 1,
                      maxLines: 5,
                      maxLength: 5000,
                validator: (value) {
                  if (value.isEmpty) {
                    return 'This is a required field';
                  }
                  return null;
                },
                onSaved: (value) => savedProjectDescription = value))),
      ),
    ]),
  ),

现在在您的 IconButtononPressed 中验证表单并使用保存功能将 TextFormField 值保存在您的变量中。

final form = _formKey.currentState;
          if (form.validate()) {
            form.save();
          }

现在在上面的代码中 form.validate() 将调用 validator 并且 form.save() 将调用 onSaved 属性 TextFormField

完整代码如下:

import 'package:flutter/material.dart';


class AddPage extends StatefulWidget {
  AddPage({Key? key}) : super(key: key);

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

class _AddPageState extends State<AddPage> {
  final _formKey = GlobalKey<FormState>();
  String savedProjectName;
  String savedProjectDescription;

  final projectNameController = TextEditingController();
  final projectDescriptionController = TextEditingController();
  double logicalHeight; //your logical height
  double logicalWidth; //your logical widgth


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: BackButton(color: Colors.white),
        title: Text(CREATEPROJECT),
        actions: <Widget>[
          IconButton(
            icon: Icon(
              Icons.check,
              color: Colors.white,
            ),
            onPressed: () {
              final form = _formKey.currentState;
              if (form.validate()) {
                form.save();
              } 
            },
          ),
        ],
      ),
      body:Form(
        key: _formKey,
        child: Column(children: <Widget>[
          Padding(
            padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.02, 0, 0),
            child: Center(
                child: Container(
                    height: logicalHeight * 0.05,
                    width: logicalWidth * 0.9,
                    child: TextFormField(
                    controller: projectNameController,
                    decoration: InputDecoration(
                        hintText: PROJECTNAME,
                         border: UnderlineInputBorder()),
                         maxLength: 30,
                    validator: (value) {
                      if (value.isEmpty) {
                        return 'This is a required field';
                      }
                      return null;
                    },
                    onSaved: (value) => savedProjectName = value),
                )),
          ),
          Padding(
              padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
              child: ElevatedButton(
                child: Text(PICKIMAGES),
                onPressed: loadAssets,
              )),
          Center(
              child: Padding(
                  padding: EdgeInsets.fromLTRB(0, logicalHeight * 0.01, 0, 0),
                  child: getWidget())),
          Padding(
            padding: EdgeInsets.fromLTRB(
                logicalWidth * 0.05, distanceFromImages, logicalWidth * 0.05, 0),
            child: Center(child: Container(child: TextFormField(
                    controller:projectDescriptionController ,
                    decoration: InputDecoration(
                        hintText: PROJECTDESCRIPTION,
                         border: UnderlineInputBorder()),
                          minLines: 1,
                          maxLines: 5,
                          maxLength: 5000,
                    validator: (value) {
                      if (value.isEmpty) {
                        return 'This is a required field';
                      }
                      return null;
                    },
                    onSaved: (value) => savedProjectDescription = value),)),
          ),
        ]),
      ),
    );
  }

}