For this exercise we are going to perform a step by step process of developing a web application with flutter from creating a flutter application, setting up the environment variables, setting up the git repository and configuring CI/CD pipeline for Flutter Web S3 deployment.
Step 1: Create a Flutter App
First create your flutter application by running this command.
flutter create yourapplication
Navigate to your application then run flutter run
.
In this application we are going to use environment variables so that we will be able to input dynamic data during our CI/CD build, in this case we are using envied package. to install run the following commands:
flutter pub add envied
flutter pub add --dev envied_generator
flutter pub add --dev build_runner
After installation our pubspec.yml should look like this.
name: flutter_app_s3
description: "A new Flutter project."
publish_to: "none"
version: 1.0.0+1
environment: sdk: ">=3.3.3 <4.0.0"
dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.6 envied: ^0.5.4+1
dev_dependencies: flutter_test: sdk: flutter build_runner: ^2.4.9 flutter_lints: ^3.0.0 envied_generator: ^0.5.4+1
flutter: uses-material-design: true
Why use environment variable?
Our flutter application may have different environment like development, test and production environment where our application will deployed to. Different environment may have its own API urls and other variable that are unique to each environment.
Our application might consuming an API that have url for dev, test and production environment, API url on our application must not be hardcoded, it must be store on an environment variable so that we will be able to change its value dependent on the environment where we want to deploy it.
Example:
- Dev environment:
APIBASEURL="https://dev.yourapiurl.com/"
- Test environment:
APIBASEURL="https://test.yourapiurl.com/"
- Prod environment:
APIBASEURL="https://api.yourapiurl.com/"
Inside the root of our application, create a file .env
then set the value to:
APIBASEURL=https://dev.yourapiurl.com/
Inside lib/env/ create a file env.dart
then set the value to:
import 'package:envied/envied.dart'; part 'env.g.dart'; @Envied(path: '.env')
abstract class Env { @EnviedField(varName: 'APIBASEURL', obfuscate: true) static String apiBaseUrl = _Env.apiBaseUrl ;
}
Now run:
dart run build_runner build
This will generate a file env.g.dart
alongside env.dart
we previously created.
Now we are ready to use the environment variable to our flutter application.
Inside our main.dart Statefull widget, import the env.dart
:
import 'package:flutter_app_s3/env/env.dart';
To get the value from our environment variable we can use this:
String apiBaseUrl = Env.apiBaseUrl ;
To display the value: (add this to your UI)
Text('Environment Variable: $apiBaseUrl '),
Step 2: Create a CodeCommit
This exercise uses CodeCommit as git repository but you can use your own like Github, BitBucket and etc.
- Go to CodeCommit then click Create repository
- Set the name ex: flutter-app-s3
- Open your repository click Clone URL (this will copy the url)
- On your local machine
run git clone pastetheurlhere
- Enter your CodeCommit credentials (Check on documentation if you don’t have one)
- On the root folder of your flutter project, run
git add .
git commit -m "first commit"
git push
Create 3 branches that will be used for our environments (dev, test, prod), run the following commands:
// Create a dev branch
git checkout -b "dev"
git push // Create a test branch
git checkout master // go back to master branch
git checkout -b "test"
git push // Create a production branch
git checkout master
git checkout -b "prod"
git push
git checkout master
these branches will be used on our CodePipeline for different type of environment. Every time there is a change detected to any of that branch, a deployment process will be triggered.
Step 3: Create an S3 bucket
this is where we are going to host our application.
- Go to S3 then click Create a bucket
- Add your S3 bucket name ex: flutter-app-s3-dev
- Uncheck Block all public access
- Disable Bucket Versioning
- Click Create Bucket
- Open your bucket name created
- Go to properties
- Go to Static website hosting then click Edit then set these settings. (You can change these settings if you want to add attach a domain name)
- Static website: Enabled
- Hosting type: Host a static website
- Index document: index.html
- Redirection rules: (leave empty for now)
- Save changes
- Go to Permissions then edit Bucket policy, this will allow our application to be accessed publicly. Note: replace yourbucketname with the bucket name you created ex: flutter-app-s3-dev
{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::flutter-app-s3-dev/*" } ]
}
Step 4: Create a CodeBuild
This will perform the build command of our application. (this will perform the build command we can do locally). To make sure that build doesn’t fail on build process, run “flutter build web” on your local machine to make sure that the build command will also run successful on CI/CD pipeline.
# Create project:
- Go to CodeBuild then click Create project
- Set your project name ex: flutter-app-s3-codebuild-dev
# Select your git repository and specific branch for this CodeBuild:
- On source set to AWS CodeCommit
- Select repository we previously created
- Select specific branch where this code build pulls its source code. (make sure that codecommit repository have 4 primary branched master, dev, test, prod)
- if you are building a CodeBuild for development environment, use dev branch. (Note: You have to create one CodeBuild for each environment)
# Environment settings:
- Provisioning model: On Demand
- Environment image: Managed image
- Compute: EC2
- Operating System: Ubuntu
- Runtime: Standard
- Image: aws/codebuild/standard:7.0
- Image version: Always use the latest image for this runtime version
- Service role: (you can create new or use existing one(manually created))
-
Additional Configuration: (expand this)
Set your environment variables (this must correspond to the environment variables we used on our flutter application)
Environment variable created here will be injected to our application during the build stage. Note: make sure that you put the correct environment variables and its values.
Name: APIBASEURL
Value: https://dev.yourbaseurl.com/
Type: Plaintext
Note: you can add as many environment variables as you want
# Set the buildspec will be used for this CodeBuild:
- Buildspec set to Use a buildspec file
- Buildspec name: buildspec.yml
- Artifacts set to No Artifact
- Leave everything default then click Create build project
Step 5: Create a buildspec
Inside the root folder of our flutter application create a buildspec.yml. Copy and paste the following script:
version: 0.2
phases: pre_build: commands: - echo Pre Build started on `date` - git clone https://github.com/flutter/flutter.git -b stable - export PATH="$PATH:`pwd`/flutter/bin" - flutter packages pub get - flutter config --enable-web build: commands: - echo Build started on `date` - dart run build_runner build - flutter build web --release post_build: commands: - echo Build completed on `date`
artifacts: files: - "**/*" discard-paths: no base-directory: "build/web"
Codebuild, performs the all the process we normally do on a manual process like installing flutter sdk, installing the packages, running commands like build and etc.
Step 6: Create a CodePipeline and the CodeDeploy
CodePipeline is responsible for the orchestration of the deployment process, it observes any any changes to our repository then creates a deployment process if so. After change is detected on the repository, the CodePipeline triggers the CodeBuild to perform all the scripts defined on buildspec.yml then uploads the artifacts on an S3 bucket for code versioning (note that this bucket is not the s3 bucket we created earlier). Once build is done, CodePipeline will trigger CodeDeploy to get the specific artifact previously created by the Codebuild then deploy it to specific environment like S3, EC2, ECS, EKS and etc.
# Create a CodePipeline
- Go to CodePipeline then click Create new pipeline
# Pipeline Settings
- Set your pipeline name ex: flutter-app-s3-pipeline-dev
- Execution mode: Queued (Pipeline type V2 required)
- Create new or use existing service role
- Leave other to default then click Next
# Add Source Stage
- Select AWS CodeCommit (you can use your own, ex Github)
- Select repository name (select the previously created repository)
- Select branch name (select specific branch you want this pipeline to observe)
- Leave everything default then click Next
# Add Build Stage
- Select AWS CodeBuild
- Select Region (Select the correct region your working on)
- Select Project Name (select the codebuild we previously created)
- Build type: Single build
- Click Next
# Add Deploy Stage
- Select Amazon S3
- Select Region (Select the correct region your working on)
- Select the S3 bucket we previously created
- Check Extract file before deploy
- Leave deployment path empty
- Click Next
- Review then click Create Pipeline
Summary
On this exercise, we performed the process of building an application and creating the deployment process. But we only created a single environment, in real world we have to create 3 or more branches for our application.
Here’s my environment settings
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.19.5, on Microsoft Windows [Version 10.0.19045.4529], locale en-PH)
[√] Windows Version (Installed version of Windows is version 10 or higher)
[√] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[√] Chrome - develop for the web
[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.9.0)
[√] Android Studio (version 2023.2)
[√] VS Code (version 1.90.1) [√] Connected device (3 available)
[√] Network resources