首页 > 解决方案 > 动态制作带有文本和按钮的卡片

问题描述

我正在制作笔记应用程序。我动态地制作了带有文本和按钮的卡片(通过单击按钮创建)。但是我在更改当前卡上的文本时遇到问题。例如,我有 3 张带有自己的文字和按钮的卡片,我想更改第二张卡片上的文字,但第三张卡片上的文字正在改变。我怎么解决这个问题?

3 张带有文字和按钮的卡片

更改文本页面

过去,我尝试过制作列表来收集文本,但我不知道如何识别当前卡。

完整的 main.dart

    import 'package:flutter/material.dart';

    import './changeTextPage.dart';

    int count = 0;
    String titlecard = '';
    String textcard = '';

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

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Notes',
          theme: ThemeData(
            primarySwatch: Colors.deepPurple
          ),
          home: HomePage(title: 'Notes',),
        );
      }
    }

    class HomePage extends StatefulWidget {
      HomePage({Key key, this.title}) : super(key: key);
      final title;
      @override
      HomePageState createState() => HomePageState();
    }

    class HomePageState extends State<HomePage> {

      @override
      Widget build(BuildContext context) {
        List<Widget> cards = new List.generate(count, (int i) => new MyCard());

        return Scaffold(
          appBar: AppBar(
            title: Text('Notes'),
          ),
          body: LayoutBuilder(
            builder: (context, constraint) {
            return Column(
              children: <Widget>[
                Container(
                  height: 650.0,
                  child: new ListView(
                    children: cards,
                    scrollDirection: Axis.vertical,
                  ),
                ),
              ],
            );
          }
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {
              setState(() {
                Navigator.push(context, MaterialPageRoute(
                    builder: (context) => changeText())
                );

              });
            },
          ),
        );
      }
    }

    class MyCard extends StatefulWidget {
      @override
      myCard createState() => myCard();
    }

    class myCard extends State<MyCard> {
      int myCount = count;

      void click() {
        setState(() {
          Navigator.push(context, MaterialPageRoute(
              builder: (context) => setNewText())
          );
        });

      }


      @override
      Widget build(BuildContext context) {
        return Center(
          child: Card(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                  ListTile(
                  leading: Icon(Icons.album),
                  title: Text(titlecard),
                  subtitle: Text(textcard),
                ),
                ButtonTheme.bar( // make buttons use the appropriate styles for cards
                  child: ButtonBar(
                    children: <Widget>[
                      FlatButton(
                        child: const Text('Change Text'),
                        onPressed: click,
                      ),
                      FlatButton(
                        child: const Text('LISTEN'),
                        onPressed: () { /* ... */ },
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        );
      }


    }

    class setNewText extends StatefulWidget {
      @override
      SetNewText createState() => SetNewText();
    }

    class SetNewText extends State<setNewText> {
      final titleController = TextEditingController();
      final textController = TextEditingController();
      final formkey = GlobalKey<FormState>();

      void _submit() {
        setState(() {
          if (formkey.currentState.validate()) {
            formkey.currentState.save();
            Navigator.pop(context);
            titlecard = titleController.text;
            textcard = textController.text;

          }
        });

      }

      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
              title: Text('Change Title'),
            ),
            body: Column(
              children: <Widget>[
                Card(
                  child: Padding(
                    padding: EdgeInsets.all(2.0),
                    child: Form(
                      key: formkey,
                      child: Column(
                        children: <Widget>[
                          TextFormField(
                            controller: titleController,
                            decoration: InputDecoration(
                                labelText: 'Title'
                            ),
                            validator: (value) => value.length < 1 ? 'Invalid Title' : null,
                            onSaved: (value) => value = titleController.text,
                          ),
                          TextFormField(
                            controller: textController,
                            decoration: InputDecoration(
                                labelText: 'Text'
                            ),
                            validator: (text) => text.length < 1 ? 'Invalid Text' : null,
                            onSaved: (text) => text = textController.text,
                          )
                        ],
                      ),
                    ),
                  ),
                ),
                FlatButton(
                  textColor: Colors.deepPurple,
                  child: Text('SUBMIT'),
                  onPressed: _submit,
                ),
              ],
            )
        );
      }


    }

changeTextPage.dart

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

    class changeText extends StatefulWidget {
      @override
      ChangeText createState() => ChangeText();
    }

    class ChangeText extends State<changeText> {
      myCard s = myCard();
      final titleController = TextEditingController();
      final textController = TextEditingController();
      final formkey = GlobalKey<FormState>();

      void _submit() {
        setState(() {
          if (formkey.currentState.validate()) {
            formkey.currentState.save();
            Navigator.pop(context);
            count++;
            titlecard = titleController.text;
            textcard = textController.text;
          }
        });

      }

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Change Title'),
          ),
          body: Column(
            children: <Widget>[
              Card(
                child: Padding(
                  padding: EdgeInsets.all(2.0),
                  child: Form(
                    key: formkey,
                    child: Column(
                        children: <Widget>[
                          TextFormField(
                            controller: titleController,
                            decoration: InputDecoration(
                              labelText: 'Title'
                            ),
                            validator: (value) => value.length < 1 ? 'Invalid Title' : null,
                            onSaved: (value) => value = titleController.text,
                          ),
                          TextFormField(
                            controller: textController,
                            decoration: InputDecoration(
                                labelText: 'Text'
                            ),
                            validator: (text) => text.length < 1 ? 'Invalid Text' : null,
                            onSaved: (text) => text = textController.text,
                          )
                        ],
                      ),
                  ),
                  ),
                ),
              FlatButton(
                textColor: Colors.deepPurple,
                child: Text('SUBMIT'),
                onPressed: _submit,
              ),
            ],
          )
        );
      }


    }

