2

I am trying to display a basic screen via flutter Bloc library , where in a person can view the number of appointments he had for a particular day , by clicking the particular date in a Calendar widget .

Below is the code of the above mentioned screen

import 'dart:developer';

import 'package:chikitsalaya/doctor/bloc/doctor_appointment_bloc.dart';
import 'package:chikitsalaya/doctor/bloc/doctor_appointment_event.dart';
import 'package:chikitsalaya/doctor/bloc/doctor_appointment_state.dart';
import 'package:chikitsalaya/doctor/doctor_appointment_details_screen.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';

import '../common/common_ui.dart';
import '../payment/appointment_booking_payment.dart';

class DoctorAppointmentScreen extends StatelessWidget {

  const DoctorAppointmentScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (BuildContext context) => DoctorAppointmentBloc(DoctorAppointmentInitial()),
      child: Scaffold(
          backgroundColor: Colors.white,
          appBar: CommonUI(context).commonAppBar(),
          drawer: CommonUI(context).commonDrawer(),
          body: SafeArea(
            minimum: const EdgeInsets.all(16.0),
            child: Column(
              children: <Widget>[
                /// This is the Calendar Picker Widget , where doctor can pick a particular date and see what all appointments are present
                buildCalendarWidget(context),
                /// This is the BlocBuilder Widget for dynamically displaying all the appointments for a particular date picked above
                BlocBuilder<DoctorAppointmentBloc,DoctorAppointmentState>(
                    builder: (context,state){
                      if(state is DoctorAppointmentDatePicked){
                        List<dynamic> currentDayAppointmentDetails = state.appointmentDetails;
                        return buildDailyAppointmentWidget(context,currentDayAppointmentDetails,state.day);
                      }else if(state is DoctorAppointmentInitial){
                        /// This is the initial state , so the date naturally would be the current date
                        context.read<DoctorAppointmentBloc>().add(DoctorAppointmentScreenOnDateChanged(DateTime.now()));
                        /// This is required because BlocBuilder requires a mandatory Widget to be returned back
                        return const CircularProgressIndicator();
                      }else if(state is DoctorAppointmentScreenError){
                        log("Error from Backend "+state.exceptionMessage);
                        return ErrorWidget(state.exceptionMessage);
                      }else if(state is DoctorAppointmentScreenNoAppointmentFound){
                        return Text("No Appointment Found for "+state.pickedDate);
                      }else if(state is DoctorAppointmentDetailsScreenInitial){
                        log("Inside state DoctorAppointmentDetailsScreenInitial");
                        //TODO: User View Resolver for this transition
                        Navigator.push(context, MaterialPageRoute(builder: (context) => DoctorAppointmentDetailsScreen(appointmentId:state.appointmentId)));
                        return const CircularProgressIndicator();
                      }else{
                        return const CircularProgressIndicator();
                      }
                    }
                ),
              ],
            ),
          )
      ),
    );
  }

  Widget buildCalendarWidget(BuildContext context) {
    return BlocBuilder<DoctorAppointmentBloc,DoctorAppointmentState>(
      builder: (context,state) {
        return CalendarDatePicker(
            initialDate: DateTime.now(),
            firstDate: DateTime.now(),
            lastDate: DateTime(2024),
            onDateChanged: (dateTime) {
              log('DateTime picked is '+dateTime.toString());
              context.read<DoctorAppointmentBloc>().add(DoctorAppointmentScreenOnDateChanged(dateTime));
            });
      }
    );
  }

  Card buildDailyAppointmentWidget(BuildContext context,List<dynamic> currentDayAppointmentDetails, String day){
    return Card(
      elevation: 6,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
      child: Column(
        children: <Widget>[
          Text(day),
          Expanded(
            child: GridView.builder(
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: (currentDayAppointmentDetails.length>=3) ? 3 : currentDayAppointmentDetails.length
              ),
              itemCount: currentDayAppointmentDetails.length,
              shrinkWrap: true,
              itemBuilder: (context,index) {
                return Padding(
                      padding: const EdgeInsets.only(left: 10.0,top: 40.0,right: 10.0,bottom: 40.0),
                      child: BlocBuilder<DoctorAppointmentBloc,DoctorAppointmentState>(
                        builder: (context,state) {
                          return TextButton(
                            /*style: ElevatedButton.styleFrom(
                              primary: Colors.green,
                              onPrimary: Colors.white,
                              shadowColor: Colors.greenAccent,
                              elevation: 3,
                              shape: RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(32.0)),
                            ),*/
                            onPressed: () {
                              try{
                                log("Pressed button");
                                print("Pressed someting");
                                context.read<DoctorAppointmentBloc>().add(DoctorAppointmentScreenOnPressed(currentDayAppointmentDetails[index]['appointmentId']));
                              }on Exception catch (_,error){
                                log("Error is "+error.toString());
                              }
                            },
                            //label: Text(currentDayAppointmentDetails![index]!['time']!),
                            //backgroundColor: Colors.green,
                            child: Text(currentDayAppointmentDetails![index]!['time']!),
                          );
                        }
                      ),
                    );
              }),
            ),
        ],
      ),
    );
  }

}

