Minline或TextField Flutter中的类似内容

时间:2019-02-05 11:56:48

标签: flutter

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。如果为null,则它会随着文本大小的增加/缩小而自动调整。如果我们在增加textContent时立即将其设置为常量,则其作用类似于在小部件中滚动。但是我想要的是一种名为“ minLines”的东西,即我们从默认的行数开始(例如,如果将最大行设置为常数),然后我们可以在文本增长时调整TextField的大小(例如,如果将最大行设置为null) )。同样,当文本内容超出屏幕下方的范围时,它也可以滚动。 如果允许我在运行时更改maxLines属性,我将轻松地进行处理。我只需要在textChange上设置一个侦听器并管理限制即可。但是它是最终版本,因此我也可以对其进行编辑。我该怎么办?

2 个答案:

答案 0 :(得分:1)

在最新版本的Flutter中,实际上有minLines的{​​{1}}参数,它按照您的描述运行。

答案 1 :(得分:0)

首先,我认为第一个错误是用户输入文本时不使用连续更改状态。我的解决方法是仅在我要编辑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;

  }
}

要注意的功能是descripionTextField的handleTextChange