Using Chopper networking library with flutter_bloc library in flutter

Issue

I am using the Chopper library to invoke Rest APIs and flutter_bloc for my business logic.

My Chopper class

import 'package:chopper/chopper.dart';
import '../model/statement_res.dart';
import 'model_converter.dart';

part 'statement_api_service.chopper.dart';

@ChopperApi()
abstract class StatementApiService extends ChopperService {
  // 6
  @Get(
      path: 'getStatement')
  Future<Response<StatementRes>> getStatement({
    @Query() String AccountNo,
    @Query() String StartDate,
    @Query() String EndDate,
  });

  // 8
  static StatementApiService create() {
    // 9
    final client = ChopperClient(
      // 10
      baseUrl: 'SERVER_URL',
      interceptors: [HttpLoggingInterceptor()],
      converter: ModelConverter(),
      errorConverter: JsonConverter(),
      // 11
      services: [
        _$StatementApiService(),
      ],
    );
    // 12
    return _$StatementApiService(client);
  }

}

My model class… Future<Response> is like

class StatementRes {
  @JsonKey(name: 'StatusCode')
  String statusCode;
  @JsonKey(name: 'Desc')
  String desc;
  @JsonKey(name: 'Body')
  Body body; //Nested model class
}

Events class code:


import 'package:equatable/equatable.dart';

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

class GetStatement extends StatementEvent {
  final String accountNo;
  final startDate;
  final endDate;

  const GetStatement(this.accountNo, this.startDate, this.endDate);

  @override
  List<Object> get props => [accountNo, startDate, endDate];
}

State class code:

import 'package:equatable/equatable.dart';
import '../model/statement_res.dart';
import 'package:chopper/chopper.dart';

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

class StatementInitial extends StatementState {
  const StatementInitial();
  @override
  List<Object> get props => [];
}

class StatementLoading extends StatementState {
  const StatementLoading();
  @override
  List<Object> get props => [];
}

class StatementLoaded extends StatementState {
  final Response<StatementRes> statementRes;
  const StatementLoaded(this.statementRes);
  @override
  List<Object> get props => [statementRes];
}

class StatementError extends StatementState {
  final String message;
  const StatementError(this.message);
  @override
  List<Object> get props => [message];
}

and this is my Bloc(mapEventToState) code

import 'dart:async';
import 'package:bloc/bloc.dart';
import '../service/statement_api_service.dart';
import './bloc.dart';

class StatementBloc
    extends Bloc<StatementEvent, StatementState> {
  StatementBloc(StatementState initialState) : super(initialState);

  @override
  StatementState get initialState => StatementInitial();

  @override
  Stream<StatementState> mapEventToState(
    StatementEvent event,
  ) async* {
    yield StatementLoading();
    if (event is GetStatement) {
      try {
        final statementResponse =
            await StatementApiService.create()
                .getStatement(
                    AccountNo: event.accountNo,
                    StartDate: event.startDate,
                    EndDate: event.endDate);
        yield StatementLoaded(statementResponse);
      } catch (e) {
        yield StatementError("Failed to fetch Statement");
      }
    }
  }
}

I’m trying to call Bloc’s code from my UI like below:

class MyHomePage extends StatefulWidget {
  final String title;

  MyHomePage({Key key, this.title}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  StatementBloc stmtBloc;

  @override
  void initState() {
    super.initState();
    stmtBloc = StatementBloc(StatementInitial());
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    stmtBloc.add(
        GetStatement("1155328002", "09-OCT-2020", "19-OCT-2020"));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Testing - Bloc"),
      ),
      body: Container(
        padding: EdgeInsets.symmetric(vertical: 16),
        alignment: Alignment.center,
        child:
          BlocBuilder<StatementBloc, StatementState>(
          builder: (context, state) {
            if (state is StatementLoading) {
              print("#################");
              print("**********StatementLoading*************");
              print("#################");

              return buildLoading();
          } else if (state is StatementLoaded) {
              print("#################");
 ,print("**********StatementLoaded*************");
              print("#################");

              return buildColumnWithData(
                  context, state.sStatementRes.body.responseDesc);
            } else {
              print("#################");
              print("**********Else*************");
              print("#################");

              return buildLoading();
            }
          },
        ),
      ),
    );
  }

}

I am able to call the API successfully and able to get the response. But not able to display in the UI. Receiving below exception:

flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown building Container(center, padding: EdgeInsets(0.0, 16.0, 0.0,
flutter: 16.0)):                                                        
flutter:         BlocProvider.of() called with a context that does not contain a Cubit of type
flutter: StatementBloc.                                     
flutter:         No ancestor could be found starting from the context that was passed to
flutter: BlocProvider.of<StatementBloc>().                  
flutter:                                                                
flutter:         This can happen if the context you used comes from a widget above the BlocProvider.
flutter:                                                                
flutter:         The context used was: BlocBuilder<StatementBloc,
flutter: StatementState>(dirty, state: _BlocBuilderBaseState<StatementBloc,
flutter: StatementState>#4c48e(lifecycle state: created))   
flutter:                                                                
flutter:                                                                
flutter: The relevant error-causing widget was:                         
flutter:   Container                                                    
flutter:   file:/lib/views/my_home_page.dart:50:13

Please guide me on how to solve the above issue.

Solution

I think it is better to use ‘BlocBuilder’ as a child of "BlocProvider", like this:

BlocProvider(
     create: (context) => StatementBloc()..add(Fetch()),
     child: BlocBuilder<StatementBloc, StatementState>(
          builder: (mContext, state) {
               // your code
          }
     ),
),

and remove this line from ‘initState()’:

stmtBloc = StatementBloc(StatementInitial());

I hope this works.

Answered By – Saeed Fekri

Answer Checked By – Robin (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.