
How to Implement CI/CD Pipelines for Flutter, React Native and .NET MAUI

Continuous Integration and Continuous Deployment (CI/CD) pipelines have become essential for modern mobile app development. They automate testing, building, and deploying apps, ensuring consistent quality while reducing manual effort. This comprehensive guide will walk you through implementing effective CI/CD pipelines for three popular cross-platform frameworks: Flutter, React Native, and .NET MAUI.
Introduction to CI/CD for Mobile Development
CI/CD practices offer several significant benefits for mobile app developers:
- Faster release cycles: Automate time-consuming build and deployment processes
- Higher quality: Catch bugs early through automated testing
- Consistency: Ensure builds are reproducible across environments
- Team efficiency: Free developers from repetitive tasks
While the specific implementation details vary between frameworks, the core principles remain consistent: automate everything, test thoroughly, and deploy reliably.
Key CI/CD Platforms for Mobile Development
Before diving into framework-specific configurations, let's briefly examine the platforms we'll be working with:
GitHub Actions
- Tightly integrated with GitHub repositories
- Flexible workflow configuration via YAML
- Free tier for public repositories
- Growing mobile-specific action ecosystem
Bitrise
- Mobile-first CI/CD platform
- Extensive library of pre-built steps for mobile workflows
- Excellent support for code signing and provisioning
- Intuitive workflow editor
Azure DevOps
- Comprehensive DevOps toolchain
- Powerful integration with Microsoft services
- Flexible build agents and deployment targets
- Advanced pipeline configuration options
Let's now explore how to implement CI/CD for each framework.
Flutter CI/CD Implementation
Flutter's growing popularity for cross-platform development makes it an excellent candidate for CI/CD automation.
Setting Up GitHub Actions for Flutter
Create a .github/workflows/flutter-ci.yml file in your repository:
name: Flutter CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.19.0"
          channel: "stable"
      - name: Install dependencies
        run: flutter pub get
      - name: Analyze project source
        run: flutter analyze
      - name: Run tests
        run: flutter test
      - name: Build APK
        run: flutter build apk --release
      - name: Upload APK
        uses: actions/upload-artifact@v3
        with:
          name: release-apk
          path: build/app/outputs/flutter-apk/app-release.apk
Bitrise Configuration for Flutter
- Add your Flutter project to Bitrise
- Use the Flutter App workflow template
- Configure your workflow with these key steps:
# bitrise.yml snippet
workflows:
  primary:
    steps:
      - activate-ssh-key@4:
          run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
      - git-clone@6: {}
      - flutter-installer@0:
          inputs:
            - version: stable
      - cache-pull@2: {}
      - flutter-analyze@0:
          inputs:
            - project_location: "$BITRISE_FLUTTER_PROJECT_LOCATION"
      - flutter-test@0:
          inputs:
            - project_location: "$BITRISE_FLUTTER_PROJECT_LOCATION"
      - flutter-build@0:
          inputs:
            - project_location: "$BITRISE_FLUTTER_PROJECT_LOCATION"
            - platform: both
      - deploy-to-bitrise-io@2: {}
      - cache-push@2: {}
Azure DevOps Pipeline for Flutter
Create an azure-pipelines.yml file with:
trigger:
  - main
pool:
  vmImage: "ubuntu-latest"
steps:
  - task: FlutterInstall@0
    inputs:
      channel: "stable"
      version: "latest"
  - task: FlutterCommand@0
    inputs:
      projectDirectory: "."
      command: "pub"
      arguments: "get"
  - task: FlutterCommand@0
    inputs:
      projectDirectory: "."
      command: "analyze"
  - task: FlutterTest@0
    inputs:
      projectDirectory: "."
  - task: FlutterBuild@0
    inputs:
      projectDirectory: "."
      target: "aab"
      buildNumber: "$(Build.BuildNumber)"
  - task: PublishBuildArtifacts@1
    inputs:
      PathtoPublish: "$(Build.SourcesDirectory)/build/app/outputs/bundle/release/app-release.aab"
      ArtifactName: "android-bundle"
      publishLocation: "Container"
React Native CI/CD Implementation
React Native remains extremely popular for JavaScript developers looking to build cross-platform mobile apps.
GitHub Actions for React Native
Create a .github/workflows/react-native-ci.yml file:
name: React Native CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
jobs:
  build-android:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: "18"
      - name: Install dependencies
        run: npm ci
      - name: Run ESLint
        run: npx eslint .
      - name: Run Jest tests
        run: npm test
      - name: Setup Java JDK
        uses: actions/setup-java@v3
        with:
          distribution: "temurin"
          java-version: "17"
      - name: Build Android Release
        run: |
          cd android
          ./gradlew bundleRelease
      - name: Upload Android Bundle
        uses: actions/upload-artifact@v3
        with:
          name: app-release-bundle
          path: android/app/build/outputs/bundle/release/app-release.aab
Bitrise Configuration for React Native
Configure your bitrise.yml with:
# bitrise.yml snippet for React Native
workflows:
  primary:
    steps:
      - activate-ssh-key@4: {}
      - git-clone@6: {}
      - npm@1:
          inputs:
            - command: ci
      - npm@1:
          inputs:
            - command: test
      - android-build@1:
          inputs:
            - project_location: "$BITRISE_SOURCE_DIR/android"
            - module: "app"
            - variant: "release"
      - deploy-to-bitrise-io@2: {}
      - cache-push@2: {}
