我有下面的屏幕,其中包含排序部分:
所以现在的问题是,当我在排序消失之前快速单击排序部分中的任何项目时,我发现了以下错误,并出现了冻结或黑屏。
E/flutter ( 6099): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: A DashboardService was used after being disposed.
E/flutter ( 6099): Once you have called dispose() on a DashboardService, it can no longer be used.
这是我拥有的底部菜单:
import 'package:BOTS/globals/localize.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:BOTS/widgets/forms/heightSpacer.dart';
import 'package:BOTS/widgets/label.dart';
import 'package:BOTS/widgets/revenyouDivider.dart';
import '../../backend/backend.dart';
import '../../globals/constants.dart';
import 'package:BOTS/pages/dashboard/dashboardService.dart';
class DashboardOptionsBottomSheet extends StatefulWidget {
final DashboardService dashboardService;
const DashboardOptionsBottomSheet({
Key key,
this.dashboardService,
}) : super(key: key);
@override
DashboardOptionsBottomSheetState createState() =>
DashboardOptionsBottomSheetState(dashboardService);
}
class DashboardOptionsBottomSheetState
extends State<DashboardOptionsBottomSheet> {
final DashboardService dashboardService;
DashboardOptionsBottomSheetState(this.dashboardService);
@override
Widget build(BuildContext context) {
final _width = MediaQuery.of(context).size.width;
final _segmentWidth = (_width - 50.0) / 3;
String results = Localize.of(context).trans("dashboardOptions.results");
String newest = Localize.of(context).trans("dashboardOptions.newest");
String amount = Localize.of(context).trans("dashboardOptions.amount");
var sharedValue = 0;
final bold = FontWeight.w700;
final med = FontWeight.w500;
FontWeight _resultsWeight = med;
FontWeight _newestWeight = med;
FontWeight _amountWeight = med;
Map<int, Widget> segmentMap() {
return <int, Widget>{
0: Container(
height: 37,
width: _segmentWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Label(
textAlign: TextAlign.center,
text: results,
size: 14,
fontWeight: _resultsWeight,
),
]),
),
1: Container(
height: 37,
width: _segmentWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Label(
textAlign: TextAlign.center,
text: newest,
size: 14,
fontWeight: _newestWeight,
),
]),
),
2: Container(
height: 37,
width: _segmentWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Label(
textAlign: TextAlign.center,
text: amount,
size: 14,
fontWeight: _amountWeight,
),
]),
),
};
}
SortedBy sortedBy = dashboardService.sortedBy;
switch (sortedBy) {
case SortedBy.results:
sharedValue = 0;
_resultsWeight = bold;
_newestWeight = med;
_amountWeight = med;
break;
case SortedBy.newest:
sharedValue = 1;
_resultsWeight = med;
_newestWeight = bold;
_amountWeight = med;
break;
case SortedBy.amount:
sharedValue = 2;
_resultsWeight = med;
_newestWeight = med;
_amountWeight = bold;
break;
}
String _sorted;
return Material(
child: Container(
height: 380,
decoration: BoxDecoration(
color: Color(contrastColor3),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
HeightSpacer(height: 30),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
padding: const EdgeInsets.only(left: 25),
child: TitleLabel(
text: Localize.of(context).trans("dashboardOptions.sortBy"),
size: 16,
),
),
],
),
HeightSpacer(height: 20),
Padding(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: Container(
child: CupertinoSlidingSegmentedControl(
children: segmentMap(),
onValueChanged: (int val) {
sharedValue = val;
SortedBy sortedBy;
switch (val) {
case 0:
sortedBy = SortedBy.results;
_sorted = "results";
_newestWeight = med;
_amountWeight = med;
break;
case 1:
sortedBy = SortedBy.newest;
_sorted = "newest";
_resultsWeight = med;
_amountWeight = med;
break;
case 2:
sortedBy = SortedBy.amount;
_sorted = "amount";
_resultsWeight = med;
_newestWeight = med;
break;
}
dashboardService.clearStrategies();
dashboardService.sortedBy = sortedBy;
prefs.sortedBy = _sorted;
dashboardService
.getBalanceData(dashboardService.showCrypto);
dashboardService.fetchAllStrategies();
setState(() {});
Future.delayed(const Duration(milliseconds: 300), () {
Navigator.of(context).pop();
});
},
groupValue: sharedValue,
),
),
),
HeightSpacer(height: 20),
RevenYouDivider(
color: Colors.black.withOpacity(0.2),
),
HeightSpacer(height: 30),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
padding: const EdgeInsets.only(left: 25),
child: TitleLabel(
text:
Localize.of(context).trans("dashboardOptions.display"),
size: 16,
),
),
],
),
HeightSpacer(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
padding: const EdgeInsets.only(left: 25),
child: Label(
text: Localize.of(context)
.trans("dashboardOptions.showGraphs"),
size: 14,
),
),
Padding(
padding: EdgeInsets.only(right: 15),
child: Switch(
value: dashboardService.showGraphs,
onChanged: (val) {
dashboardService.clearStrategies();
dashboardService.showGraphs = val;
prefs.showGraphs = val;
dashboardService
.getBalanceData(dashboardService.showCrypto);
dashboardService.fetchAllStrategies();
// Update the switch UI, then pop the bottomsheet after a delay
setState(() {});
Future.delayed(const Duration(milliseconds: 300), () {
Navigator.of(context).pop();
});
},
activeColor: Colors.white,
activeTrackColor: Color(primaryColor5),
inactiveTrackColor: Color(contrastColor2),
),
),
],
),
HeightSpacer(height: 12),
RevenYouDivider(
color: Colors.black.withOpacity(0.1),
),
HeightSpacer(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
padding: const EdgeInsets.only(left: 25),
child: Label(
text: Localize.of(context)
.trans("dashboardOptions.showCrypto"),
size: 14,
),
),
Padding(
padding: EdgeInsets.only(right: 15),
child: Switch(
value: dashboardService.showCrypto,
onChanged: (val) {
dashboardService.clearStrategies();
dashboardService.showCrypto = val;
prefs.showCrypto = val;
dashboardService.getBalanceData(val);
dashboardService.fetchAllStrategies();
// Update the switch UI, then pop the bottomsheet after a delay
setState(() {});
Future.delayed(const Duration(milliseconds: 300), () {
Navigator.of(context).pop();
});
},
activeColor: Colors.white,
activeTrackColor: Color(primaryColor5),
activeThumbImage: AssetImage(
"assets/artwork/crypto/crypto.webp",
),
inactiveThumbImage: AssetImage(
"assets/artwork/euro/euro.webp",
),
inactiveTrackColor: Color(contrastColor2),
),
),
],
),
HeightSpacer(height: 12),
RevenYouDivider(
color: Colors.black.withOpacity(0.1),
),
Container(height: 20),
],
),
),
);
}
}
这是dashBoardService
DashboardService() {
showCrypto = prefs.showCrypto;
showGraphs = prefs.showGraphs;
final sort = prefs.sortedBy;
switch (sort) {
case "results":
sortedBy = SortedBy.results;
break;
case "newest":
sortedBy = SortedBy.newest;
break;
case "amount":
sortedBy = SortedBy.amount;
break;
}
_initFetchTimer();
_initCheckCryptoTimer();
// fetch the latest langauge updates when the user is logged in
languageService.updateLangaugeInApp();
}
这是DashBOardService()的完整部分;
import 'dart:async';
import 'package:BOTS/backend/backend.dart';
import 'package:BOTS/backend/models/dashboardData.dart';
import 'package:BOTS/backend/models/strategy.dart';
import 'package:BOTS/backend/models/totalBalance.dart';
import 'package:BOTS/backend/models/totalPortfolio.dart';
import 'package:BOTS/globals/localize.dart';
import 'package:BOTS/helpers/localAuthHelper.dart';
import 'package:BOTS/widgets/platform_alert_dialog.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:BOTS/extensions/iterablesExtensions.dart';
import 'package:BOTS/extensions/doubleExtensions.dart';
import 'package:http/http.dart';
enum StrategyType { active, pending, closed }
enum SortedBy { results, newest, amount }
class DashboardService extends ChangeNotifier {
Timer _fetchTimer;
Timer _cryptoTimer;
DashboardPortfolioData portfolioData;
List<Strategy> activeStrategies = [];
List<Strategy> pendingStrategies = [];
List<dynamic> closedStrategies = [];
List<TotalBalance> cardDetails = [];
double euroAmount = 0.0;
var activeHasBeenFetched = false;
var pendingHasBeenFetched = false;
var closedHasBeenFetched = false;
var showGetStartedPage = false;
var showTotalPortfolioSpinner = true;
var showActiveCardSpinners = true;
var showPendingCardSpinners = true;
var closedStrategiesLoading = true;
bool ignoreSwitchTap = true;
bool _fetchingAll = false;
bool showCrypto = false;
bool showGraphs = true;
bool userHasBalances = false;
String totalBalance;
String availableBalance;
double scrollOffset = 0.0;
double scrollAmount;
/// the current selected strategy tab on the dashboard.
/// when the timer completes the strategy tab will get
/// refreshed
StrategyType selectedStrategyTab = StrategyType.active;
SortedBy sortedBy = SortedBy.results;
TabController tabBarViewController;
DashboardService() {
showCrypto = prefs.showCrypto;
showGraphs = prefs.showGraphs;
final sort = prefs.sortedBy;
switch (sort) {
case "results":
sortedBy = SortedBy.results;
break;
case "newest":
sortedBy = SortedBy.newest;
break;
case "amount":
sortedBy = SortedBy.amount;
break;
}
_initFetchTimer();
_initCheckCryptoTimer();
// fetch the latest langauge updates when the user is logged in
languageService.updateLangaugeInApp();
}
void _initFetchTimer() {
_fetchTimer = Timer.periodic(Duration(minutes: 5), (_) async {
if (!data.isLoggedOn) {
_fetchTimer.cancel();
return;
}
await clearStrategies();
await fetchPortfolioData(isCrypto: showCrypto, showGraphs: showGraphs);
fetchAllStrategies();
});
}
void _initCheckCryptoTimer() {
bool _showCrypto = showCrypto;
_cryptoTimer = Timer.periodic(Duration(milliseconds: 500), (_) async {
if (_showCrypto != prefs.showCrypto) {
await forceRefresh();
await getBalanceData(prefs.showCrypto);
}
});
}
Future<DashboardPortfolioData> fetchPortfolioData({
bool isCrypto = false,
bool showGraphs = true,
}) async {
String exchange = "all";
String asset = "";
bool paper = false;
ignoreSwitchTap = true;
showTotalPortfolioSpinner = true;
notifyListeners();
portfolioData = await payment.getTotalPortfolioAndBalances(
exchange,
asset,
paper,
isCrypto: isCrypto,
);
showGetStartedPage = _shouldShowGetStartedPage();
prefs.showGetStarted = showGetStartedPage;
prefs.showGraphs = showGraphs;
showTotalPortfolioSpinner = false;
notifyListeners();
return portfolioData;
}
bool _shouldShowGetStartedPage() {
final totalBalance =
portfolioData?.totalPortfolio?.totalBalance?.parseToDouble();
final pendingBalance =
portfolioData?.totalPortfolio?.pendingBalance?.parseToDouble();
final withdrawableBalance =
portfolioData?.totalPortfolio?.withdrawableBalance?.parseToDouble();
final availableBalance =
portfolioData?.totalPortfolio?.availableBalance?.parseToDouble();
final avialableBalanceYouhex =
portfolioData?.totalPortfolio?.availableBalanceYouhex?.parseToDouble();
final avialableBalanceGalaex =
portfolioData?.totalPortfolio?.availableBalanceGalaex?.parseToDouble();
return totalBalance == 0.0 &&
(activeStrategies?.length ?? 0) == 0 &&
pendingBalance == 0.0 &&
withdrawableBalance == 0.0 &&
availableBalance == 0.0 &&
avialableBalanceYouhex == 0.0 &&
avialableBalanceGalaex == 0.0 &&
(pendingStrategies?.length ?? 0) == 0;
}
/// Select the type of strategies to load using an enum.
/// The [tabChanged] flag is used to prevent over requesting of strategies when the tab
/// changes in the dashboard. The exception to this rule is if the tab is selected for the first time.
Future fetchStrategies({
@required StrategyType strategyType,
bool tabChanged = false,
}) async {
switch (strategyType) {
case StrategyType.active:
if (tabChanged && activeHasBeenFetched) {
return;
}
activeHasBeenFetched = true;
showActiveCardSpinners = true;
ignoreSwitchTap = true;
notifyListeners();
// sort the returned strategy list
final unsortedStrategies =
await subscribeBot.activeStrategy(showCrypto: showCrypto);
activeStrategies = await sortStrategies(unsortedStrategies);
activeStrategies = await sortStrategies(unsortedStrategies);
// refresh the dashboard for a final time to turn off the loading spinners
showActiveCardSpinners = false;
if (!_fetchingAll) ignoreSwitchTap = false;
notifyListeners();
break;
case StrategyType.pending:
if (tabChanged && pendingHasBeenFetched) {
return;
}
pendingHasBeenFetched = true;
showPendingCardSpinners = true;
ignoreSwitchTap = true;
notifyListeners();
pendingStrategies = await subscribeBot.pendingStrategy();
showPendingCardSpinners = false;
if (!_fetchingAll) ignoreSwitchTap = false;
notifyListeners();
break;
case StrategyType.closed:
if (tabChanged && closedHasBeenFetched) {
return;
}
closedHasBeenFetched = true;
closedStrategiesLoading = true;
ignoreSwitchTap = true;
notifyListeners();
final closedItems =
await subscribeBot.stoppedStrategy(showCrypto: showCrypto);
closedItems.sort((x, y) => x.dateStopped.compareTo(y.dateStopped));
final groupedItems =
closedItems.reversed.groupBy((x) => x.formattedDateStopped);
closedStrategies = [];
groupedItems.forEach((key, value) {
closedStrategies.add(key);
closedStrategies.addAll(value);
});
closedStrategiesLoading = false;
if (!_fetchingAll) ignoreSwitchTap = false;
notifyListeners();
break;
}
}
Future fetchAllStrategies() async {
_fetchingAll = true;
showActiveCardSpinners = true;
showPendingCardSpinners = true;
closedStrategiesLoading = true;
ignoreSwitchTap = true;
notifyListeners();
for (StrategyType type in StrategyType.values) {
await fetchStrategies(strategyType: type);
}
_fetchingAll = false;
ignoreSwitchTap = false;
notifyListeners();
return;
}
Future<List<Strategy>> sortStrategies(List<Strategy> unsortedList) async {
if (unsortedList == null) {
return [];
}
var newList = unsortedList;
if (sortedBy == SortedBy.results) {
newList.sort((b, a) => a.percentageChange.compareTo(b.percentageChange));
} else if (sortedBy == SortedBy.amount) {
newList.sort((b, a) => a.totalAmount.compareTo(b.totalAmount));
} else if (sortedBy == SortedBy.newest) {
newList.sort((b, a) => a.dateSubbed.compareTo(b.dateSubbed));
}
return newList;
}
void clearStrategies() async {
activeStrategies = null;
pendingStrategies = null;
closedStrategies = null;
notifyListeners();
}
Future<TotalPortfolio> updateTotalBalance() async {
final result =
await fetchPortfolioData(isCrypto: showCrypto, showGraphs: showGraphs);
portfolioData.totalPortfolio = result.totalPortfolio;
return portfolioData.totalPortfolio;
}
Future getBalanceData(bool isCrypto) async {
final portAndBalance =
await fetchPortfolioData(isCrypto: isCrypto, showGraphs: showGraphs);
if (!isCrypto) {
euroAmount =
portfolioData?.totalBalances?.firstOrNull?.totalAmount ?? 0.00;
}
totalBalance = portAndBalance.totalPortfolio.totalBalance;
availableBalance = portAndBalance.totalPortfolio.availableBalance;
if (portAndBalance.totalBalances.length > 0) {
userHasBalances = true;
} else {
userHasBalances = false;
}
cardDetails = portAndBalance.totalBalances;
notifyListeners();
}
Future initBalanceData(bool isCrypto) async {
await getBalanceData(false);
euroAmount = portfolioData?.totalBalances?.firstOrNull?.totalAmount ?? 0.00;
if (isCrypto) await getBalanceData(true);
}
void forceRefresh() async {
await clearStrategies();
_fetchTimer.cancel();
_cryptoTimer.cancel();
showCrypto = prefs.showCrypto;
fetchPortfolioData(isCrypto: showCrypto);
fetchAllStrategies();
_initFetchTimer();
_initCheckCryptoTimer();
}
void setScrollAmount(double amount) {
scrollAmount = amount;
notifyListeners();
}
void setScrollOffset(double offset) {
scrollOffset = offset;
notifyListeners();
}
Future askToUseBiometrics(BuildContext context) async {
Future.delayed(Duration(seconds: 0), () async {
final canCheckBiometrics = await LocalAuthHelper.canCheckBiometrics();
if (!canCheckBiometrics) {
return;
}
if (prefs.biometrics || prefs.userAskedToEnableBiometrics) {
return;
}
var idString = Localize.of(context).trans("biometrics.touchID");
var permissionContent =
Localize.of(context).trans("biometrics.permissionContentTouchID");
if (await LocalAuthHelper.faceIDAvailable()) {
idString = Localize.of(context).trans("biometrics.faceID");
permissionContent =
Localize.of(context).trans("biometrics.permissionContentFaceID");
}
if (canCheckBiometrics) {
await PlatformAlertDialog(
title: "${permissionContent} ${idString}?",
content: permissionContent,
cancelActionText:
Localize.of(context).trans("biometrics.permissionCancelButton"),
defaultActionText: Localize.of(context).trans("verifyId.ok"),
onActionButtonTapped: () async {
prefs.biometrics = true;
prefs.userAskedToEnableBiometrics = true;
},
).show(context);
prefs.userAskedToEnableBiometrics = true;
}
});
}
@override
void dispose() {
super.dispose();
_fetchTimer.cancel();
_cryptoTimer.cancel();
}
}
这是下面的DeBug状态:
我希望这些信息足够清楚,让我知道是否缺少某些东西