如何解决未处理的异常:处置后使用了DashboardService

时间:2020-09-21 12:11:49

标签: flutter dart

我有下面的屏幕,其中包含排序部分:

Sort Part

所以现在的问题是,当我在排序消失之前快速单击排序部分中的任何项目时,我发现了以下错误,并出现了冻结或黑屏。

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状态:

DeBug Desc

我希望这些信息足够清楚,让我知道是否缺少某些东西

0 个答案:

没有答案