installNetworkModule function

Future<void> installNetworkModule(
  1. String projectPath
)

Implementation

Future<void> installNetworkModule(String projectPath) async {

  final flutterCmd = Platform.isWindows ? 'flutter.bat' : 'flutter';

  print('📦 Installing network dependencies...');

  await Process.run(
    flutterCmd,
    ['pub', 'add', 'dio', 'pretty_dio_logger', 'connectivity_plus'],
    workingDirectory: projectPath,
    runInShell: true,
  );
  final networkDirPath = p.join(projectPath, 'lib', 'core', 'network');

  safeCreateDir(networkDirPath);

  // ==========================
  // app_config.dart
  // ==========================

  final configDir = Directory(
    p.join(projectPath, 'lib', 'core', 'config'),
  );

  configDir.createSync(recursive: true);

  safeWriteFile(p.join(configDir.path, 'app_config.dart'),'''
class AppConfig {

  static late String baseUrl;

  static void initialize(String flavor) {

    switch (flavor) {

      case "dev":
        baseUrl = "https://dev-api.example.com";
        break;

      case "prod":
        baseUrl = "https://api.example.com";
        break;

      default:
        baseUrl = "https://dev-api.example.com";

    }

  }

}
''');

  // ==========================
  // api_service.dart
  // ==========================

  final servicesDir = Directory(
    p.join(projectPath, 'lib', 'core', 'services'),
  );

  servicesDir.createSync(recursive: true);

  safeWriteFile(p.join(servicesDir.path, 'api_service.dart'),'''
import '../config/app_config.dart';
import '../network/api_client.dart';

class ApiService {

  static final ApiClient client =
      ApiClient(AppConfig.baseUrl);

}
''');

  // ==========================
  // api_client.dart
  // ==========================

  safeWriteFile(p.join(networkDirPath, 'api_client.dart'),'''
import 'package:dio/dio.dart';
import 'api_interceptor.dart';
import 'api_response.dart';
import 'api_exception.dart';

class ApiClient {

  late final Dio _dio;

  ApiClient(String baseUrl) {

    _dio = Dio(
      BaseOptions(
        baseUrl: baseUrl,
        connectTimeout: const Duration(seconds: 30),
        receiveTimeout: const Duration(seconds: 30),
        headers: {
          "Content-Type": "application/json",
        },
      ),
    );

    _dio.interceptors.add(ApiInterceptor());

  }

  Future<ApiResponse> get(String path,{Map<String,dynamic>? query}) async {

    try {

      final response = await _dio.get(
        path,
        queryParameters: query,
      );

      return ApiResponse.success(response.data);

    } catch (e) {

      if(e is DioException && e.error is ApiException){
        return ApiResponse.error(e.error.toString());
      }

      return ApiResponse.error("Unexpected error occurred");

    }

  }

  Future<ApiResponse> post(String path,{dynamic data}) async {

    try {

      final response = await _dio.post(
        path,
        data: data,
      );

      return ApiResponse.success(response.data);

    } catch (e) {

      if(e is DioException && e.error is ApiException){
        return ApiResponse.error(e.error.toString());
      }

      return ApiResponse.error("Unexpected error occurred");

    }

  }

  Future<ApiResponse> put(String path,{dynamic data}) async {

    try {

      final response = await _dio.put(
        path,
        data: data,
      );

      return ApiResponse.success(response.data);

    } catch (e) {

      if(e is DioException && e.error is ApiException){
        return ApiResponse.error(e.error.toString());
      }

      return ApiResponse.error("Unexpected error occurred");

    }

  }

  Future<ApiResponse> delete(String path) async {

    try {

      final response = await _dio.delete(path);

      return ApiResponse.success(response.data);

    } catch (e) {

      if(e is DioException && e.error is ApiException){
        return ApiResponse.error(e.error.toString());
      }

      return ApiResponse.error("Unexpected error occurred");

    }

  }

  Future<ApiResponse> multipart(
    String path,
    Map<String,dynamic> fields,
    Map<String,MultipartFile> files,
  ) async {

    try {

      final formData = FormData.fromMap({
        ...fields,
        ...files,
      });

      final response = await _dio.post(
        path,
        data: formData,
        options: Options(contentType: "multipart/form-data"),
      );

      return ApiResponse.success(response.data);

    } catch (e) {

      if(e is DioException && e.error is ApiException){
        return ApiResponse.error(e.error.toString());
      }

      return ApiResponse.error("Unexpected error occurred");

    }

  }

  void setToken(String token){
    _dio.options.headers["Authorization"] = "Bearer \$token";
  }

}
''');

  // ==========================
  // api_endpoints.dart
  // ==========================

  safeWriteFile(p.join(networkDirPath, 'api_endpoints.dart'),'''
class ApiEndpoints {

  static const String login = "/login";
  static const String signup = "/signup";
  static const String profile = "/profile";

}
''');

  // ==========================
  // api_exception.dart
  // ==========================

  safeWriteFile(p.join(networkDirPath, 'api_exception.dart'),'''
class ApiException implements Exception {

  final String message;

  ApiException(this.message);

  @override
  String toString() => message;

}
''');

  // ==========================
  // api_response.dart
  // ==========================

  safeWriteFile(p.join(networkDirPath, 'api_response.dart'),'''
class ApiResponse<T> {

  final T? data;
  final String? message;
  final bool success;

  ApiResponse({
    this.data,
    this.message,
    this.success = true,
  });

  factory ApiResponse.success(T data) {

    return ApiResponse(
      data: data,
      success: true,
    );

  }

  factory ApiResponse.error(String message) {

    return ApiResponse(
      message: message,
      success: false,
    );

  }

}
''');

  // ==========================
  // api_interceptor.dart
  // ==========================

  safeWriteFile(p.join(networkDirPath, 'api_interceptor.dart'),'''
import 'package:dio/dio.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
import 'api_exception.dart';

class ApiInterceptor extends Interceptor {

  final PrettyDioLogger _logger = PrettyDioLogger(
    requestBody: true,
    responseBody: true,
  );

  @override
  void onRequest(
    RequestOptions options,
    RequestInterceptorHandler handler,
  ) {

    options.headers["Accept"] = "application/json";

    _logger.onRequest(options, handler);

  }

  @override
  void onResponse(
    Response response,
    ResponseInterceptorHandler handler,
  ) {

    _logger.onResponse(response, handler);

  }

  @override
  void onError(
    DioException err,
    ErrorInterceptorHandler handler,
  ) {

    final message = _handleError(err);

    handler.reject(
      DioException(
        requestOptions: err.requestOptions,
        error: ApiException(message),
      ),
    );

  }

  String _handleError(DioException error) {

    final response = error.response;
    final data = response?.data;

    final serverMessage = _extractServerMessage(data);

    if (serverMessage != null) {
      return serverMessage;
    }

    switch (error.type) {

      case DioExceptionType.connectionTimeout:
        return "Connection timeout. Please try again.";

      case DioExceptionType.sendTimeout:
        return "Request timeout. Please try again.";

      case DioExceptionType.receiveTimeout:
        return "Server is taking too long to respond.";

      case DioExceptionType.connectionError:
        final msg = error.message ?? "";

        if (msg.contains("SocketException")) {
          return "No internet connection.";
        }

        return "Unable to reach server.";

      case DioExceptionType.badResponse:

        final statusCode = response?.statusCode;

        switch (statusCode) {

          case 400:
            return "Invalid request.";

          case 401:
            return "Session expired. Please login again.";

          case 403:
            return "Access denied.";

          case 404:
            return "Requested resource not found.";

          case 422:
            return "Validation error.";

          case 500:
            return "Server error. Please try again later.";

          default:
            return "Something went wrong.";

        }

      case DioExceptionType.cancel:
        return "Request cancelled.";

      default:
        return "Unexpected error occurred.";

    }

  }

  String? _extractServerMessage(dynamic data) {

    if (data == null) return null;

    if (data is Map<String, dynamic>) {

      if (data["message"] != null) {
        return data["message"].toString();
      }

      if (data["error"] != null) {
        return data["error"].toString();
      }

      if (data["detail"] != null) {
        return data["detail"].toString();
      }

      if (data["errors"] != null) {

        final errors = data["errors"];

        if (errors is List && errors.isNotEmpty) {
          return errors.first.toString();
        }

        if (errors is Map && errors.isNotEmpty) {
          return errors.values.first.toString();
        }

      }

    }

    return null;
  }

}
''');

  // ==========================
  // network_info.dart
  // ==========================

  safeWriteFile(p.join(networkDirPath, 'network_info.dart'),'''
import 'package:connectivity_plus/connectivity_plus.dart';

class NetworkInfo {

  final Connectivity connectivity;

  NetworkInfo(this.connectivity);

  Future<bool> get isConnected async {

    final result = await connectivity.checkConnectivity();

    return result != ConnectivityResult.none;

  }

}
''');

  await _updateBootstrapForNetwork(projectPath);

  print('🌐 Enterprise network module generated successfully.');
}