felangel / bloc

A predictable state management library that helps implement the BLoC design pattern
https://bloclibrary.dev
MIT License
11.84k stars 3.4k forks source link

Bloc listener not responding #1640

Closed davidAg9 closed 4 years ago

davidAg9 commented 4 years ago
Screenshot 2020-08-19 at 9 08 15 AM

I implemented a Bloclistener on a button that calls a Finished registration event which save the patient to firebase and some of the fields to sembast(local database ) but after the event is dispatch in debug mode I see the state are emmitted but it seem the listener is not reacting to any of response declared nothing happens on the UI

davidAg9 commented 4 years ago

also I am using bloc 5.0.0 and flutter_bloc 5.0.0 since I am not yet familiar with cubit and bloc 6.0.0

felangel commented 4 years ago

Hi @davidAg9 👋 Thanks for opening an issue!

Can you verify that you only have a single instance of the bloc? I'm guessing you are creating multiple instances of the bloc and so the listener is listening to a different instance than the one you are interacting with. If that doesn't help, could you please provide a link to a sample app which reproduces the issue? Thanks 👍

davidAg9 commented 4 years ago

What exactly do you mean by instance of bloc ; do you mean create an bloc provider in on screen/ wigdet and creating another provider in a widget below it in another screen down the previous widget with the same bloc?

davidAg9 commented 4 years ago

I have invited to the repo to have a look at the issue

davidAg9 commented 4 years ago