The below code is running fine , and even the appointmentList items are getting presented properly in a GridView . But on Pressing any individual appointment date, the "onPressed" function is not getting called . And it even includes the "print" and "log" lines .

onPressed: () {
                              try{
                                log("Pressed button");
                                print("Pressed someting");
                                context.read<DoctorAppointmentBloc>().add(DoctorAppointmentScreenOnPressed(currentDayAppointmentDetails[index]['appointmentId']));
                              }on Exception catch (_,error){
                                log("Error is "+error.toString());
                              }
                            },

I have also tried using "ElevatedButton & FloatingActionButton" instead of the existing "TextButton" ,but nothing seems to be working . And it seems as if buttons are getting disabled by default.

I am really feeling stuck over here , and any help would be appreciated .

I am also providing the Bloc , BlocState and BlocEvent codes

Bloc

import 'dart:async';
import 'dart:developer';
import 'package:chikitsalaya/appointment/appointment_service.dart';
import 'package:chikitsalaya/common/user_service.dart';
import 'package:chikitsalaya/doctor/bloc/doctor_appointment_event.dart';
import 'package:chikitsalaya/doctor/bloc/doctor_appointment_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';

class DoctorAppointmentBloc extends Bloc<DoctorAppointmentEvent,DoctorAppointmentState>{

  final AppointmentService _appointmentService = AppointmentServiceImpl();
  final UserService _userService = UserServiceImpl();

  DoctorAppointmentBloc(DoctorAppointmentState initialState) : super(initialState){
    on<DoctorAppointmentScreenOnDateChanged>(_onDateChanged);
    on<DoctorAppointmentDetailsScreenOnInit>(_onInitDetailsScreen);
    on<DoctorAppointmentScreenOnPressed>(_onPressed);
  }

  /// OnDateChanged , need to fetch all the monthly appointments from backend
  FutureOr<void> _onDateChanged(event,emit) async{
    DateTime pickedDate = event.dateTime;
    //TODO: Get the month of the pickedDate and pass it as a parameter for fetching data
    String formattedDate = DateFormat('dd-MM-yyyy').format(pickedDate);
    _appointmentService.fetchMonthlyAppointments("doctorId", "month")
        .then((appointmentList) {
          bool found = false;
          for(int i=0;i<appointmentList.length;i++){
            if(appointmentList[i].date==formattedDate){
              found = true;
              String weekDay = DateFormat('EEEE').format(pickedDate);
              emit(DoctorAppointmentDatePicked(appointmentDetails: appointmentList[i].appointments,
                  day: weekDay));
            }
            if(!found){
              log("No appointment found for date "+formattedDate);
              emit(DoctorAppointmentScreenNoAppointmentFound(formattedDate));
            }
          }
        }).onError((error, stackTrace) {
          log("In DoctorAppointmentScreenBloc error "+error.toString());
          log("In DoctorAppointmentScreenBloc stacktrace "+stackTrace.toString());
          emit(DoctorAppointmentScreenError(error.toString()));
        });
  }


  FutureOr<void> _onInitDetailsScreen(event, emit) async{
    String appointmentId = (event as DoctorAppointmentDetailsScreenOnInit).appointmentId;
    _appointmentService.fetchAppointmentDetails(appointmentId)
        .then((appointment) {
          log("Appointment Found for Id "+appointmentId+" "+appointment.toString());
          /// After fetching appointment details , need to fetch Patient details via Patient API
          String patientId = appointment.patientId;
          _userService.fetchPatientDetails(patientId)
              .then((patient) {
                log("Patient details found for Id "+patientId+" "+patient.toString());
                appointment.patientName=patient.patientName;
                appointment.patientAge=patient.patientAge;
                appointment.patientSex=patient.patientSex;
                emit(DoctorAppointmentDetailsScreenSuccess(appointment));
              }).onError((error, stackTrace) {
                log("Failed to fetch Patient details for Id "+patientId+" "+stackTrace.toString());
                emit(DoctorAppointmentDetailsScreenError(error.toString()));
              });
        }).onError((error, stackTrace) {
          log("Failed to fetch Appointment details for Id "+appointmentId+" "+stackTrace.toString());
          emit(DoctorAppointmentDetailsScreenError(error.toString()));
        });
  }

  FutureOr<void> _onPressed(event, emit) async{
    String appointmentId = (event as DoctorAppointmentScreenOnPressed).appointmentId;
    log("Inside AppointmentBloc OnPressed "+appointmentId);
    emit(DoctorAppointmentDetailsScreenInitial(appointmentId));
  }
}

BlocEvent

import 'package:equatable/equatable.dart';

abstract class DoctorAppointmentEvent extends Equatable {
  const DoctorAppointmentEvent();
}

class DoctorAppointmentStarted extends DoctorAppointmentEvent {
  @override
  List<Object?> get props => [];

}

