第一个海报在这里!不知道我是否缺少任何信息/摘要,让我知道!
好的,
我正在为我们的提供者创建急救文档应用程序,并且不断出现此错误。
NoSuchMethodError: The getter 'bloc' was called on null.
Receiver: null
Tried calling: bloc
当前,BLoC会获取并构建在用户设备上创建的所有报告的列表。选择其中一个报告会在报告的详细页面上推送正确的个人信息(名字,姓氏,过敏等)。同一页面由带有Report
标签和Procedures
标签的TabBarView划分。 Procedures
标签显示了错误消息,但我似乎无法弄清楚原因。参见下面的代码:
ReportsPage.dart
class ReportsPage extends StatefulWidget {
ReportsPage({
Key key,
this.title,
}) : super(key: key);
static const pageId = 'reportsPage';
final String title;
@override
_ReportsPageState createState() => _ReportsPageState();
}
class _ReportsPageState extends State<ReportsPage> {
ReportsBloc _reportsBloc;
@override
void initState() {
super.initState();
_reportsBloc = BlocProvider.of<ReportsBloc>(context);
}
static createCallNb() {
String callNumber =
DateTime.now().toString().replaceAll(RegExp('[^0-9]'), '');
callNumber = callNumber.replaceRange(15, null, '');
return callNumber;
}
void _addReport() async {
Report report = new Report(reportId: createCallNb());
_reportsBloc.inAddReport.add(report);
}
void _navigateToReport(Report report) async {
bool update = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => BlocProvider(
bloc: ViewReportBloc(reportId: report.reportId),
child: ViewReportPage(
report: report,
),
),
),
);
if (update != null) {
_reportsBloc.getReports();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
drawer: NavMenu(),
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: StreamBuilder<List<Report>>(
stream: _reportsBloc.reports,
builder: (BuildContext context,
AsyncSnapshot<List<Report>> snapshot) {
//Make sure the data exists and is actually loaded
if (snapshot.hasData) {
//if there are no reports (data), display this message
if (snapshot.data.length == 0) {
return Text('No reports!');
}
List<Report> reports = snapshot.data;
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Report report = reports[index];
return GestureDetector(
onTap: () {
_navigateToReport(report);
},
child: Container(
child: CustomListTile(
title: report.firstName != null &&
report.lastName != null
? '${report.firstName} ${report.lastName}'
: ' ',
subtitle: report.reportId,
trailing: Icon(Icons.edit),
),
),
);
},
);
}
return Center(
child: CircularProgressIndicator(),
);
},
),
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _addReport,
child: Icon(Icons.add),
),
);
}
}
view_report.dart
class ViewReportPage extends StatefulWidget {
ViewReportPage({Key key, this.report, this.reportId}) : super(key: key);
final Report report;
final String reportId;
@override
_ViewReportState createState() => _ViewReportState();
}
class _ViewReportState extends State<ViewReportPage> with TickerProviderStateMixin {
TabController _tabController;
ViewReportBloc _viewReportBloc;
Report report;
TextEditingController _firstNameController = new TextEditingController();
TextEditingController _lastNameController = new TextEditingController();
TextEditingController _dobController = new TextEditingController();
TextEditingController _chiefComplaintController = new TextEditingController();
TextEditingController _incidentHxController = new TextEditingController();
TextEditingController _pmhxController = new TextEditingController();
TextEditingController _allergiesController = new TextEditingController();
TextEditingController _rxController = new TextEditingController();
@override
void initState() {
super.initState();
_viewReportBloc = BlocProvider.of<ViewReportBloc>(context);
_firstNameController.text = widget.report.firstName;
_lastNameController.text = widget.report.lastName;
_dobController.text = widget.report.dob;
_chiefComplaintController.text = widget.report.chiefComplaint;
_incidentHxController.text = widget.report.incidentHx;
_pmhxController.text = widget.report.pmhx;
_rxController.text = widget.report.rx;
_allergiesController.text = widget.report.allergies;
}
void _saveReport() async {
widget.report.firstName = _firstNameController.text;
widget.report.lastName = _lastNameController.text;
widget.report.dob = _dobController.text;
widget.report.chiefComplaint = _chiefComplaintController.text;
widget.report.incidentHx = _incidentHxController.text;
widget.report.pmhx = _pmhxController.text;
widget.report.allergies = _allergiesController.text;
widget.report.rx = _rxController.text;
_viewReportBloc.inSaveReport.add(widget.report);
Navigator.of(context).pop();
}
void _deleteReport() {
_viewReportBloc.inDeleteReport.add(widget.report.reportId);
_viewReportBloc.deleted.listen((deleted) {
if (deleted) {
Navigator.of(context).pop(true);
}
});
}
void _addProcedure() async {
Procedure procedure = new Procedure(reportId: widget.report.reportId);
_viewReportBloc.inAddProcedure.add(procedure);
}
Future _selectDOB(BuildContext context) async {
final DateTime picked = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(1900, 1, 1),
lastDate: DateTime.now());
setState(() {
String _dobString = '${picked.year}/${picked.month}/${picked.day}';
_dobController.text = _dobString;
});
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
initialIndex: 0,
child: Scaffold(
appBar: AppBar(
title: Text(
'Report ' + widget.report.id.toString() + widget.report.reportId,
style: TextStyle(
fontSize: 16,
),
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.save),
onPressed: _saveReport,
),
IconButton(
icon: Icon(Icons.delete),
onPressed: _deleteReport,
),
],
),
body: TabBarView(
controller: _tabController,
children: [
Form(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
ListTile(
leading: const Icon(Icons.person),
title: new TextFormField(
decoration: new InputDecoration(
hintText: 'First Name',
),
maxLines: 1,
controller: _firstNameController,
),
subtitle: TextFormField(
decoration: InputDecoration(
hintText: 'Last Name',
),
maxLines: 1,
controller: _lastNameController,
),
),
ListTile(
leading: const Icon(Icons.cake),
title: new TextFormField(
decoration: new InputDecoration(
hintText: 'DOB',
),
maxLines: 1,
controller: _dobController,
enabled: false,
),
trailing: IconButton(
icon: Icon(Icons.calendar_today),
onPressed: () => _selectDOB(context),
),
),
ListTile(
title: new TextFormField(
decoration: new InputDecoration(
hintText: 'Chief Complaint',
),
maxLines: 1,
controller: _chiefComplaintController,
),
),
ListTile(
title: new TextFormField(
decoration: new InputDecoration(
hintText: 'Incident Hx',
),
maxLines: null,
controller: _incidentHxController,
),
),
ListTile(
title: new TextFormField(
decoration: new InputDecoration(
hintText: 'Past Med. Hx',
),
maxLines: null,
controller: _pmhxController,
),
),
ListTile(
title: new TextFormField(
decoration: new InputDecoration(
hintText: 'Meds',
),
maxLines: null,
controller: _rxController,
),
),
ListTile(
title: new TextFormField(
decoration: new InputDecoration(
hintText: 'Allergies',
),
maxLines: null,
controller: _allergiesController,
),
),
],
),
),
),
Builder(builder: (context) {
return BlocProvider(
bloc: ViewReportBloc(reportId: widget.report.reportId), child: ProcedureList());
}),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: _addProcedure,
),
bottomNavigationBar: new TabBar(
tabs: <Widget>[
Tab(text: 'Report'),
Tab(text: 'Procedures'),
],
controller: _tabController,
labelColor: Colors.black,
),
),
);
}
}
view_report_bloc.dart
class ViewReportBloc implements BlocBase {
final _saveReportController = StreamController<Report>.broadcast();
StreamSink<Report> get inSaveReport => _saveReportController.sink;
final _deleteReportController = StreamController<String>.broadcast();
StreamSink<String> get inDeleteReport => _deleteReportController.sink;
final _reportDeletedController = StreamController<bool>.broadcast();
StreamSink<bool> get _inDeleted => _reportDeletedController.sink;
Stream<bool> get deleted => _reportDeletedController.stream;
ViewReportBloc({String reportId}) {
// Listen for changes to the stream, and execute a function when a change is made
_saveReportController.stream.listen(_handleSaveReport);
_deleteReportController.stream.listen(_handleDeleteReport);
// Retrieve all the procedures on initialization
getProcedures(reportId);
// Listens for changes to the addProcedureController and calls _handleAddProcedure on change
_addProcedureController.stream.listen(_handleAddProcedure);
}
void _handleSaveReport(Report report) async {
await DbProvider.db.updateReport(report);
}
void _handleDeleteReport(String reportId) async {
await DbProvider.db.deleteReport(reportId);
_inDeleted.add(true);
}
// Create a broadcast controller that allows this stream to be listened
// to multiple times.
final _proceduresController = StreamController<List<Procedure>>.broadcast();
// Input stream.
StreamSink<List<Procedure>> get _inProcedures => _proceduresController.sink;
// Output stream.
Stream<List<Procedure>> get procedures => _proceduresController.stream;
// Input stream for adding new procedures.
final _addProcedureController = StreamController<Procedure>.broadcast();
StreamSink<Procedure> get inAddProcedure => _addProcedureController.sink;
void getProcedures(String reportId) async {
List<Procedure> procedures = await DbProvider.db.getProcedures(reportId);
_inProcedures.add(procedures);
}
_handleAddProcedure(Procedure procedure) async {
await DbProvider.db.newProcedure(procedure);
}
@override
void dispose() {
_saveReportController.close();
_deleteReportController.close();
_reportDeletedController.close();
_proceduresController.close();
_addProcedureController.close();
}
}
bloc_provider.dart
abstract class BlocBase {
void dispose();
}
class BlocProvider<T extends BlocBase> extends StatefulWidget {
BlocProvider({
Key key,
@required this.child,
@required this.bloc,
}) : super(key: key);
final T bloc;
final Widget child;
@override
_BlocProviderState<T> createState() => _BlocProviderState<T>();
static T of<T extends BlocBase>(BuildContext context) {
final type = _typeOf<BlocProvider<T>>();
BlocProvider<T> provider = context.findAncestorWidgetOfExactType();
return provider.bloc;
}
static Type _typeOf<T>() => T;
}
class _BlocProviderState<T> extends State<BlocProvider<BlocBase>> {
@override
void dispose() {
widget.bloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}
来自 Database.dart
getProcedures(String reportId) async {
final db = await database;
var res = await db.query('procedure', where: 'reportId = ?', whereArgs: [reportId]);
List<Procedure> procedures = res.isNotEmpty ? res.map((procedure) => Procedure.fromJson(procedure)).toList() : [];
return procedures;
}
是因为TabBarView无法接受两个不同的流吗?我是Flutter中的Streams和BLoC的新手,所以如果这是实现它的正确方法,我不是100%。
任何帮助将不胜感激!预先感谢!
答案 0 :(得分:0)
Flutter 应该能够处理 TabBarView
上的多个流。
错误日志暗示 getter bloc
是在 null 上调用的。这可能来自 bloc_provider.dart 上的 provider
。确保在 BlocBase
上传递的值通过以保证 BlocProvider
具有所需的参数。