在 ListView 上构建期间调用 setState() 或 markNeedsBuild()
setState() or markNeedsBuild() called during build on ListView
所以我正在尝试重构我的 listView 逻辑。基本上我的 ListView 已经变得笨重 UI 逻辑,所以我决定,为什么不将 UI 逻辑的某些部分移动到另一个 class
这是我的代码
ListPage.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:sample_flutter_works/ListTextArea.dart';
import 'package:sample_flutter_works/Model.dart';
import 'dart:convert';
import 'package:sample_flutter_works/RefreshTableContainer.dart';
class ListPage extends StatefulWidget {
@override
MyListPage createState() => MyListPage();
}
class MyListPage extends State<ListPage> {
MessageList messageList;
List<int> viewTimeInfo;
ScrollController _controller;
_scrollListener() {
}
@override
void initState() {
super.initState();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
_controller = ScrollController();
_controller.addListener(_scrollListener);
loadMessages(
completionBlock: (dataSet) => {
setState(() {
messageList = dataSet;
})
});
}
void loadMessages({completionBlock}) async {
var jsonString = await rootBundle.loadString('assets/Chat.json');
final jsonResponse = json.decode(jsonString);
if (jsonResponse != null) {
completionBlock(MessageList.fromJSON(jsonResponse));
} else {
completionBlock(null);
}
}
Widget listLayout() {
return ListView.separated(
padding: const EdgeInsets.all(8.0),
itemCount: (messageList != null && messageList.msgList != null)
? messageList.msgList.length
: 0,
separatorBuilder: (context, index) => Divider(
color: Colors.black,
height: 4.0,
),
itemBuilder: (BuildContext context, int index) {
var msgValToSend =
(messageList != null && messageList.msgList != null)
? messageList.msgList[index]
: null;
return Stack(
children: <Widget>[
IntrinsicHeight(
child: Row(
children: <Widget>[
getTheImageLayout(msgValToSend),
new ListTextArea(
msg: msgValToSend,
didTapOnTextArea: tappedOnTextArea,
visibilityCheck: checkForVisibility)
],
),
)
],
);
});
}
tappedOnTextArea(Message msg) {
var viewedInfo = this.viewTimeInfo;
if (viewedInfo != null) {
var indexOfTappedElement = viewedInfo.indexOf(msg.messageID);
if (indexOfTappedElement != null && indexOfTappedElement != -1) {
viewedInfo.removeAt(indexOfTappedElement);
} else {
viewedInfo.add(msg.messageID);
}
} else {
viewedInfo = [msg.messageID];
}
setState(() {
viewTimeInfo = viewedInfo;
});
}
checkForVisibility(bool _visible, Message msg) {
if (msg != null && this.viewTimeInfo != null) {
var checkForIndex = this.viewTimeInfo.indexOf(msg.messageID);
if (checkForIndex != null && checkForIndex != -1) {
_visible = true;
}
}
}
Widget getTheImageLayout(Message msg) {
return Expanded(
flex: 2,
child: Align(
alignment: Alignment.topLeft,
child: Padding(
padding: EdgeInsets.fromLTRB(5, 2.5, 0, 0),
child: Container(
color: Colors.red,
height: 50,
child: Row(
children: <Widget>[
userImageView(msg),
],
)),
)));
}
Widget userImageView(Message msg) {
return Expanded(
flex: 8,
child: Align(
alignment: Alignment.centerLeft,
child: Container(
width: 40.0,
height: 40.0,
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.green),
child: ClipOval(
child: Image.network(
(msg.msgUser.userPicUrl != null)
? msg.msgUser.userPicUrl
: 'https://picsum.photos/250?image=9',
fit: BoxFit.fill,
),
))));
}
Future<void> refreshTheChatTable() async {
print(" This is where the logic of pulll 2 refresh must be written ");
loadMessages(
completionBlock: (dataSet) => {
setState(() {
messageList = dataSet;
})
});
}
@override
Widget build(BuildContext context) {
return new RefreshTableContainer(
listLayout: listLayout(),
pull2RefreshAction: refreshTheChatTable,
);
}
}
ListTextArea.dart
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:sample_flutter_works/Model.dart';
class ListTextArea extends StatelessWidget {
Message msg;
Function didTapOnTextArea;
Function visibilityCheck;
ListTextArea({
this.msg,
this.didTapOnTextArea,
this.visibilityCheck
});
@override
Widget build(BuildContext context) {
return Expanded(
flex: 8,
child: GestureDetector(
onTap: didTapOnTextArea(msg),
child: Padding(
padding: EdgeInsets.fromLTRB(0, 2.5, 10, 0),
child: Column(
children: getChildWidgetArray(msg) ,
),
),
));
}
List<Widget> getChildWidgetArray(Message msg) {
var elementalArray = [
Align(
alignment: Alignment.topLeft,
child: Text(
(msg != null) ? msg.msgContent.content : "Data Loading",
style: TextStyle(
background: Paint()..color = Colors.orange,
),
),
),
Spacer(), // Defaults to a flex of one.
Align(
alignment: Alignment.bottomRight,
child: Text(
'Date of sending',
textDirection: TextDirection.rtl,
style: TextStyle(
background: Paint()..color = Colors.blue,
),
),
)
];
var _visible = false;
visibilityCheck(_visible,msg);
var timeInfo = AnimatedOpacity (
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Align(
child: _visible ? (Align(alignment: Alignment.topLeft,child:Column(children: <Widget>[Text("Last Read :" + (msg.msgTimeInfo.lastReadInfo)),
Text("Delievered :" + (msg.msgTimeInfo.deliveredInfo))],))): null));
elementalArray.add(timeInfo);
return elementalArray;
}
}
错误如下:
我正在尝试做的(或者当整个代码在 ListPage.dart 中时我已经做过)是在 listView 中动态计算的单元格,每个单元格响应在更多数据中显示的点击操作。我完全不明白我在这里做错了什么。
我在 init 中调用了 setState,但在回调函数中。 statelesswidget ListTextArea 根本不会处理状态,但是 returns tapAction 到 StateFulWidget ListPage.dart.
那么为什么我会收到此错误。任何见解都会有所帮助。
问题出在 ListTextArea.dart 行
onTap: didTapOnTextArea(msg),
您正在构建方法中调用函数 didTapOnTextArea
,而不是将其作为点击侦听器传递。您必须将其替换为
onTap: (){
didTapOnTextArea(msg);
},
在我的例子中,错误发生在我在构建完成之前设置状态时,所以,我将它推迟到下一个 tick 并且它起作用了。
以前
myFunction()
新建
Future.delayed(Duration.zero, () async {
myFunction();
});
所以我正在尝试重构我的 listView 逻辑。基本上我的 ListView 已经变得笨重 UI 逻辑,所以我决定,为什么不将 UI 逻辑的某些部分移动到另一个 class
这是我的代码 ListPage.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:sample_flutter_works/ListTextArea.dart';
import 'package:sample_flutter_works/Model.dart';
import 'dart:convert';
import 'package:sample_flutter_works/RefreshTableContainer.dart';
class ListPage extends StatefulWidget {
@override
MyListPage createState() => MyListPage();
}
class MyListPage extends State<ListPage> {
MessageList messageList;
List<int> viewTimeInfo;
ScrollController _controller;
_scrollListener() {
}
@override
void initState() {
super.initState();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
_controller = ScrollController();
_controller.addListener(_scrollListener);
loadMessages(
completionBlock: (dataSet) => {
setState(() {
messageList = dataSet;
})
});
}
void loadMessages({completionBlock}) async {
var jsonString = await rootBundle.loadString('assets/Chat.json');
final jsonResponse = json.decode(jsonString);
if (jsonResponse != null) {
completionBlock(MessageList.fromJSON(jsonResponse));
} else {
completionBlock(null);
}
}
Widget listLayout() {
return ListView.separated(
padding: const EdgeInsets.all(8.0),
itemCount: (messageList != null && messageList.msgList != null)
? messageList.msgList.length
: 0,
separatorBuilder: (context, index) => Divider(
color: Colors.black,
height: 4.0,
),
itemBuilder: (BuildContext context, int index) {
var msgValToSend =
(messageList != null && messageList.msgList != null)
? messageList.msgList[index]
: null;
return Stack(
children: <Widget>[
IntrinsicHeight(
child: Row(
children: <Widget>[
getTheImageLayout(msgValToSend),
new ListTextArea(
msg: msgValToSend,
didTapOnTextArea: tappedOnTextArea,
visibilityCheck: checkForVisibility)
],
),
)
],
);
});
}
tappedOnTextArea(Message msg) {
var viewedInfo = this.viewTimeInfo;
if (viewedInfo != null) {
var indexOfTappedElement = viewedInfo.indexOf(msg.messageID);
if (indexOfTappedElement != null && indexOfTappedElement != -1) {
viewedInfo.removeAt(indexOfTappedElement);
} else {
viewedInfo.add(msg.messageID);
}
} else {
viewedInfo = [msg.messageID];
}
setState(() {
viewTimeInfo = viewedInfo;
});
}
checkForVisibility(bool _visible, Message msg) {
if (msg != null && this.viewTimeInfo != null) {
var checkForIndex = this.viewTimeInfo.indexOf(msg.messageID);
if (checkForIndex != null && checkForIndex != -1) {
_visible = true;
}
}
}
Widget getTheImageLayout(Message msg) {
return Expanded(
flex: 2,
child: Align(
alignment: Alignment.topLeft,
child: Padding(
padding: EdgeInsets.fromLTRB(5, 2.5, 0, 0),
child: Container(
color: Colors.red,
height: 50,
child: Row(
children: <Widget>[
userImageView(msg),
],
)),
)));
}
Widget userImageView(Message msg) {
return Expanded(
flex: 8,
child: Align(
alignment: Alignment.centerLeft,
child: Container(
width: 40.0,
height: 40.0,
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.green),
child: ClipOval(
child: Image.network(
(msg.msgUser.userPicUrl != null)
? msg.msgUser.userPicUrl
: 'https://picsum.photos/250?image=9',
fit: BoxFit.fill,
),
))));
}
Future<void> refreshTheChatTable() async {
print(" This is where the logic of pulll 2 refresh must be written ");
loadMessages(
completionBlock: (dataSet) => {
setState(() {
messageList = dataSet;
})
});
}
@override
Widget build(BuildContext context) {
return new RefreshTableContainer(
listLayout: listLayout(),
pull2RefreshAction: refreshTheChatTable,
);
}
}
ListTextArea.dart
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:sample_flutter_works/Model.dart';
class ListTextArea extends StatelessWidget {
Message msg;
Function didTapOnTextArea;
Function visibilityCheck;
ListTextArea({
this.msg,
this.didTapOnTextArea,
this.visibilityCheck
});
@override
Widget build(BuildContext context) {
return Expanded(
flex: 8,
child: GestureDetector(
onTap: didTapOnTextArea(msg),
child: Padding(
padding: EdgeInsets.fromLTRB(0, 2.5, 10, 0),
child: Column(
children: getChildWidgetArray(msg) ,
),
),
));
}
List<Widget> getChildWidgetArray(Message msg) {
var elementalArray = [
Align(
alignment: Alignment.topLeft,
child: Text(
(msg != null) ? msg.msgContent.content : "Data Loading",
style: TextStyle(
background: Paint()..color = Colors.orange,
),
),
),
Spacer(), // Defaults to a flex of one.
Align(
alignment: Alignment.bottomRight,
child: Text(
'Date of sending',
textDirection: TextDirection.rtl,
style: TextStyle(
background: Paint()..color = Colors.blue,
),
),
)
];
var _visible = false;
visibilityCheck(_visible,msg);
var timeInfo = AnimatedOpacity (
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Align(
child: _visible ? (Align(alignment: Alignment.topLeft,child:Column(children: <Widget>[Text("Last Read :" + (msg.msgTimeInfo.lastReadInfo)),
Text("Delievered :" + (msg.msgTimeInfo.deliveredInfo))],))): null));
elementalArray.add(timeInfo);
return elementalArray;
}
}
错误如下:
我正在尝试做的(或者当整个代码在 ListPage.dart 中时我已经做过)是在 listView 中动态计算的单元格,每个单元格响应在更多数据中显示的点击操作。我完全不明白我在这里做错了什么。
我在 init 中调用了 setState,但在回调函数中。 statelesswidget ListTextArea 根本不会处理状态,但是 returns tapAction 到 StateFulWidget ListPage.dart.
那么为什么我会收到此错误。任何见解都会有所帮助。
问题出在 ListTextArea.dart 行
onTap: didTapOnTextArea(msg),
您正在构建方法中调用函数 didTapOnTextArea
,而不是将其作为点击侦听器传递。您必须将其替换为
onTap: (){
didTapOnTextArea(msg);
},
在我的例子中,错误发生在我在构建完成之前设置状态时,所以,我将它推迟到下一个 tick 并且它起作用了。
以前
myFunction()
新建
Future.delayed(Duration.zero, () async {
myFunction();
});