简体   繁体   English

如何在 BLoC 中使用 TabBarView?

[英]How can I use a TabBarView with BLoC?

First poster here!第一张海报来了! Not sure if I'm missing any info/snippets so let me know!不确定我是否遗漏了任何信息/片段,所以请告诉我!

Alright, so.好吧,所以。

I'm creating a first aid documentation app for our providers and I keep getting this error.我正在为我们的提供者创建一个急救文档应用程序,但我不断收到此错误。

NoSuchMethodError: The getter 'bloc' was called on null.
Receiver: null
Tried calling: bloc

Currently, a BLoC gets and builds a list of all the reports created on the user's device.目前,BLoC 获取并构建用户设备上创建的所有报告的列表。 Selecting one one of the reports pushes a detailed page of the report with the correct personal information (first name, last name, allergies, etc.).选择其中一个报告会推送报告的详细页面,其中包含正确的个人信息(名字、姓氏、过敏症等)。 This same page is divided by a TabBarView with a Report tab and a Procedures tab.同一个页面由一个 TabBarView 划分,带有一个Report选项卡和一个Procedures选项卡。 The Procedures tab shows the error message and I can't seem to figure out why. Procedures选项卡显示错误消息,我似乎无法弄清楚原因。 See code below:见下面的代码:

ReportsPage.dart 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 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 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 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;
    }
}

from Database.dart来自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;
  }

view_report in Report tab报告选项卡中的 view_report

view_report in Procedures tab过程选项卡中的 view_report

Is it because TabBarView can't accept two different streams?是不是因为 TabBarView 不能接受两个不同的流? I was newly introduced to Streams and BLoC in Flutter, so I'm not 100% if this was the right way to implement it.我刚接触 Flutter 中的 Streams 和 BLoC,所以我不是 100% 确定这是实现它的正确方法。

Any help would be appreciated!任何帮助,将不胜感激! Thanks in advance!提前致谢!

Flutter should be able to handle multiple Streams on TabBarView . Flutter 应该能够处理TabBarView上的多个流。

The error log implies that the getter bloc was called on null.错误日志暗示 getter bloc是在 null 上调用的。 This likely came from provider on bloc_provider.dart.这可能来自 bloc_provider.dart 上的provider Make sure that the values passed on BlocBase pushes through to guarantee that BlocProvider has the parameters needed.确保在BlocBase传递的值通过以保证BlocProvider具有所需的参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM