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上设置一个侦听器并管理限制即可。但是它是最终版本,因此我也可以对其进行编辑。我该怎么办?
答案 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&¤tSize=="variable")
{
setState(() {
print("called");
size = 3;
currentSize="fixed";
});
}
}
void handleCurrentTitle(
String value, TextEditingControllerWorkaroud titleEditWidgetController) {
titleTextContent = value;
}
}
要注意的功能是descripionTextField的handleTextChange