首先,我认为第一个错误是,当用户输入文本时,没有必要不断地改变状态。我的工作是,只有当我想编辑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&¤tSize=="variable")
{
setState(() {
print("called");
size = 3;
currentSize="fixed";
});
}
}
void handleCurrentTitle(
String value, TextEditingControllerWorkaroud titleEditWidgetController) {
titleTextContent = value;
}
}
要注意的函数是handletextchange of describiontextfield