动态制作带有文本和按钮的卡片 [英] Make cards with texts and buttons dynamically

查看:55
本文介绍了动态制作带有文本和按钮的卡片的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm making Notes app. I made cards with text and buttons dynamically (Create by clicking the button). But I have problem with Changing Text on CURRENT card. For example, I have 3 cards with own texts and buttons and I want to change text on 2nd card but text is changing on the 3rd card. How can I solve this problem?

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

更改文本页面

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

In the past, I've tried making list to collect texts, but i dont know how to identify current card.

完整的main.dart

full 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

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


    }

推荐答案

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

Okay, so you happen to make some common mistakes, one of which is critical.

  • 最重要的是不要使用全局变量! count titlecard textcard 一样.
  • 有一种做法是使用PascalCase和相应的状态来命名有状态的小部件,就像该小部件一样,但以下划线( _ )开头以使其私有并以 State 单词.
  • most importantly don't use global variables! As you do with count, titlecard and textcard.
  • there is a practice to name stateful widgets with PascalCase and corresponding states just like the widget but prefixed with an underscore (_) to make it private and suffixed by the State word.

对此(或其中之一)的正确方法是拥有一个小部件,该小部件将是您的屏幕,带有用于编辑内容的表单,并且会在提交时弹出一些带有用户值的结构:

The correct approach for this (or one of them) would be to have a widget that would be your screen with a form to edit stuff and it would pop some struct with user values on submit:

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之类的.

You should also have a place where you store your notes in some kind of a list. Your main screen looks like a good place for it. Once your app will be bigger, think about using scoped_model or Redux or something.

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

So let's add a Note class and a list with your notes to your main screen:

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 小部件(下次尝试使用更好的名称)应包含有关其内容的某种信息,最好的方法之一就是将该信息传递给其构造函数,就像那样:

Your MyCard widget (try to use better names next time) should contain some kind of information about its content, one of the best approaches would be to pass this info to its constructor, just like that:

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 重命名)中使用这些参数:

And use those parameters in your _MyCardState class (I renamed it from _myCard):

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

返回打开 ChangeTextScreen 的那一刻,应将 Navigation.push()的结果分配给变量.这是您的结果,您可以对其进行处理(一旦我们检查了 null ,用户可能已经从此屏幕返回,那么结果将是 null )./p>

Returning to the moment where you open your ChangeTextScreen, you should assign the result of Navigation.push() to a variable. This is your result, you can deal with it (once we check it for null, the user could have returned from this screen and then the result would be null).

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

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

您还记得 onEdit 参数(我在上面的代码的注释中提到过)吗?我们在这里称该参数.

Do you remember that onEdit parameter (I mentioned it in a comment in the code above)? We call that parameter here.

就是这样.我本可以将您的应用程序的一些概念混合在一起,但我认为您还是可以设法阐明我的观点的.

That's it I think. I could have mixed some concepts of your app, but I think you'll manage to get my point anyways.

我完全重写了您的所有代码.我认为重新开始并记住这些提示会更容易.另外,尝试使用Google进行类似的操作(例如简单的Todo应用程序),或执行从颤动开始.io 与第二部分!有关如何解决Flutter中的常见问题,这应该给您一个很好的主意.

I quite rewrote all of your code. I think it will be easier for you to start again from scratch and have those tips in mind. Also, try to Google some similar things (like a simple Todo application) or do Getting started from flutter.io with part two! That should give you a nice idea on how to resolve that common problem in Flutter.

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

And also, read about good practises in Flutter and Dart. Things like correctly formatting your code are really important.

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

BTW that's my longest answer on Stack Overflow so far. I hope you'll appreciate that.

这篇关于动态制作带有文本和按钮的卡片的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