class DoctorAppointmentScreenOnDateChanged extends DoctorAppointmentEvent {
  final DateTime dateTime;
  //TODO: Add Doctor ID as variable here

  const DoctorAppointmentScreenOnDateChanged(this.dateTime);

  @override
  List<Object?> get props => [dateTime];

}

class DoctorAppointmentScreenOnPressed extends DoctorAppointmentEvent {
  final String appointmentId;

  const DoctorAppointmentScreenOnPressed(this.appointmentId);

  @override
  List<Object?> get props => [appointmentId];

}

class DoctorAppointmentDetailsScreenOnInit extends DoctorAppointmentEvent {
  final String appointmentId;

  const DoctorAppointmentDetailsScreenOnInit(this.appointmentId);

  @override
  List<Object?> get props => [];

}

BlocState

import 'package:chikitsalaya/appointment/appointment_model.dart';
import 'package:equatable/equatable.dart';

abstract class DoctorAppointmentState extends Equatable {
  const DoctorAppointmentState();
}

class DoctorAppointmentInitial extends DoctorAppointmentState {

  const DoctorAppointmentInitial();

  @override
  List<Object?> get props => [];
}

class DoctorAppointmentDatePicked extends DoctorAppointmentState {

  final List<dynamic> appointmentDetails;
  final String day;
  const DoctorAppointmentDatePicked({required this.appointmentDetails,required this.day});

  @override
  List<Object?> get props => [appointmentDetails,day];

}

class DoctorAppointmentScreenError extends DoctorAppointmentState {
  final String exceptionMessage;

  const DoctorAppointmentScreenError(this.exceptionMessage);

  @override
  List<Object?> get props => [exceptionMessage];
}

class DoctorAppointmentScreenNoAppointmentFound extends DoctorAppointmentState {
  final String pickedDate;

  const DoctorAppointmentScreenNoAppointmentFound(this.pickedDate);

  @override
  List<Object?> get props => [pickedDate];
}

class DoctorAppointmentDetailsScreenInitial extends DoctorAppointmentState {
  final String appointmentId;

  const DoctorAppointmentDetailsScreenInitial(this.appointmentId);

  @override
  List<Object?> get props => [appointmentId];

}

class DoctorAppointmentDetailsScreenSuccess extends DoctorAppointmentState {
  final Appointment appointment;

  const DoctorAppointmentDetailsScreenSuccess(this.appointment);

  @override
  List<Object?> get props => [appointment];
}

class DoctorAppointmentDetailsScreenError extends DoctorAppointmentState {
  final String exceptionMessage;

  const DoctorAppointmentDetailsScreenError(this.exceptionMessage);

  @override
  List<Object?> get props => [exceptionMessage];
}
animo3991
  • 181
  • 2
  • 9
  • 1
    You might want to avoid using the ! operator on lines like: `child: Text(currentDayAppointmentDetails![index]!['time']!),` – John Weidner Apr 08 '23 at 23:48
  • Hi @JohnWeidner , I have also tried doing that , but still it's not working . It's behaving as if the button is disabled – animo3991 Apr 09 '23 at 13:02
  • The problem has to do with how GridView works. I've reproduced the problem and am trying to understand how you would do what you are wanting to do with GridView. I think GridView normally tries to handle scrolling so it is probably preventing the tap gesture from getting to your button. – John Weidner Apr 09 '23 at 18:58
  • If you think GridView might be the problem , then i can replace it with some other Widget . But i don't have much idea about any alternatives over here . So , can you please suggest some – animo3991 Apr 10 '23 at 10:24

2 Answers2

0

The problem has to do with the GridView. If you have the GridView's itemBuilder return an InkWell with your desired child widget, you'll be able to add an onTap event on the InkWell to call your

context.read<DoctorAppointmentBloc>().add(DoctorAppointmentScreenOnPressed(currentDayAppointmentDetails[index]['appointmentId']));`
John Weidner
  • 2,055
  • 3
  • 18
  • 31
  • I tried your approach , and returned an InkWell inside the itemBuilder. I also added an onTap event as mentioned . But it still isn't working . – animo3991 Apr 10 '23 at 10:21
  • Please show your updated GridView code. The GridView's itemBuilder should return the InkWell. The InkWell's child would be the Padding who's child would be the BlocBuilder and the BlocBuilder's builder would return a Text widget. – John Weidner Apr 10 '23 at 13:33
  • I have created the below Gist for the same :- https://gist.github.com/animo93/802f2555f356134460cc12dbe04a24d9 – animo3991 Apr 10 '23 at 18:57
  • I'll try to look into this more tonight. I'd suggest isolating the issue by making a standalone program that demonstrates the problem so that you can easily share it with others to investigate. – John Weidner Apr 11 '23 at 15:43
0

It seems you are not allowed to add a clickable card inside a parent card .
So , the moment i changed my parent widget to return a Column instead of a card , it worked .

So , in short
Old Code

return Card(
  elevation: 6,
  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
  child: Column(

Working Code

return Column(
    children: <Widget>[
      Text(day),
      Expanded(
animo3991
  • 181
  • 2
  • 9