动态制作带有文本和按钮的卡片

时间:2019-01-27 16:50:01

标签: dart flutter

我正在制作Notes应用。我用文本和按钮动态制作了卡片(单击按钮创建)。但是我在更改当前卡上的文本时遇到问题。例如,我有3张卡片,它们都有自己的文本和按钮,我想更改第二张卡片上的文本,但是文本在第三张卡片上正在更改。我该如何解决这个问题?

3 cards with texts and buttons

Change Text Page

过去,我曾尝试制作清单来收集文字,但我不知道如何识别当前卡。

完整的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,
              ),
            ],
          )
        );
      }


    }

1 个答案:

答案 0 :(得分:2)

好的,所以您碰巧会犯一些常见的错误,其中之一很关键。

  • 最重要的是请勿使用全局变量!!就像使用counttitlecardtextcard一样。
  • 有一种做法是使用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应用程序),或使用Getting started from flutter.io 第二部分!!这应该给您一个很好的主意,以帮助您解决Flutter中的常见问题。

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

顺便说一句,这是到目前为止我对Stack Overflow的最长回答。希望您会对此表示感谢。