Azure DevOps Pipeline for React Native
Create an azure-pipelines.yml file:
trigger:
  - main
pool:
  vmImage: "macos-latest"
steps:
  - task: NodeTool@0
    inputs:
      versionSpec: "18.x"
    displayName: "Install Node.js"
  - script: |
      npm ci
    displayName: "Install dependencies"
  - script: |
      npm test
    displayName: "Run tests"
  - task: JavaToolInstaller@0
    inputs:
      versionSpec: "17"
      jdkArchitectureOption: "x64"
      jdkSourceOption: "PreInstalled"
  - script: |
      cd android
      ./gradlew bundleRelease
    displayName: "Build Android Release"
  - task: PublishBuildArtifacts@1
    inputs:
      PathtoPublish: "$(Build.SourcesDirectory)/android/app/build/outputs/bundle/release"
      ArtifactName: "android-release"
      publishLocation: "Container"
.NET MAUI CI/CD Implementation
Microsoft's .NET Multi-platform App UI (.NET MAUI) is the evolution of Xamarin.Forms, enabling you to build cross-platform apps with C# and XAML.
GitHub Actions for .NET MAUI
Create a .github/workflows/maui-ci.yml file:
name: .NET MAUI CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
jobs:
  build-android:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup .NET
        uses: actions/setup-dotnet@v3
        with:
          dotnet-version: "8.0.x"
      - name: Install MAUI Workload
        run: dotnet workload install maui
      - name: Restore dependencies
        run: dotnet restore
      - name: Build Android App
        run: dotnet build -f net8.0-android -c Release
      - name: Upload Android Artifacts
        uses: actions/upload-artifact@v3
        with:
          name: android-artifacts
          path: bin/Release/net8.0-android/**/*.apk
Bitrise Configuration for .NET MAUI
Configure your bitrise.yml with:
# bitrise.yml snippet for .NET MAUI
workflows:
  primary:
    steps:
      - activate-ssh-key@4: {}
      - git-clone@6: {}
      - nuget-restore@1: {}
      - script@1:
          inputs:
            - content: |-
                #!/bin/bash
                dotnet workload install maui
                dotnet build -f net8.0-android -c Release
      - deploy-to-bitrise-io@2: {}
Azure DevOps Pipeline for .NET MAUI
Create an azure-pipelines.yml file:
trigger:
  - main
pool:
  vmImage: "windows-latest"
variables:
  buildConfiguration: "Release"
steps:
  - task: UseDotNet@2
    inputs:
      packageType: "sdk"
      version: "8.0.x"
      includePreviewVersions: true
  - script: dotnet workload install maui
    displayName: "Install MAUI workload"
  - task: DotNetCoreCLI@2
    inputs:
      command: "restore"
      projects: "**/*.csproj"
      feedsToUse: "select"
  - task: DotNetCoreCLI@2
    inputs:
      command: "build"
      projects: "**/*.csproj"
      arguments: "-f net8.0-android -c $(buildConfiguration)"
  - task: PublishBuildArtifacts@1
    inputs:
      PathtoPublish: "bin/$(buildConfiguration)/net8.0-android"
      ArtifactName: "android-app"
      publishLocation: "Container"
Best Practices for Mobile CI/CD
Regardless of the framework you're using, follow these best practices:
- Code signing management: Use secure methods to manage certificates and profiles - GitHub Actions: Use secrets for storing credentials
- Bitrise: Use the Code Signing tab
- Azure DevOps: Use secure files and variable groups
 
- Environment-specific configurations: Separate dev, staging, and production builds - Use environment variables or build variants
- Implement feature flags for controlled rollouts
 
- Automated testing at multiple levels: - Unit tests for business logic
- Integration tests for component interactions
- UI tests for critical user flows
 
- Versioning strategy: - Implement semantic versioning
- Automate version bumping based on commit messages or branch names
 
- Distribution channels: - Configure automated deployments to app stores
- Set up beta testing programs (TestFlight, Google Play Beta)
- Consider internal distribution for QA teams
 
Common Challenges and Solutions
Managing iOS Certificates and Provisioning Profiles
Challenge: iOS code signing is notoriously complex.
Solution:
- Use fastlane match with a private Git repository
- Implement Bitrise's iOS Code Signing feature
- Store certificates and profiles securely in Azure KeyVault
Handling Long Build Times
Challenge: Mobile CI/CD pipelines can be time-consuming.
Solution:
- Implement caching for dependencies
- Use incremental builds when possible
- Consider parallelizing platform-specific builds
Ensuring Consistent Environments
Challenge: Different CI environments can lead to inconsistent builds.
Solution:
- Use containerization (Docker) where possible
- Pin dependency versions strictly
- Document environment requirements thoroughly
Conclusion
Implementing CI/CD pipelines for Flutter, React Native, and .NET MAUI requires initial investment but pays significant dividends through improved quality, faster releases, and reduced manual effort. Each framework has its unique considerations, but the fundamental CI/CD principles remain consistent.
By following the implementation guides and best practices outlined in this article, you can create robust automation for your mobile app development workflow, regardless of which cross-platform technology you choose. Start small, focus on the most impactful automation first, and gradually expand your pipeline as your team gains confidence in the process.
Remember that CI/CD is not just about tooling but also about fostering a culture of quality and automation within your development team. Encourage developers to write tests, fix broken builds promptly, and continuously improve the pipeline itself.
Happy building!
Ready to streamline your internal app distribution?
Start sharing your app builds with your team and clients today.
 No app store reviews, no waiting times.