【发布时间】:2020-05-10 04:56:54
【问题描述】:
我将在此处发布我的项目最低级别,以便您可以重现错误行为。
这里的类列表主要是从 Flutter 小部件层次结构的顶部到其余部分......
main.dart
import 'package:TestIt/widgets/applicationpage.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
final ApplicationPage applicationPage =
ApplicationPage(title: 'Flutter Demo');
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
primarySwatch: Colors.blue,
),
home: applicationPage);
}
}
applicationpage.dart
import 'package:flutter/material.dart';
import 'body.dart';
class ApplicationPage extends StatefulWidget {
ApplicationPage({Key key, this.title}) : super(key: key);
final String title;
@override
_ApplicationPageState createState() => _ApplicationPageState();
}
class _ApplicationPageState extends State<ApplicationPage> {
final Body body = new Body();
@override
Widget build(BuildContext context) {
return Scaffold(
body: body);
}
}
body.dart
import 'package:TestIt/viewmodels/excercise.dart';
import 'package:TestIt/viewmodels/workout.dart';
import 'package:flutter/material.dart';
import 'Excercises/ExcerciseListWidget.dart';
class Body extends StatelessWidget {
@override
Widget build(BuildContext context) {
var workouts = new List<Workout>();
var pullDay = new Workout("Pull day", new List<Excercise>());
workouts.add(pullDay);
return Container(
margin: EdgeInsets.all(5),
child: DefaultTabController(
// Added
length: workouts.length, // Added
initialIndex: 0, //Added
child: Scaffold(
appBar: PreferredSize(
// todo: add AppBar widget here again
preferredSize: Size.fromHeight(50.0),
child: Row(children: <Widget>[
TabBar(
indicatorColor: Colors.blueAccent,
isScrollable: true,
tabs: getTabs(workouts),
),
Container(
margin: EdgeInsets.only(left: 5.0),
height: 30,
width: 30,
child: FloatingActionButton(
heroTag: null,
child: Icon(Icons.add),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
elevation: 5.0,
onPressed: () => print("add workout"))),
Container(
margin: EdgeInsets.only(left: 5.0),
height: 30,
width: 30,
child: FloatingActionButton(
heroTag: null,
child: Icon(Icons.remove),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
elevation: 5.0,
onPressed: () => print("add workout"))),
])),
body: TabBarView(
children: getTabViews(workouts),
),
)));
}
List<ExcerciseListWidget> getTabViews(List<Workout> workouts) {
var tabViews = new List<ExcerciseListWidget>();
for (var i = 0; i < workouts.length; i++) {
tabViews.add(ExcerciseListWidget(workouts[i].excercises));
}
return tabViews;
}
List<Tab> getTabs(List<Workout> workouts) {
Color textColor = Colors.blueAccent;
return workouts
.map((w) => new Tab(
child: Text(w.name, style: TextStyle(color: textColor)),
))
.toList();
}
}
ExcerciseListWidget.dart
import 'package:TestIt/viewmodels/excercise.dart';
import 'package:flutter/material.dart';
import 'ExcerciseWidget.dart';
class ExcerciseListWidget extends StatefulWidget {
ExcerciseListWidget(this.excercises);
final List<Excercise> excercises;
@override
_ExcerciseListWidgetState createState() => _ExcerciseListWidgetState();
}
class _ExcerciseListWidgetState extends State<ExcerciseListWidget> {
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
widget.excercises.insert(
0,
new Excercise(widget.excercises.length + 1, "test",
widget.excercises.length * 10));
});
},
child: Icon(Icons.add),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
elevation: 5.0,
),
body: Container(
padding: const EdgeInsets.all(2),
child: ReorderableListView(
onReorder: (index1, index2) => {
print("onReorder"),
},
children: widget.excercises
.map((excercise) => ExcerciseWidget(
key: ValueKey(excercise.id), excercise: excercise))
.toList())));
}
}
ExcerciseWidget.dart
import 'package:TestIt/viewmodels/excercise.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'ExcerciseDetailsWidget.dart';
class ExcerciseWidget extends StatefulWidget {
ExcerciseWidget({this.key, this.excercise}) : super(key: key);
final Excercise excercise;
final Key key;
@override
_ExcerciseWidgetState createState() => _ExcerciseWidgetState();
}
class _ExcerciseWidgetState extends State<ExcerciseWidget> {
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 3.0, bottom: 3.0),
// TODo: with this ink box decoration the scrolling of the excercises goes under the tabbar... but with the ink I have a ripple effect NOT under
// the element...
child: Ink(
decoration: BoxDecoration(
borderRadius: new BorderRadius.all(new Radius.circular(5.0)),
border: Border.all(color: Colors.orange),
color: Colors.green),
child: InkWell(
onTap: () => {navigateToEditScreen(context)},
child: Column(
children: <Widget>[
Container(
color: Colors.red, child: Text(widget.excercise.name)),
],
)),
));
}
navigateToEditScreen(BuildContext context) async {
final Excercise result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ExcerciseDetailsWidget(excercise: widget.excercise)));
setState(() {
widget.excercise.name = result.name;
});
}
}
ExcerciseDetailsWidget.dart
import 'package:TestIt/viewmodels/excercise.dart';
import 'package:flutter/material.dart';
class ExcerciseDetailsWidget extends StatefulWidget {
final Excercise excercise;
ExcerciseDetailsWidget({Key key, @required this.excercise}) : super(key: key);
@override
_ExcerciseDetailsWidgetState createState() => _ExcerciseDetailsWidgetState();
}
class _ExcerciseDetailsWidgetState extends State<ExcerciseDetailsWidget> {
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.excercise.name),
),
body: Padding(
padding: EdgeInsets.only(left: 20, right: 20, bottom: 2, top: 2),
child: Form(
key: _formKey,
child: Column(children: <Widget>[
new RaisedButton(
elevation: 2,
color: Colors.blue,
child: Text('Save'),
onPressed: () {
setState(() {
widget.excercise.name = "new name";
});
Navigator.pop(context, widget.excercise);
}),
TextFormField(
decoration: InputDecoration(
//hintText: 'excercise name',
labelText: 'Excercise name',
),
initialValue: widget.excercise.name,
),
]))));
}
}
workout.dart
import 'excercise.dart';
class Workout{
Workout(this.name, this.excercises);
String name;
List<Excercise> excercises;
}
excercise.dart
class Excercise {
int id;
Excercise(this.id,this.name, this.restBetweenSetsInSeconds);
String name;
int restBetweenSetsInSeconds;
}
如何重现错误行为以获得异常:
单击右下角的浮动操作按钮以创建一个锻炼测试存根,该存根被添加到唯一的现有锻炼中。
点击新添加的练习
ExcerciseDetailsWidget 已加载
点击 ExcerciseDetailsWidget 中的保存
导航返回初始屏幕,异常正中你的脸!
例外
FlutterError (setState() 在 dispose() 之后调用:_ExcerciseWidgetState#bccdb(lifecycle state: defunct, notmounted) 如果您对不再出现在小部件树中的小部件(例如,其父小部件不再在其构建中包含该小部件)调用 State 对象上的 setState(),则会发生此错误。当代码从计时器或动画回调中调用 setState() 时,可能会发生此错误。 首选的解决方案是取消计时器或停止在 dispose() 回调中收听动画。另一种解决方案是在调用 setState() 之前检查该对象的“已安装”属性,以确保该对象仍在树中。 如果正在调用 setState(),则此错误可能表示内存泄漏,因为另一个对象在从树中删除此 State 对象后仍保留对该对象的引用。为避免内存泄漏,请考虑在 dispose() 期间中断对此对象的引用。)
问题
为什么我从 ExcerciseDetailsWidget 返回时,之前添加并点击的 ExcerciseWidget 的 State 被释放了?
检查是否已安装,然后调用 setState 不是解决方案,因为在任何情况下都不应该释放练习,因为我必须使用新的练习名称来更新它。
如果你知道我可以在其中放置项目的 Flutter 在线网站,请告诉我!
我是一个颤振初学者,也许我做错了什么,记住这一点:-)
更新
我为解决这个问题所做的工作是:
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ExcerciseDetailsWidget(excercise: widget.excercise)));
不要等待导航器的结果。
相反,我在 Screen2 中执行此操作:
onPressed: () {
if (_formKey.currentState.validate()) {
// 为什么我可以在这里设置没有 setState 的新文本,但是当我导航回来时,新的练习名称会反映在练习列表中。其实不应该这样吧?这完全让我感到困惑。
widget.excercise.name =
excerciseNameTextController.value.text;
Navigator.pop(context);
}
},
但这实际上只是一种适用于这种特殊 EDIT 用例的解决方法。
当我有一个 ADD 用例时,我需要返回一些内容以将其添加到练习列表中...
问题可能是我在练习中等待结果吗? 我想我会尝试在 ExcerciseListWidget 的上下文/级别上等待结果练习,而不是在 ExcerciseWidget 内。
更新 2
阅读有关导航器的更多信息,似乎或可能是当我导航回前一条路线(即我的初始/根目录)时,关于单击的练习的所有知识都消失了?因此,我需要那种嵌套路由吗?比如“/workouts/id/excercises/id”?
【问题讨论】: