#!/bin/bash # ----------- # Cubit Generator Script # ----------- # Usage: ./cubit_generator.sh --class_name= --target_dir= [--auto_run_build=true|false] # Example: ./cubit_generator.sh --class_name=Chat --target_dir=lib/ui/pages --auto_run_build=false # # # chmod +x cubit_generator.sh # # # Read package name from pubspec.yaml PUBSPEC="pubspec.yaml" if [ -f "$PUBSPEC" ]; then PACKAGE_NAME=$(grep -m1 '^name:' "$PUBSPEC" | sed 's/name:\s*//' | tr -d '[:space:]') else PACKAGE_NAME="unknown" fi CLASS_NAME="" OUTPUT_DIR="" AUTO_RUN_BUILD="false" for arg in "$@"; do case $arg in --class_name=*) CLASS_NAME="${arg#*=}" ;; --target_dir=*) OUTPUT_DIR="${arg#*=}" ;; --auto_run_build=*) AUTO_RUN_BUILD="${arg#*=}" ;; esac done # If class name is not provided as an argument, prompt the user if [[ -z "$CLASS_NAME" ]]; then echo -n "📦 Enter the class name (e.g., chat, videos, call, follow_users): " read CLASS_NAME fi echo "className: $CLASS_NAME" echo "outputDir: $OUTPUT_DIR" echo "autoRunBuild: $AUTO_RUN_BUILD" echo "package: $PACKAGE_NAME" # Validate if [[ -z "$CLASS_NAME" ]]; then echo "❌ Feature name cannot be empty." exit 1 fi # Name conversions: snake_case, PascalCase, camelCase snake_case=$(echo "$CLASS_NAME" \ | sed -E 's/([a-z0-9])([A-Z])/\1_\2/g' \ | tr '[:upper:]' '[:lower:]' \ | tr -s ' _-' '_' \ | tr -cd '[:alnum:]_') pascal_case=$(echo "$CLASS_NAME" \ | awk -F '[_ -]' '{for (i=1; i<=NF; i++) if ($i!="") $i=toupper(substr($i,1,1)) substr($i,2)}1' OFS='') camel_case=$(echo "${pascal_case:0:1}" | tr '[:upper:]' '[:lower:]')${pascal_case:1} # Target dir (strip leading slash if present) OUTPUT_DIR="${OUTPUT_DIR#/}" target_dir="$OUTPUT_DIR/$snake_case" import_dir="${target_dir#lib/}" mkdir -p "$target_dir" # Files state_file="$target_dir/${snake_case}_state.dart" screen_file="$target_dir/${snake_case}_screen.dart" cubit_file="$target_dir/${snake_case}_cubit.dart" # -------------------------- # STATE TEMPLATE # -------------------------- cat > "$state_file" < "$cubit_file" < { ${pascal_case}Cubit() : super(${pascal_case}State(isLoading: true)); Future loadInitialData() async { try { emit(state.copyWith(isLoading: true)); // TODO your code here emit(state.copyWith(isLoading: false)); } catch (error) { safeEmit( state.copyWith( isLoading: false, userMessage: UserMessage.fromDynamic(error), ), ); } } } EOF # -------------------------- # SCREEN TEMPLATE # -------------------------- cat > "$screen_file" < ${pascal_case}ScreenState(); } class ${pascal_case}ScreenState extends State<${pascal_case}Screen> { final cubit = ${pascal_case}Cubit(); @override void initState() { cubit.loadInitialData(); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( body: BlocConsumer<${pascal_case}Cubit, ${pascal_case}State>( bloc: cubit, listener: (BuildContext context, ${pascal_case}State state) { if (state.routing != null) { goTo(state.routing!); } if (state.userMessage != null) { context.showUserMessage( message: state.userMessage!.message, type: state.userMessage!.type, ); } }, builder: (BuildContext context, ${pascal_case}State state) { if (state.isLoading) { return Center(child: CircularProgressIndicator()); } return buildBody(state); }, ), ); } Widget buildBody(${pascal_case}State state) { return ListView( children: [ // TODO your code here ], ); } // -------------------------------------------------------------- void goTo(Routing routing) { switch (routing.screenName) { case Screen.back: context.pop(); break; // TODO your code here default: context.go(routing.screenName, extra: routing.extra); } } } EOF # Remove conflicting outputs conflicts=( "$target_dir/${snake_case}_state.freezed.dart" "$target_dir/${snake_case}_view_model.g.dart" ) for file in "${conflicts[@]}"; do [ -f "$file" ] && rm "$file" done # Ask to run build_runner if [[ -z "$AUTO_RUN_BUILD" ]]; then echo -n "❓ Do you want to run build_runner now? (yes/no): " # shellcheck disable=SC2162 read run_build AUTO_RUN_BUILD="$run_build" fi if [[ "$AUTO_RUN_BUILD" == "true" || "$AUTO_RUN_BUILD" == "yes" ]]; then dart run build_runner build --delete-conflicting-outputs if [[ $? -eq 0 ]]; then echo "✅ build_runner finished successfully" else echo "❌ build_runner failed" fi else echo "⏭️ Skipped build_runner" fi echo "🎉 Feature \"$pascal_case\" generated in ${target_dir}"