代码之家  ›  专栏  ›  技术社区  ›  Nimish Bansal

Minlines或类似的文本场颤振

  •  0
  • Nimish Bansal  · 技术社区  · 6 年前
    import 'package:flutter/foundation.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    class Todo {
      String title;
      String description;
    
      Todo(this.title, this.description);
    }
    
    class TextEditingControllerWorkaroud extends TextEditingController {
      TextEditingControllerWorkaroud({String text}) : super(text: text);
      void setTextAndPosition(String newText, int caretPosition) {
        int offset = caretPosition != null ? caretPosition : newText.length;
        value = value.copyWith(
            text: newText,
            selection: TextSelection.collapsed(offset: offset),
            composing: TextRange.empty);
      }
    }
    
    void main() {
      runApp(MaterialApp(
        title: 'Passing Data',
        home: TodosScreen(
          todos: List.generate(
            20,
            (i) => Todo(
                  'Todo $i',
                  'A description of what needs to be done for Todo $i',
                ),
          ),
        ),
      ));
    }
    
    class TodosScreen extends StatelessWidget {
      final List<Todo> todos;
    
      TodosScreen({Key key, @required this.todos}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Todos'),
          ),
          body: ListView.builder(
            itemCount: todos.length,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text(todos[index].title),
                onTap: () async {
                  Map results = await Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => DetailScreen1(todo: todos[index]),
                    ),
                  );
                  if (results["new"]!=results["old"] || results["newTitle"]!=results["oldTitle"]){
                      todos[index].description = results["new"];
                      todos[index].title = results["oldTitle"];
                      final snackBar = SnackBar(duration: Duration(milliseconds: 2000),
                          content: Text('Todo Saved Succesfully'),
                          action: SnackBarAction(
                              label: 'Undo',
                              onPressed: () {
                                  print("go back to old");
                                  todos[index].description = results["old"];
                                  todos[index].title = results["oldTitle"];
                              },
                          ),
                      );
    
                      // Find the Scaffold in the Widget tree and use it to show a SnackBar!
                      Scaffold.of(context).hideCurrentSnackBar();
                      Scaffold.of(context).showSnackBar(snackBar);
                  }
    
                },
              );
            },
          ),
        );
      }
    }
    
    class DetailScreen1 extends StatefulWidget {
      final Todo todo;
    
      DetailScreen1({Key key, @required this.todo}) : super(key: key);
    
      @override
      DetailScreen1State createState() => DetailScreen1State();
    }
    
    class DetailScreen1State extends State<DetailScreen1> {
      var descriptionTextContent = "";
      var titleTextContent = "";
    
      @override
      void initState() {
        super.initState();
        print("intialized");
        descriptionTextContent = widget.todo.description;
        titleTextContent = widget.todo.title;
      }
    
      @override
      Widget build(BuildContext context) {
    
        TextEditingControllerWorkaroud descriptionEditWidgetController =
            TextEditingControllerWorkaroud(text: descriptionTextContent);
        TextEditingControllerWorkaroud titleEditWidgetController =
        TextEditingControllerWorkaroud(text: titleTextContent);
    
        TextField descriptionEditWidget = new TextField(
          maxLines: 4,
          keyboardType: TextInputType.multiline,
          controller: descriptionEditWidgetController,
          onChanged: (value) {
            handleCurrentText(value, descriptionEditWidgetController);
          },
        );
        TextField titleEditWidget = new TextField(
          maxLines: 1,
          keyboardType: TextInputType.text,
          controller: titleEditWidgetController,
          onChanged: (value) {
            handleCurrentTitle(value, titleEditWidgetController);
          },
        );
        descriptionEditWidgetController.setTextAndPosition(
            descriptionTextContent, descriptionTextContent.length);
        titleEditWidgetController.setTextAndPosition(
            titleTextContent, titleTextContent.length);
    
    
    
        return WillPopScope(
          child: Scaffold(
            appBar: AppBar(
              title: Text(widget.todo.title),
              leading: new IconButton(
                  icon: new Icon(Icons.arrow_back),
                  onPressed: () {
                      SystemChannels.textInput.invokeMethod('TextInput.hide');
                      Navigator.pop(context, {
                        'new': descriptionTextContent,
                        "old": widget.todo.description,
                        "newTitle": titleTextContent,
                        "oldTitle": widget.todo.title,
                      },
                      );
                  },
              ),
            ),
            body: Padding(
                padding: EdgeInsets.all(16.0), child: Column(children: <Widget>[titleEditWidget, descriptionEditWidget],)),
          ),
          onWillPop: () {
            Navigator.pop(context, {
              'new': descriptionTextContent,
              "old": widget.todo.description,
              "newTitle": titleTextContent,
              "oldTitle": widget.todo.title,
            },
            );
          },
        );
      }
    
      handleCurrentText(String value,
          TextEditingControllerWorkaroud descriptionEditWidgetController) {
        setState(() {
          descriptionTextContent = value;
          print("value is " + value);
        });
      }
    
      void handleCurrentTitle(String value, TextEditingControllerWorkaroud titleEditWidgetController) {
        setState(() {
          titleTextContent = value;
        });
      }
    }
    

    上面的代码运行正常,可以直接运行。我有一个问题,就是textfield有maxlines属性。如果它为空,那么它会随着文本大小的增大/缩小而自动调整。如果我们一增加文本内容就将其设置为常量,那么它在小部件中的行为就像是可滚动的。但是我想要的是一个叫做“minlines”的东西,那就是我们从默认的no of lines开始(比如,如果我们将max lines设置为常量),然后我们可以在文本增长时调整textfield的大小(比如,如果我们将max lines设置为空)。此外,当文本内容超出屏幕下的范围时,它将变为可滚动。 如果允许我在运行时更改maxlines属性,我会很容易地处理这个问题。我只需要在textchange上设置一个监听器并管理限制。但它是最后的,所以我也可以编辑它。我能做什么?

    1 回复  |  直到 6 年前
        1
  •  0
  •   Nimish Bansal    6 年前

    首先,我认为第一个错误是,当用户输入文本时,没有必要不断地改变状态。我的工作是,只有当我想编辑MaxLines时,才能改变状态。 因此,我将maxlines设置为类中定义的变量,并在需要时立即修改该变量(当文本超过字符数时)

    import 'package:flutter/foundation.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    class Todo {
      String title;
      String description;
    
      Todo(this.title, this.description);
    }
    
    class TextEditingControllerWorkaroud extends TextEditingController {
      TextEditingControllerWorkaroud({String text}) : super(text: text);
      void setTextAndPosition(String newText, int caretPosition) {
        int offset = caretPosition != null ? caretPosition : newText.length;
        value = value.copyWith(
            text: newText,
            selection: TextSelection.collapsed(offset: offset),
            composing: TextRange.empty);
      }
    }
    
    void main() {
      runApp(MaterialApp(
        title: 'Passing Data',
        home: TodoScreen(
          todos: List.generate(
            5,
                (i) => Todo(
              'Todo $i',
              'A description of what needs to be done for Todo $i',
            ),
          ),
        ),
      ));
    }
    
    class TodoScreenState extends State<TodoScreen> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Todos'),
          ),
          floatingActionButton: new FloatingActionButton(
            onPressed: () async {
              setState(() {
                print("pressed");
                Todo newTodo = Todo("todo", "");
                widget.todos.insert(widget.todos.length, newTodo);
              });
              int index = widget.todos.length - 1;
              Map results = await Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => DetailScreen1(todo: widget.todos[index]),
                ),
              );
              if (results["new"] != results["old"] ||
                  results["newTitle"] != results["oldTitle"]) {
                widget.todos[index].description = results["new"];
                widget.todos[index].title = results["newTitle"];
                final snackBar = SnackBar(
                  duration: Duration(milliseconds: 2000),
                  content: Text('Todo Saved Succesfully'),
                  action: SnackBarAction(
                    label: 'Undo',
                    onPressed: () {
                      setState(() {
                        widget.todos[index].description = results["old"];
                        widget.todos[index].title = results["oldTitle"];
                      });
                    },
                  ),
                );
    
                // Find the Scaffold in the Widget tree and use it to show a SnackBar!
                Scaffold.of(context).hideCurrentSnackBar();
                Scaffold.of(context).showSnackBar(snackBar);
              }
            },
            child: Icon(Icons.add),
          ),
          body: ListView.builder(
            itemCount: widget.todos.length,
            itemBuilder: (context, index) {
              return Dismissible(
                background: Container(color: Colors.green[700]),
                key: Key(widget.todos[index].title),
                onDismissed: (direction) {
                  print(direction);
                  Todo currentTodo = widget.todos[index];
                  setState(() {
                    widget.todos.removeAt(index);
                  });
                  final snackBar = SnackBar(
                    duration: Duration(milliseconds: 2000),
                    content: Text('Todo Deleted Succesfully'),
                    action: SnackBarAction(
                      label: 'Undo',
                      onPressed: () async {
                        setState(() {
                          widget.todos.insert(index, currentTodo);
                        });
                      },
                    ),
                  );
    
                  // Find the Scaffold in the Widget tree and use it to show a SnackBar!
                  Scaffold.of(context).hideCurrentSnackBar();
                  Scaffold.of(context).showSnackBar(snackBar);
                },
                child: ListTile(
                  title: Text(widget.todos[index].title),
                  onTap: () async {
                    Map results = await Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) =>
                            DetailScreen1(todo: widget.todos[index]),
                      ),
                    );
                    if (results["new"] != results["old"] ||
                        results["newTitle"] != results["oldTitle"]) {
                      widget.todos[index].description = results["new"];
                      widget.todos[index].title = results["newTitle"];
                      final snackBar = SnackBar(
                        duration: Duration(milliseconds: 2000),
                        content: Text('Todo Saved Succesfully'),
                        action: SnackBarAction(
                          label: 'Undo',
                          onPressed: () {
                            setState(() {
                              widget.todos[index].description = results["old"];
                              widget.todos[index].title = results["oldTitle"];
                            });
                          },
                        ),
                      );
    
                      // Find the Scaffold in the Widget tree and use it to show a SnackBar!
                      Scaffold.of(context).hideCurrentSnackBar();
                      Scaffold.of(context).showSnackBar(snackBar);
                    }
                  },
                ),
              );
            },
          ),
        );
      }
    }
    
    class TodoScreen extends StatefulWidget {
      final List<Todo> todos;
    
      TodoScreen({Key key, @required this.todos}) : super(key: key);
    
      @override
      TodoScreenState createState() => TodoScreenState();
    }
    
    class DetailScreen1 extends StatefulWidget {
      final Todo todo;
    
      DetailScreen1({Key key, @required this.todo}) : super(key: key);
    
      @override
      DetailScreen1State createState() => DetailScreen1State();
    }
    
    class DetailScreen1State extends State<DetailScreen1> {
      var descriptionTextContent = "";
      var titleTextContent = "";
      var size = 3;
      var currentSize="fixed";
    
      @override
      void initState() {
        super.initState();
        print("intialized");
        descriptionTextContent = widget.todo.description;
        titleTextContent = widget.todo.title;
        if (descriptionTextContent.length>=100){
          size=null;
          currentSize="variable";
        }
      }
    
      @override
      Widget build(BuildContext context) {
        TextEditingControllerWorkaroud descriptionEditWidgetController =
        TextEditingControllerWorkaroud(text: descriptionTextContent);
        TextEditingControllerWorkaroud titleEditWidgetController =
        TextEditingControllerWorkaroud(text: titleTextContent);
    
        TextField descriptionEditWidget = new TextField(
          decoration: new InputDecoration(hintText: 'Description'),
          maxLines: size,
          keyboardType: TextInputType.multiline,
          controller: descriptionEditWidgetController,
          onChanged: (value) {
            handleCurrentText(value, descriptionEditWidgetController);
          },
        );
    
    
    
        TextField titleEditWidget = new TextField(
          decoration: new InputDecoration(hintText: 'Title'),
          maxLines: 1,
          keyboardType: TextInputType.text,
          controller: titleEditWidgetController,
          onChanged: (value) {
            handleCurrentTitle(value, titleEditWidgetController);
          },
        );
        descriptionEditWidgetController.setTextAndPosition(
            descriptionTextContent, descriptionTextContent.length);
        titleEditWidgetController.setTextAndPosition(
            titleTextContent, titleTextContent.length);
    
        return WillPopScope(
          child: Scaffold(
            appBar: AppBar(
              title: Text(widget.todo.title),
              leading: new IconButton(
                icon: new Icon(Icons.arrow_back),
                onPressed: () {
                  SystemChannels.textInput.invokeMethod('TextInput.hide');
                  Navigator.pop(
                    context,
                    {
                      'new': descriptionTextContent,
                      "old": widget.todo.description,
                      "newTitle": titleTextContent,
                      "oldTitle": widget.todo.title,
                    },
                  );
                },
              ),
            ),
            body: Padding(
                padding: EdgeInsets.all(16.0),
                child: Column(
                  children: <Widget>[titleEditWidget, descriptionEditWidget],
                )),
          ),
          onWillPop: () {
            Navigator.pop(
              context,
              {
                'new': descriptionTextContent,
                "old": widget.todo.description,
                "newTitle": titleTextContent,
                "oldTitle": widget.todo.title,
              },
            );
          },
        );
      }
    
      handleCurrentText(String value,
          TextEditingControllerWorkaroud descriptionEditWidgetController) {
        descriptionTextContent = value;
        if (descriptionTextContent.length>100 && currentSize=="fixed"){
          setState(() {
            print("called");
            size = null;
            currentSize="variable";
          });
        }
    
        else if (descriptionTextContent.length<=100&&currentSize=="variable")
        {
          setState(() {
            print("called");
            size = 3;
            currentSize="fixed";
          });
        }
      }
    
      void handleCurrentTitle(
          String value, TextEditingControllerWorkaroud titleEditWidgetController) {
        titleTextContent = value;
    
      }
    }
    

    要注意的函数是handletextchange of describiontextfield