問題描述
設置狀態內存洩漏 (set state memory leak)
我是flutter的初學者,我寫了一個從api調用json數據的代碼,然後我執行了但是執行速度有點慢,在控制台中我發現了一條錯誤消息,上面寫著
E/flutter ( 6908): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: setState() called after dispose(): _PlayersCreationDetailsViewState#559ce(lifecycle state: defunct, not mounted)
E/flutter ( 6908): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
E/flutter ( 6908): The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
E/flutter ( 6908): This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
E/flutter ( 6908): #0 State.setState.<anonymous closure> (package:flutter/src/widgets/framework.dart:1208:9)
E/flutter ( 6908): #1 State.setState (package:flutter/src/widgets/framework.dart:1243:6)
E/flutter ( 6908): #2 _PlayersCreationDetailsViewState.getPlayer (package:footyappp/Fantazyy/players_creation_details_view.dart:101:5)
E/flutter ( 6908): <asynchronous suspension>
E/flutter ( 6908): #3 _PlayersCreationDetailsViewState.getClubIds.<anonymous closure> (package:footyappp/Fantazyy/players_creation_details_view.dart:84:14)
E/flutter ( 6908): #4 State.setState (package:flutter/src/widgets/framework.dart:1244:30)
E/flutter ( 6908): #5 _PlayersCreationDetailsViewState.getClubIds (package:footyappp/Fantazyy/players_creation_details_view.dart:73:5)
E/flutter ( 6908): #6 _rootRunUnary (dart:async/zone.dart:1198:47)
E/flutter ( 6908): #7 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter ( 6908): #8 _FutureListener.handleValue (dart:async/future_impl.dart:143:18)
這是我嘗試過的代碼:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:footyappp/Fantazyy/Playerrs.dart';
import 'package:footyappp/Fantazyy/club_api.dart';
import 'package:footyappp/Fantazyy/create_team_view.dart';
import 'package:footyappp/Fantazyy/player%20copy.dart';
import 'package:footyappp/Fantazyy/player_lab.dart';
import 'package:footyappp/Key/Key.dart';
import 'package:http/http.dart' as http;
class PlayersCreationDetailsView extends StatefulWidget {
final List<Playerr> selectedPlayers;
final int playerIndex;
const PlayersCreationDetailsView ({
Key key,
@required this.selectedPlayers,
@required this.playerIndex
}) : super(key: key);
@override
_PlayersCreationDetailsViewState createState() => _PlayersCreationDetailsViewState();
}
class _PlayersCreationDetailsViewState extends State<PlayersCreationDetailsView> {
bool _sortAsc = false;
int _sortColumnIndex = 0;
double _columnWidth = 40.0;
double _columnNameWidth = 60.0;
double _columnPosWidth = 80.0;
PlayersDataSource _playersDataSource;
List<Playerr> _players = [];
List<ClubApi> selectedClubs = [];
List<int> clubIdentifiers = [];
List<Playerrs> playersjson = [];
List<Playerr> playersApi = [];
List<dynamic> playerList ;
List _table;
int _rowsPerPage = 20;
final String apiUrl =
"https://v3.football.api‑sports.io/players?season=2020&league=39";
static const headers = {
'x‑rapidapi‑host': "v3.football.api‑sports.io",
//Always make sure to check the api key and the limit of a request in a free api
'x‑rapidapi‑key': ApiKey.key
};
void _sort<T>(Comparable<T> getField(Playerr p), int columnIndex, bool ascending) {
_playersDataSource._sort<T>(getField, ascending);
setState(() {
_sortColumnIndex = columnIndex;
_sortAsc = ascending ;
});
}
Future<void> getClubIds() async {
http.Response response = await http.get(
"https://v3.football.api‑sports.io/teams?season=2020&league=39",
headers: {'x‑rapidapi‑host': "v3.football.api‑sports.io",
'x‑rapidapi‑key': ApiKey.key});
String body = response.body;
var data = jsonDecode(body);
List<dynamic> table = data['response'];
setState(() {
selectedClubs = table
.map((dynamic item) => ClubApi.fromJson(item))
.toList();
for(var item in selectedClubs){
clubIdentifiers.add(item.team.id);
}
for(var item in clubIdentifiers){
print("club id"+item.toString());
this.getPlayer(item);
}
});
}
Future<void> getPlayer(int id) async {
http.Response response = await http.get(
"https://v3.football.api‑sports.io/players?season=2020&league=39&team=$id",
headers: {'x‑rapidapi‑host': "v3.football.api‑sports.io",
'x‑rapidapi‑key': ApiKey.key});
String body = response.body;
var data = jsonDecode(body);
List<dynamic> table = data['response'];
setState(() {
playersjson = table
.map((dynamic item) => Playerrs.fromJson(item))
.toList();
for(var item in playersjson){
String pos = item.statistics[0].games.position.toString().substring(9);
playersApi.add(Playerr(item.player.id,item.player.firstname, item.player.lastname, pos,
item.statistics[0].team.name,item.statistics[0].games.rating, item.statistics[0].games.appearences,item.statistics[0].goals.total,
item.statistics[0].goals.assists,item.statistics[0].goals.conceded,
item.statistics[0].cards.red, item.statistics[0].cards.yellow));
}
print(playersApi.length);
// for(var item in playersApi){
// print("position"+item.position.toString());
// print(item.position.contains("DEFENDER"));
// }
for(var item in playersApi){
print("position"+item.position.toString());
}
if (widget.playerIndex < 2) {
_players = playersApi.where((player) => player
.position.contains("GOALKEEPER")).toList();
} else if (widget.playerIndex < 7) {
_players = playersApi.where((player) => player.position.contains("DEFENDER")).toList();
} else if (widget.playerIndex < 12) {
_players = playersApi.where((player) => player.position.contains("MIDFIELDER")).toList();
} else {
_players = playersApi.where((player) => player.position.contains("ATTACKER")).toList();
}
print("length of selected"+widget.selectedPlayers.length.toString());
for(var item in widget.selectedPlayers){
print("selected"+item.toString());
if(item == null){
print("hello");
}else if(item !=null){
print(item.playerID.toString());
_players.removeWhere((player) => player.playerID == item.playerID);
}
}
// widget.selectedPlayers.forEach((selectedPlayer) =>
// _players.removeWhere((player) => (player.playerID == selectedPlayer.playerID) && (selectedPlayer.playerID != null)));
// print("length of selected"+widget.selectedPlayers.length.toString());
//filter out players who are already selected
// for (Playerr player in widget.selectedPlayers) {
// _players.remove(player);
// }
_playersDataSource = PlayersDataSource(widget.playerIndex, widget.selectedPlayers, _players, context);
});
}
@override
void initState() {
super.initState();
this.getClubIds();
//filter out players by position
}
@override
Widget build(BuildContext context) {
print(playersApi.length);
return playersApi.length == 0
? Container(
color: Colors.white,
child: Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
Color(0xFFe70066),
),
),
),
)
: WillPopScope(
onWillPop: () async => false,
child: Scaffold(
body: ListView(
children: <Widget>[
PaginatedDataTable(
columnSpacing: 1.0,
horizontalMargin: 1.0,
availableRowsPerPage: [10,20,50],
rowsPerPage: _rowsPerPage,
onRowsPerPageChanged: (int value) { setState(() { _rowsPerPage = value; }); },
sortColumnIndex: _sortColumnIndex,
sortAscending: _sortAsc,
header: Text("Players"),
columns: <DataColumn>[
new DataColumn(
label: new Container(width: _columnPosWidth , child: Text("First Name", softWrap: true,)),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<String>((Playerr p) => p.firstName, columnIndex, ascending)
),
new DataColumn(
label: new Container(width: _columnPosWidth , child: Text("Last Name", softWrap: true,)),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<String>((Playerr p) => p.lastName, columnIndex, ascending)
),
new DataColumn(
label: new Container(width: _columnPosWidth , child: Text("Position", softWrap: true,)),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<String>((Playerr p) => p.position, columnIndex, ascending)
),
new DataColumn(
label: new Container(width: _columnPosWidth , child: new Text("Price")),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Playerr p) => p.price, columnIndex, ascending)
),
new DataColumn(
label: new Container(width: _columnPosWidth , child: Text("Rating", softWrap: true,)),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<String>((Playerr p) => p.rating, columnIndex, ascending)
),
/* new DataColumn(
label: new Container(width: _columnWidth , child: new Text("Position")),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<String>((Playerr p) => p.position, columnIndex, ascending)
),*/
new DataColumn(
label: new Container(width: _columnPosWidth , child: new Text("Team")),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<String>((Playerr p) => p.team, columnIndex, ascending)
),
new DataColumn(
label: new Container(width: _columnPosWidth , child: new Text("Apps")),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Playerr p) => p.appearances, columnIndex, ascending)
),
/* new DataColumn(
label: new Container(width: _columnWidth , child: new Text("Points")),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Player p) => p.points, columnIndex, ascending)
),
new DataColumn(
label: new Container(width: _columnWidth , child: new Text("Week Points")),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Player p) => p.pointsWeek, columnIndex, ascending)
),*/
/* new DataColumn(
label: new Container(width: _columnWidth , child: new Text("Sub Apps")),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Player p) => p.subAppearances, columnIndex, ascending)
),*/
new DataColumn(
label: new Container(width: _columnPosWidth , child: new Text("Goals")),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Playerr p) => p.goals, columnIndex, ascending)
),
new DataColumn(
label: new Container(width: _columnPosWidth , child: new Text("Assists")),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Playerr p) => p.assists, columnIndex, ascending)
),
new DataColumn(
label: new Container(width: _columnPosWidth , child: new Text("Clean")),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Playerr p) => p.cleanSheets, columnIndex, ascending)
),
new DataColumn(
label: new Container(width: _columnPosWidth , child: new Text("Yellows")),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Playerr p) => p.yellowCards, columnIndex, ascending)
),
new DataColumn(
label: new Container(width: _columnPosWidth , child: new Text("Reds")),
numeric: true,
onSort: (int columnIndex, bool ascending) => _sort<num>((Playerr p) => p.redCards, columnIndex, ascending)
),
],
source: _playersDataSource,
)
],
)
)
);
}
}
class PlayersDataSource extends DataTableSource {
PlayersDataSource(this._playerIndex, this._selectedPlayers, this._players, this.context);
int _playerIndex;
List<Playerr> _players;
List<Playerr> _selectedPlayers;
int _selectedCount = 0;
var context;
double _columnWidth = 40.0;
double _columnNameWidth = 60.0;
double _columnPosWidth = 80.0;
void _sort<T>(Comparable<T> getField(Playerr p), bool ascending) {
_players.sort((Playerr a, Playerr b) {
if (!ascending) {
final Playerr c = a;
a = b;
b = c;
}
final Comparable<T> aValue = getField(a);
final Comparable<T> bValue = getField(b);
return Comparable.compare(aValue, bValue);
});
notifyListeners();
}
DataCell getCell(String text) {
return DataCell(Container(width: _columnPosWidth, child: Text(text, overflow: TextOverflow.fade, softWrap: false,)));
}
@override
DataRow getRow(int index) {
assert(index >= 0);
if (index >= _players.length)
return null;
final Playerr player = _players[index];
return DataRow.byIndex(
onSelectChanged: (bool) {
_players.removeAt(index);
_selectedPlayers[_playerIndex] = player;
Navigator.pushReplacement(context, MaterialPageRoute(builder: (BuildContext context) {return CreateTeamView(players: _players, selectedPlayers: _selectedPlayers,);}));
},
index: index,
cells: <DataCell>[
getCell(player.firstName),
getCell(player.lastName),
getCell(player.position),
getCell('${player.price}'),
getCell('${player.rating}'),
// getCell('${player.isFresher}'),
getCell('${player.team}'),
//getCell('${player.points}'),
// getCell('${player.pointsWeek}'),
getCell('${player.appearances}'),
// getCell('${player.subAppearances}'),
getCell('${player.goals}'),
getCell('${player.assists}'),
getCell('${player.cleanSheets}'),
// getCell('${player.motms}'),
getCell('${player.yellowCards}'),
getCell('${player.redCards}'),
// getCell('${player.ownGoals}'),
]
);
}
@override
int get rowCount => _players.length;
@override
bool get isRowCountApproximate => false;
@override
int get selectedRowCount => _selectedCount;
}
所以問題還在於具有位置攻擊者的玩家列表:並非所有人都顯示在列表中,可能是因為設置狀態或其他原因,
p>我正在嘗試為設置狀態和過濾器的這個問題找到解決方案,我打賭這些問題是相關的
參考解法
方法 1:
You are calling setState((){})
after page is closed and dispose()
was called, you can use mounted
property which is a boolean and check whether State object is currently in a tree, or in other words page is still active before calling setState((){})
if(mounted){
setState((){})
}
(by Fares Ben Slama、Ara Kurghinyan)