Skip to content

Instantly share code, notes, and snippets.

@ehbc221
Last active June 8, 2023 16:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ehbc221/efb76b87ad90aa5b7953d8207075681e to your computer and use it in GitHub Desktop.
Save ehbc221/efb76b87ad90aa5b7953d8207075681e to your computer and use it in GitHub Desktop.
A wrapper for all your repositories in Flutter. Used to parse responses from your datasources (remote; ie: API / local; ie: SqfLite) with a unified Resource class, making it easy to have the same logic throughout your application UIs.
import 'dart:async';
import 'package:expenza/constants/global.constants.dart';
import 'package:expenza/domain/resource.dart';
import 'package:expenza/utils/log.utils.dart';
const String _tag = 'BaseRepository';
/// Base Repository that wraps all the app's repositories.
///
/// Used to store some repetitive methods.
///
/// - author - @ehbc221
/// - version - 1.0.0
/// - last updated - June 8th, 2023
abstract class BaseRepository {
/// Wrapper for basic remote datasource calls.
///
/// Returns Success or Error depending on the Api call.
Future<Resource<Res>> remoteDatasourceCall<Req, Res>({
Future<Resource<Res>> Function(Req)? datasourceCall,
Future<Resource<Res>> Function()? datasourceVoidCall,
required StreamController<BaseStatus> controller,
Req? data,
}) async {
logD(_tag, 'remoteDatasourceCall: called.');
Resource<Res> response = ResourceError<Res>(
message: defaultErrorMessage,
);
try {
// Send request
response = await ((data != null && datasourceCall != null
? datasourceCall.call(data)
: (datasourceVoidCall != null
? datasourceVoidCall.call()
: ResourceError<Res>(
message:
'Impossible to call this resource. Incomplete function.',
))) as Future<Resource<Res>>);
// Check response and send emit corresponding event
if (response is ResourceSuccess<dynamic>) {
controller.add(BaseStatus.success);
} else {
controller.add(BaseStatus.error);
}
return response;
} catch (exception) {
logW(_tag, 'Catch exception: $exception');
controller.add(BaseStatus.error);
return response;
}
}
/// Base DAO that wraps all the app's DAOs.
///
/// Used to handle calls and error handling.
Future<Resource<Res>> localDatasourceCall<Req, Res>({
Future<Res> Function(Req)? datasourceCall,
Future<Res> Function()? datasourceVoidCall,
required StreamController<BaseStatus> controller,
Req? data,
}) async {
logD(_tag, 'localDatasourceCall: called.');
Resource<Res> response = Resource<Res>.error(
detail: defaultErrorMessage,
);
try {
// Send request
if (data != null && datasourceCall != null) {
Res result = await datasourceCall.call(data);
controller.add(BaseStatus.success);
return Resource<Res>.success(
data: result,
isFromNetwork: false,
);
} else if (datasourceVoidCall != null) {
Res result = await datasourceVoidCall.call();
controller.add(BaseStatus.success);
return Resource<Res>.success(
data: result,
isFromNetwork: false,
);
} else {
controller.add(BaseStatus.error);
return Resource<Res>.error(
isNetworkError: false,
message: 'Impossible to call this resource. Incomplete function.',
);
}
} catch (exception) {
logW(_tag, 'Catch exception: $exception');
controller.add(BaseStatus.error);
return response;
}
}
}
@ehbc221
Copy link
Author

ehbc221 commented Jun 8, 2023

Example

Assuming you want to switch between your remote and local datasources, depending on the network status of the user (online / offline).
You can call the repository and it handles formatting the response with the Resource wrapper class.

/// Create a budget.
  Future<Resource<Budget>> create(
    Budget budget, {
    bool isOffline = true,
  }) async {
    logD(_tag, 'create called.');
    if (isOffline) {
      return localDatasourceCall<Budget, Budget>(
        controller: _controller,
        datasourceCall: budgetDao.create,
        data: budget,
      );
    } else {
      return remoteDatasourceCall<Budget, Budget>(
        controller: _controller,
        datasourceCall: budgetDatasource.create,
        data: budget,
      );
    }
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment