构建期间调用的异常 setState() 或 markNeedsBuild()
Exception setState() or markNeedsBuild() called during build
一切正常,功能齐全,除了我在构建时抛出以下异常:
The following assertion was thrown while dispatching notifications for DatePickModel: setState() or markNeedsBuild() called during build.
我尝试搜索错误并发现了类似的情况,但是 none 我可以使用 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
参数。例如,您真的只想 read 回调函数中的提供者(而不是监视),例如 onPressed
。链接文档对此进行了更深入的探讨。
确定后,出现异常的原因是您在尝试构建小部件时更新状态。
避免这种情况的最简单方法是使用 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();
}
...
}
一切正常,功能齐全,除了我在构建时抛出以下异常:
The following assertion was thrown while dispatching notifications for DatePickModel: setState() or markNeedsBuild() called during build.
我尝试搜索错误并发现了类似的情况,但是 none 我可以使用 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
参数。例如,您真的只想 read 回调函数中的提供者(而不是监视),例如 onPressed
。链接文档对此进行了更深入的探讨。
确定后,出现异常的原因是您在尝试构建小部件时更新状态。
避免这种情况的最简单方法是使用 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();
}
...
}