`import 'dart:io';

import 'package:date_format/date_format.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flushbar/flushbar.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:image_picker/image_picker.dart'; import 'package:rokcare/bloc/patient_register_bloc/patient_registration_bloc.dart'; import 'package:rokcare/models/patient.dart'; import 'package:rokcare/repositories/firebase/storage.dart';

import 'components/bloodtypeDrop.dart';

class PatientForm extends StatefulWidget { @override _PatientFormState createState() => _PatientFormState(); }

class _PatientFormState extends State { final GlobalKey _patientFormKey = GlobalKey(); final GlobalKey _residenceFormKey = GlobalKey(); final GlobalKey _medicalInfoFormKey = GlobalKey();

String _img = ''; final picker = ImagePicker(); bool male = false; bool female = false; DateTime _selectedDate = DateTime.now(); String _selectedsex = ''; int index = 0; bool photoLoading = false; Patient pat = Patient(); @override Widget build(BuildContext context) { final String formatDob = formatDate(_selectedDate, [yyyy, '-', M, '-', dd]); return BlocProvider( create: (BuildContext context) => PatientRegistrationBloc(), child: BlocBuilder<PatientRegistrationBloc, PatientRegistrationState>( builder: (context, state) => SafeArea( child: DefaultTabController( initialIndex: index, length: 3, child: Scaffold( appBar: AppBar( title: Text( 'Medical Form', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), bottom: TabBar(tabs: [ Tab( text: 'Personal information', icon: Icon(Icons.person), ), Tab( text: 'Residence', icon: Icon(Icons.home), ), Tab( text: 'Medical Infomation', icon: Icon(Icons.local_hospital), ) ]), ), extendBody: true, body: TabBarView(children: [ Container( height: 380, child: Form( onChanged: () => _residenceFormKey.currentState.save(), key: _residenceFormKey, child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(10.0), child: Column( children: [ TextFormField( decoration: InputDecoration( labelText: 'Sur Name', border: OutlineInputBorder(), ), onSaved: (newValue) => pat.info.name.lastName = newValue, ), SizedBox( height: 10.0, ), TextFormField( decoration: InputDecoration( labelText: 'First Name', border: OutlineInputBorder()), onSaved: (value) => pat.info.name.firstName = value, ), SizedBox( height: 10.0, ), TextFormField( decoration: InputDecoration( labelText: 'Other names', border: OutlineInputBorder()), onSaved: (value) => pat.info.name.otherNames = value, ), SizedBox( height: 10.0, ), TextFormField( keyboardType: TextInputType.phone, decoration: InputDecoration( labelText: 'Phone Number', border: OutlineInputBorder()), onSaved: (value) => pat.info.contactInfo.phoneNumber = value, ), SizedBox( height: 10.0, ), TextFormField( keyboardType: TextInputType.emailAddress, decoration: InputDecoration( labelText: 'Alternative Email', border: OutlineInputBorder()), onSaved: (value) => pat.info.email = value, ), SizedBox( height: 10.0, ), Padding( padding: const EdgeInsets.only(right: 50), child: FlatButton( color: Colors.blueGrey[200], onPressed: () { if (Platform.isAndroid) { return androidDate(context); } else if (Platform.isIOS) { return iosDate(context); } }, child: ListTile( leading: Icon(Icons.calendar_today), title: Text('Date of Birth'), subtitle: Text('$formatDob'), trailing: Icon(Icons.arrow_drop_down_circle), ), ), ), SizedBox( height: 15.0, ), Align( alignment: Alignment.centerLeft, child: Text( 'Gender', style: TextStyle( fontSize: 20, color: Colors.grey[700], fontWeight: FontWeight.w600), ), ), SizedBox( height: 10.0, ), Row( children: [ GestureDetector( onTap: () { setState(() { male = true; female = false; _selectedsex = 'male';

                                  pat.info.sex = _selectedsex;
                                });
                              },
                              child: Container(
                                decoration: BoxDecoration(
                                    color: male
                                        ? Colors.teal[500]
                                        : Colors.grey[300],
                                    borderRadius: BorderRadius.all(
                                        Radius.circular(30))),
                                height: 60,
                                width: 120,
                                child: Center(
                                    child: Text(
                                  'Male',
                                  style: TextStyle(
                                      fontWeight: FontWeight.w500,
                                      fontSize: 22),
                                )),
                              ),
                            ),
                            SizedBox(
                              width: 30,
                            ),
                            GestureDetector(
                              onTap: () {
                                setState(() {
                                  male = false;
                                  female = true;
                                  _selectedsex = 'female';
                                  pat.info.sex = _selectedsex;
                                });
                              },
                              child: Container(
                                decoration: BoxDecoration(
                                    color: female
                                        ? Colors.teal[500]
                                        : Colors.grey[300],
                                    borderRadius: BorderRadius.all(
                                        Radius.circular(30))),
                                height: 60,
                                width: 140,
                                child: Center(
                                    child: Text(
                                  'Female',
                                  style: TextStyle(
                                      fontWeight: FontWeight.w500,
                                      fontSize: 22),
                                )),
                              ),
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
                ),
              ),
            ),
            Container(
              height: 100,
              child: Form(
                onChanged: () => _patientFormKey.currentState.save(),
                key: _patientFormKey,
                child: SingleChildScrollView(
                  child: Padding(
                    padding: const EdgeInsets.all(10.0),
                    child: Column(
                      children: <Widget>[
                        TextFormField(
                          decoration: InputDecoration(
                              labelText: 'Address 1',
                              border: OutlineInputBorder()),
                          onSaved: (value) =>
                              pat.info.contactInfo.addressLine = value,
                        ),
                        SizedBox(
                          height: 10.0,
                        ),
                        TextFormField(
                          decoration: InputDecoration(
                            labelText: 'Address 2',
                            border: OutlineInputBorder(),
                          ),
                          onSaved: (value) =>
                              pat.info.contactInfo.addressLine2 = value,
                        ),
                        SizedBox(
                          height: 10.0,
                        ),
                        TextFormField(
                          decoration: InputDecoration(
                              labelText: 'City',
                              hintText: 'Accra',
                              border: OutlineInputBorder()),
                          onSaved: (value) =>
                              pat.info.contactInfo.city = value,
                        ),
                        SizedBox(
                          height: 10.0,
                        ),
                        TextFormField(
                          keyboardType: TextInputType.emailAddress,
                          decoration: InputDecoration(
                              labelText: 'Region',
                              hintText: 'Greater Accra',
                              border: OutlineInputBorder()),
                          onSaved: (value) =>
                              pat.info.contactInfo.state = value,
                        ),
                      ],
                    ),
                  ),
                ),
              ),
            ),
            Container(
              height: 580,
              child: Form(
                key: _medicalInfoFormKey,
                onChanged: () => _medicalInfoFormKey.currentState.save(),
                child: SingleChildScrollView(
                  child: Padding(
                    padding: const EdgeInsets.all(12.0),
                    child: Column(
                      children: <Widget>[
                        SizedBox(
                          height: 10,
                        ),
                        Container(
                          decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(16),
                              border: Border.all(),
                              image: DecorationImage(
                                image: NetworkImage(_img ??
                                    'http://www.european-athletics.org/imgml/athletes/man-placeholder.jpg'),
                              )),
                          height: 86,
                          width: 102,
                          child: Stack(children: <Widget>[
                            Padding(
                              padding: const EdgeInsets.symmetric(
                                  vertical: 1, horizontal: 1),
                              child: Align(
                                alignment: Alignment.bottomRight,
                                child: GestureDetector(
                                  onTap: () async {
                                    setState(() {
                                      photoLoading = true;
                                    });

                                    await getImage()
                                        .then((value) => this.setState(() {
                                              photoLoading = false;
                                            }));
                                  },
                                  child: Container(
                                    decoration: BoxDecoration(
                                      borderRadius:
                                          BorderRadius.circular(20),
                                      color: Colors.blue,
                                    ),
                                    width: 32,
                                    height: 38,
                                    child: Center(
                                      child: Icon(
                                        Icons.add_a_photo,
                                        color: Colors.white,
                                      ),
                                    ),
                                  ),
                                ),
                              ),
                            ),
                          ]),
                        ),
                        Padding(
                          padding: const EdgeInsets.symmetric(vertical: 10),
                          child: photoLoading
                              ? CircularProgressIndicator()
                              : Text(
                                  'Select a Passport picture',
                                  style: TextStyle(
                                    fontWeight: FontWeight.bold,
                                    fontSize: 15,
                                  ),
                                ),
                        ),
                        Align(
                          alignment: Alignment.center,
                          child: BloodType(
                              patientBlood: pat.medicalInfo.bloodtype),
                        ),
                        SizedBox(
                          height: 10.0,
                        ),
                        TextFormField(
                          keyboardType: TextInputType.number,
                          decoration: InputDecoration(
                              labelText: 'Height(cm)',
                              border: OutlineInputBorder()),
                          onSaved: (value) =>
                              pat.medicalInfo.height = value,
                        ),
                        SizedBox(
                          height: 10.0,
                        ),
                        TextFormField(
                          keyboardType: TextInputType.number,
                          decoration: InputDecoration(
                              labelText: 'Weight(kg)',
                              border: OutlineInputBorder()),
                          onSaved: (value) =>
                              pat.medicalInfo.weight = value,
                        ),
                        SizedBox(
                          height: 10.0,
                        ),
                        TextFormField(
                          keyboardType: TextInputType.phone,
                          decoration: InputDecoration(
                            labelText: 'Emergency Contact',
                            border: OutlineInputBorder(),
                          ),
                          onSaved: (value) =>
                              pat.emergencyContact.number = value,
                        ),
                        SizedBox(
                          height: 10.0,
                        ),
                        TextFormField(
                          decoration: InputDecoration(
                            labelText: 'Emergency Contact name',
                            border: OutlineInputBorder(),
                          ),
                          onSaved: (value) =>
                              pat.emergencyContact.name = value,
                        ),
                        SizedBox(
                          height: 10.0,
                        ),
                        TextFormField(
                          decoration: InputDecoration(
                            labelText: 'Emergency Contact Relationship',
                            border: OutlineInputBorder(),
                          ),
                          onSaved: (value) =>
                              pat.emergencyContact.relation = value,
                        ),
                        SizedBox(
                          height: 16,
                        ),
                        BlocListener<PatientRegistrationBloc,
                            PatientRegistrationState>(
                          listener: (context, state) {
                            if (state is PFinishingRegistration)
                              return CircularProgressIndicator();
                            else if (state
                                is PRegistrationFinalizationFailure)
                              return Flushbar(
                                title: 'REGISTRATION FAILED',
                                message: 'Unable to Finish Registration',
                                icon: Icon(
                                  Icons.warning,
                                  color: Colors.red,
                                ),
                                duration: Duration(seconds: 4),
                                backgroundGradient: LinearGradient(
                                    colors: [Colors.white70, Colors.teal]),
                              );
                            else if (state is PFinishedRegistration)
                              return Navigator.of(context)
                                  .popAndPushNamed('/patientDashBoard');
                          },
                          child: GestureDetector(
                            onTap: () {
                              BlocProvider.of<PatientRegistrationBloc>(
                                  context)
                                ..add(PFinishRegistration(patient: pat));
                            },
                            child: Align(
                              alignment: Alignment.centerRight,
                              child: Container(
                                decoration: BoxDecoration(
                                    color: Colors.teal[300],
                                    borderRadius: BorderRadius.all(
                                        Radius.circular(30))),
                                height: 60,
                                width: 120,
                                child: Center(
                                    child: Text(
                                  'ENROLL',
                                  style: TextStyle(
                                      fontWeight: FontWeight.w500,
                                      fontSize: 22),
                                )),
                              ),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              ),
            ),
          ]),
        ),
      ),
    ),
  ),
);

}

androidDate(BuildContext context) async { showDatePicker( initialDate: DateTime.now(), firstDate: DateTime(1900), lastDate: DateTime.now(), context: context, initialEntryMode: DatePickerEntryMode.calendar, ).then((value) { this.setState(() { _selectedDate = value; pat.info.dob = value; }); }); }

iosDate(BuildContext context) { showModalBottomSheet( context: context, builder: (BuildContext builder) { return Container( height: MediaQuery.of(context).copyWith().size.height, color: Colors.white, child: Column( children: [ Flexible( child: CupertinoDatePicker( mode: CupertinoDatePickerMode.date, onDateTimeChanged: (picked) { if (picked != null && picked != _selectedDate) setState(() { _selectedDate = picked; pat.info.dob = picked; }); }, initialDateTime: _selectedDate, minimumYear: 1900, maximumYear: DateTime.now().year, ), ), FlatButton( child: Text('Done'), onPressed: () => Navigator.pop(context), color: Colors.green, ), ], ), ); }); }

Future getImage() async { photoLoading = true; File docImg = await FilePicker.getFile(type: FileType.image);

StorageRepository img = StorageRepository();
img
    .saveFile(docImg, StoragePaths.Avatars)
    .then((value) => this.setState(() {
          _img = value;
          pat.info.photoUrl = value;
          photoLoading = false;
        }));

} } `