标签: dartflutter

解决方案


好的,所以你碰巧犯了一些常见的错误,其中一个很关键。

  • 最重要的是不要使用全局变量!正如您对count,titlecard和所做的那样textcard
  • 有一种做法是用 PascalCase 和相应的状态来命名有状态的小部件,就像小部件一样,但前缀为下划线 ( _) 以使其私有并以State单词为后缀。

这个(或其中一个)的正确方法是有一个小部件,它是你的屏幕,带有一个编辑内容的表单,它会在提交时弹出一些带有用户值的结构:

class ChangeTextScreen extends StatefulWidget {
  _ChangeTextScreenState createState() => _ChangeTextScreenState();
}

class _ChangeTextScreenState extends State<ChangeTextScreen> {
  void _submit() {
    setState(() {
      formkey.currentState.save();

      Navigator.pop(ChangeTextResult(title: titleController.text, text: textController.text));
    });
  }

  // Rest of your code...
}

class ChangeTextResult {
  final String title;
  final String text;

  ChangeTextResult({@required this.title, @required this.text});
}

您还应该有一个将笔记存储在某种列表中的地方。你的主屏幕看起来是个不错的地方。一旦您的应用程序变得更大,请考虑使用scoped_model或 Redux 之类的。

因此,让我们在主屏幕上添加一个Note类和一个带有笔记的列表:

class Note {
    String title;
    String text;

    Note(this.title, this.text);
}

class HomePageState extends State<HomePage> {
  List<Note> _notes = [Note('Test', 'Some test note')];

  @override
  Widget build(BuildContext context) {
    ListView cards = ListView.builder(
        itemCount: _notes.length,
        itemBuilder: (context, index) => MyCard(
            title: _notes[index].title,
            text: _notes[index].text,
            onEdit: (title, text) => setState(() { // We'll get back to that later
                _notes[index].title = title;
                _notes[index].text = text;
            })
        ));
// (...)

您的MyCard小部件(下次尝试使用更好的名称)应该包含有关其内容的某种信息,最好的方法之一是将这些信息传递给它的构造函数,就像这样:

class MyCard extends StatefulWidget {
    final String title;
    final String text;
    final Function onEdit;

    MyCard({Key key, @required this.title, @required this.text, @required this.onEdit}) : super(key: key);

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

拥有此Key参数是一个好习惯。

并在您的类中使用这些参数_MyCardState(我将其重命名为_myCard):

// (...)
    children: <Widget>[
        ListTile(
        leading: Icon(Icons.album),
        title: Text(widget.title),
        subtitle: Text(widget.text),
    ),
// (...)

回到你打开你的那一刻ChangeTextScreen,你应该将结果分配Navigation.push()给一个变量。这是你的结果,你可以处理它(一旦我们检查它null,用户可能已经从这个屏幕返回,然后结果将是null)。

void click() {
    setState(() {
        final ChangeTextResult result = Navigator.push(context, MaterialPageRoute(
            builder: (context) => ChangeTextScreen())
        );

        if (result != null) {
            widget.onEdit(result.title, result.text);
        }
    });
}

你还记得那个onEdit参数吗(我在上面代码的注释中提到过)?我们在这里调用该参数。


我想就是这样。我本可以混合你的应用程序的一些概念,但我认为你无论如何都会设法理解我的观点。

我完全重写了你所有的代码。我认为您从头开始并记住这些提示会更容易。另外,尝试在 Google 上搜索一些类似的东西(比如一个简单的 Todo 应用程序)或从 Flutter.io 开始第二 部分!这应该让您对如何解决 Flutter 中的常见问题有了一个很好的了解。

此外,阅读有关 Flutter 和 Dart 的良好实践。诸如正确格式化代码之类的事情非常重要。

顺便说一句,这是迄今为止我对 Stack Overflow 最长的回答。我希望你会感激。


推荐阅读