For this exercise, we are going to perform a step-by-step process of developing a web application with Flutter, starting with creating a Flutter application, setting up the environment variables, setting up the git repository, and configuring the 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 the 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 environments, like development, testing, and production environments, depending on where it is deployed. Different environments may have their own API urls and other variables that are unique to each environment.
Our application might consume an API that has a URL for the development, test, and production environments. The API URL on our application must not be hardcoded; it must be stored in an environment variable so that we will be able to change its value depending 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 a git repository, but you can use your own, like Github, BitBucket, 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 in our CodePipeline for different types of environments. Every time there is a change detected in any of those branches, 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 for our application. (This will perform the build command we can do locally.) To make sure that build doesn't fail on the build process, run "flutter build web" on your local machine to make sure that the build command will also run successfully on the 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)
The environment variable created here will be injected into our application during the build stage. Note: Make sure that you input the correct environment variables and their values. In our case, we only created one environment variable, which we can set like this:.
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 all the processes we normally do on a manual basis, like installing Flutter SDK, installing packages, running commands like build, etc.
Step 6: Create a CodePipeline with CodeDeploy
CodePipeline is responsible for the orchestration of the deployment process; it observes any changes to our repository and then creates a deployment process if so. After a change is detected on the repository, the CodePipeline triggers the CodeBuild to perform all the scripts defined in buildspec.yml, then uploads the artifacts to an S3 bucket for code versioning (note that this bucket is not the S3 bucket we created earlier). Once the build is done, CodePipeline will trigger CodeDeploy to get the specific artifact previously created by the Codebuild and then deploy it to a specific environment like S3, EC2, ECS, EKS, 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
Step 7: Testing the app
- Push your latest code into your git repository.
git checkout master
git add .
git commit -m "your updates"
git push
- Go to CodeCommit then open your repository then
- Create a pull request from master->dev
- After successful pull request go to CodePipeline
- Open the CodePipeline you created for the development environment (you will notice a CI/CD process is being performed automatically)
- After successful deployment go to S3 (You will notice a new s3 bucket is created. Do not touch that bucket as it keeps all the version of your application)
- Open the S3 bucket you manually created before for the development environment
- Go to properties then scroll down to Static website hosting
- Click the link (you will be redirected to your app)
Summary
In this exercise, we performed the process of building an application and creating the deployment process. But we only created a single environment; in the real world, we have to create three 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
Top comments (0)