narcodico commented 4 years ago

Hi @davidAg9 👋

Your code is incomplete and really hard for anyone to get it in a run-able state. Please create a minimal reproduction repository with your issue and share a link to it so we could run it locally 👍

davidAg9 commented 4 years ago

I have gone through and I sent only the widget of where the listener was implemented Do you want to to see how I implanted the bloc itself before implementation in the above widget ?,I have though given access to the repo to @felangel for review .

davidAg9 commented 4 years ago

Still waiting for response ,if you need help navigating to the bloc and the where the error appears to occurs please lemme know @felangel

felangel commented 4 years ago

Hi @davidAg9 I took a look and the issue is you are creating two different instances of the PatientRegistrationBloc

This means that the bloc that you are listening on is different from the bloc you are adding the events to. Please make sure to have a single instance of the bloc which you are adding events and consuming.

Closing for now but feel free to comment with additional questions and I'm happy to continue the conversation 👍

davidAg9 commented 4 years ago

thank you will take a look at it and give you a response

davidAg9 commented 4 years ago

you know its because of your overall character and commitment to the quality of your product that makes it so good .I am personally impressed and will be loyal to bloc as I believe its the best state management for flutter and should be implemented in other frameworks like django and the likes .

davidAg9 commented 4 years ago
Screenshot 2020-08-23 at 7 44 50 AM

this is what happens if I take the patientregistrationBloc implementation from the PatientSignup widget. I thought bloc works by a tree order so theoretically it should work since PatientSignUp widget is one level above the PatientForm so I don't get why it cannot find the instance because in learning the bloc I understood that once a bloc provider is instantiated any widget below where the bloc was instantiated can use that bloc .if that is not the case please enlighten me .(how then will a bloc be scope or global if I am having such behaviour in my project)

davidAg9 commented 4 years ago
Screenshot 2020-08-23 at 7 56 06 AM

this is how I wrote it

davidAg9 commented 4 years ago

It I believe I have unraveled the issue ,so the issue is from a Firebase document its checking and of which its returning null which in turn throws an exception and does not allow the listener to execute tho I don't understand why the failed state to did not execute I am attempting to fix the issue with firebase to see If it will respond

felangel commented 4 years ago

Glad to hear you found the problem! What was the exception and how are you planning to address it?