在构建期间调用的异常 setState() 或 markNeedsBuild()

问题描述

一切正常,功能齐全,除了我在构建时抛出以下异常:

The following assertion was thrown while dispatching notifications for DatePickModel: setState() or markNeedsBuild() called during build.

我尝试搜索错误并发现了类似的情况,但没有一个我可以使用 riverpod 轻松应用于这种情况。而且我确定这没什么大不了的,但是我读到如果不处理这可能会造成循环。

任何想法如何解决它?

小工具

import 'package:Flutter/material.dart';
import 'package:date_range_picker/date_range_picker.dart' as DaterangePicker;
import 'package:Flutter_riverpod/Flutter_riverpod.dart';
import 'package:intl/intl.dart';

final dateController = ChangeNotifierProvider((ref) => DatePickModel());

class DaterangeWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return IconButton(
        color: Colors.white,icon: Icon(Icons.calendar_today),onpressed: () async {
          final List<DateTime> picked = await DaterangePicker.showDatePicker(
              context: context,initialFirstDate: context.read(dateController).startDate,initialLastDate: context.read(dateController).endDate,firstDate: DateTime(2015),lastDate: DateTime(DateTime.Now().year + 2));
          if (picked != null && picked.length == 2) {
            context.read(dateController).setDates(picked.first,picked.last);
          }
        });
  }
}

class DatePickModel extends ChangeNotifier {
  DateTime _startDate = DateTime.Now().subtract(Duration(days: 7));
  DateTime _endDate = DateTime.Now();

  String startFormatted;
  String endFormatted;

  DateTime get startDate => _startDate;
  DateTime get endDate => _endDate;

  void setDates(DateTime startDate,DateTime endDate) {
    _startDate = startDate;
    _endDate = endDate;
    notifyListeners();
  }

  void formatDates() {
    startFormatted = DateFormat.yMMMd().format(_startDate);
    endFormatted = DateFormat.yMMMd().format(_endDate);
    notifyListeners();
  }

  void resetDates() {
    _startDate = DateTime.Now().subtract(Duration(seconds: 7));
    _endDate = DateTime.Now();
    notifyListeners();
  }
}

屏幕

import 'package:Flutter/material.dart';
import 'package:Flutter_riverpod/Flutter_riverpod.dart';
import 'package:linechart/date_range_widget.dart';
import 'package:linechart/linechart.dart';

class LineChartScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
          title: AppBarTitle(),backgroundColor: Colors.lightBlue,actions: [DaterangeWidget()]),body: Column(
        children: [
          SafeArea(
            child: Center(
                child: Padding(
              padding: const EdgeInsets.all(10.0),child: LineChart(),)),),],);
  }
}

class AppBarTitle extends ConsumerWidget {
  @override
  Widget build(BuildContext context,ScopedReader watch) {
    context.read(dateController).formatDates();
    final formStart = watch(dateController).startFormatted;
    final formEnd = watch(dateController).endFormatted;
    return Text('INCOME GRAPH --- $formStart - $formEnd');
  }
}

解决方法

首先,避免在构建方法中使用 context.read,而是使用 ConsumerWidget 的 watch 参数。例如,您只想在回调函数(如 onPressed)中读取一个提供程序(而不是 watch)。链接的文档对此进行了更深入的探讨。

确定后,您收到异常的原因是您正在尝试构建小部件时更新状态。

避免这种情况的最简单方法是使用 addPostFrameCallback 来确保在初始构建后更新状态:

@override
Widget build(BuildContext context,ScopedReader watch) {
  WidgetsBinding.instance?.addPostFrameCallback((_) {
    context.read(dateController).formatDates();
  });
  ...
}

请注意 context.read 在这种情况下是正确的,因为我们在回调函数中使用它。

编辑: 您也可以在设置日期时格式化日期,如下所示:

class DatePickModel extends ChangeNotifier {
  ...
  void setDates(DateTime startDate,DateTime endDate) {
    _startDate = startDate;
    _endDate = endDate;
    formatDates(notify: false);
    notifyListeners();
  }

  void formatDates({bool notify = true}) {
    startFormatted = DateFormat.yMMMd().format(_startDate);
    endFormatted = DateFormat.yMMMd().format(_endDate);
    if (notify) notifyListeners();
  }
  ...
}