Using a Mac? You might find www.synapsemac.com useful :)
Flutter · Dart · Mobile

Dev Notes by Harshana

Practical notes on building apps. Written while learning, kept short and useful.

Read the latest Meet the author

Tuesday, 11 November 2025

Adding Text-to-Speech in iOS is Easy | AVSpeechSynthesizer (AVFoundation)

 Ever wanted your app to talk to your users? Maybe to read out a new message, an article, or give a notification? It sounds hard, but on iOS, it’s surprisingly easy.

Apple has this all figured out. They give you a tool called AVFoundation (the same one used for playing audio and video) that does all the work. You don't need to build any AI or record any voices.

How It Works: The "Speaker" and the "Message"

You only need to know two things:

  1. AVSpeechSynthesizer: Think of this as the speaker. It's the object that actually does the talking.

  2. AVSpeechUtterance: This is the message. It's the piece of text you want the speaker to say


You just create the message (the text) and hand it to the speaker. That's it.

The Code (It's Really This Simple)

To make your app say "Hello Harshana," this is basically all the Swift code you need:


import AVFoundation

let speaker = AVSpeechSynthesizer() let message = AVSpeechUtterance(string: "Hello Harshana, this is easy!")

speaker.speak(message)



Why You Should Use It

  • Accessibility: This is the biggest win. It makes your app usable for people with visual impairments or reading difficulties

  • Convenience: Your users can listen to content while their hands are busy, like when they are driving, cooking, or working out.

  • Engagement: A voice can make an app feel more personal and interactive, especially in learning or guide-style apps.


 

Friday, 17 October 2025

"Great Thread Merge" in Flutter



Hey everyone,

If you're a Flutter dev like me, you've probably spent some time wrestling with platform channels. You know, that whole song and dance you have to do just to get a piece of information from the native side, like the battery level. It works, but it can feel a bit clunky, right?

Well, the Flutter team just put out a video about something they're calling "The Great Thread Merge," and trust me, it's a bigger deal than it sounds. It's a huge change to how Flutter works under the hood, and it’s going to make our lives a lot easier.

The Old Way

Think of it like this. In the past, your Dart code (your app's UI) was running in one room. The native platform code (the stuff specific to Android or iOS) was running in a completely separate room.

If your Dart code needed something from the native side, it had to send a message over, wait for a reply, and deal with all that async/await stuff using a Future. This is what platform channels do. It's like shouting through a wall to the person in the next room. It's slow, and sometimes things get lost in translation.

The New Way



With the thread merge, Flutter is basically tearing down the wall between those two rooms.

It's moving our Dart code onto the same main thread that the native platform code runs on.

So what does this mean for us?

  • Way Simpler Code: Remember needing a whole StatefulWidget and setState just to show the battery percentage? The video shows how after the merge, you can just call a function directly in your build method. No Future, no state management, nothing. it's just one line. How cool is that?

  • Less Native Code to Write: Because talking to the native side is so much cheaper and faster now, we can write more of our logic in Dart. i really hate having to write the same feature twice – once in Kotlin for Android and again in Swift for iOS. This change means we can avoid that more often.

  • Better Performance: Fewer "thread hops" (jumping between rooms) means things are just more efficient. This should lead to smoother and faster apps.

Who Needs to Pay Attention?

For most of us building regular Flutter apps, this is just good news. We'll start seeing simpler plugins and a better development experience over time.

& i think, If you're a plugin author, you do need to be a little careful. If you were using the platform thread to do heavy background work, you'll need to move that stuff to a proper Isolate now. Otherwise, you might freeze the app's UI.

My Thoughts

Honestly, i'm super excited about this. It's one of those deep-down changes that fixes a ton of small annoyances we've all faced. It shows the Flutter team is serious about making native integration as smooth as possible.

This is a huge win for Flutter's future!





Sunday, 7 September 2025

Compare objects in Dart

 This package solve pain of comparing objects in Dart. By default, even if two objects have the same data, they are not considered equal because Dart looks at where they are stored in memory.

This means you have to write a bunch of annoying, boilerplate code to compare every single property.

This is where the equatable package comes in.

https://pub.dev/packages/equatable

Instead of writing extra code, you just make your class extend Equatable and tell it which properties to check. Here’s a simple example:

class User extends Equatable { const User({required this.name, required this.age});

final String name; final int age;

@override List<Object> get props => [name, age]; }


Now, you can just use user1 == user2 and it will work perfectly. No more manual checks. It saves you time and prevents a lot of bugs, especially with state management. It's a small change with a huge payoff.

This is a good one to know. 

 this video on the equatable package useful : 




Setting up CI/CD for Flutter using GitHub Actions




Simply, a pipeline which builds, test, and maybe even release your app when you push code. That’s where CI/CD with GitHub Actions comes in.

In this article, i’ll walk you through how i setup CI/CD for a Flutter project using GitHub Actions. It’s not super complicated, but there are few things that can trip you if you are new.

Why CI/CD for Flutter?

Flutter is cross-platform. That means you probably want to build for Android, iOS, Web, maybe Desktop. Doing it manually is boring and error prone. CI/CD helps with:

  • Running unit/widget tests automatically.

  • Making sure your app builds on every push.

  • Generating release APKs or IPA files.

  • Catching bugs early instead of when client shouts at you.

Getting Started

All GitHub Actions needs is a workflow file inside .github/workflows/

Let’s create one:

.github/
 └── workflows/
     └── flutter_ci.yml

Inside flutter_ci.yml, we define jobs. Here is a simple example:

name: Flutter CI

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: "3.19.0"

      - name: Install dependencies
        run: flutter pub get

      - name: Analyze
        run: flutter analyze

      - name: Run tests
        run: flutter test

      - name: Build APK
        run: flutter build apk --release

iOS Builds

For Android, GitHub Actions is smooth. For iOS it’s tricky because you need a MacOS runner. You can change runs-on: macos-latest and then add:

- name: Build iOS
  run: flutter build ios --release --no-codesign

If you want to sign and upload to TestFlight, you’ll need to setup certificates, provisioning profiles and use fastlane.

Secrets & Keystores

Don’t push your keystore or API keys to GitHub repo. Use GitHub Secrets instead. Go to:

Repo Settings → Secrets → Actions → New repository secret

Then in workflow:

- name: Decode keystore
  run: echo "${{ secrets.ANDROID_KEYSTORE }}" | base64 --decode > android/app/key.jks

Deployments

You can go further and deploy automatically. Example:

  • Upload APK to Firebase App Distribution.
  • Push Web build to GitHub Pages.
  • Send IPA to TestFlight.

For Firebase distribution, add:

- name: Upload to Firebase
  uses: wzieba/Firebase-Distribution-Github-Action@v1
  with:
    appId: ${{ secrets.FIREBASE_APP_ID }}
    token: ${{ secrets.FIREBASE_TOKEN }}
    groups: testers
    file: build/app/outputs/flutter-apk/app-release.apk

Wrap Up

Setting up CI/CD for Flutter using GitHub Actions is not rocket science, but you need to play with configs until it works for your case. Start simple — just run tests and build. Then later add deployments.

Once setup, you never have to worry “did i forget to run tests?” or “is my build broken?” GitHub Actions will tell you before your client does.

Saturday, 10 May 2025

A Simple Fix for Big APKs

Ever noticed that your Flutter app's APK file is way too big? The simple flutter build apk command packs everything into one big file. This includes code for different types of phone processors, called ABIs. A user's phone only needs one of these, but it has to download the entire large file.

This is where the split-per-abi command comes in. You just add it to your usual build command:


  flutter build apk --split-per-abi.


 What this does is create a separate, smaller APK for each type of processor.


For example, you might get an APK just for arm64-v8a and another one for armeabi-v7a. Each file is much smaller because it only contains the code needed for that specific processor.

When you upload these smaller files to the Google Play Store, the store automatically sends the right version to each user's phone. This makes the download faster and saves a lot of data for your users.

You can find the new, smaller APKs in your project's 

build/app/outputs/apk/release/ 

folder. This command is a great way to make your app more efficient and user-friendly.


Source - Stackoverflow - https://stackoverflow.com/questions/49064969/flutter-apps-are-too-big-in-size

Friday, 14 February 2025

Error when adding Tailwind CSS to an existing React - vite project

Problem -
I'm working on a React project based on Vite. I want to add Tailwind CSS, and after following the steps below, I'm encountering an error. 1. npm install -D tailwindcss postcss autoprefixer Initialize Tailwind CSS 2. npx tailwindcss init -p The first command executed without errors. However, the second command resulted in the following error: npm error could not determine executable to run npm error A complete log of this run can be found in: C:\Users\HelloWorld\AppData\Local\npm-cache\_logs\2025-02-14T17_30_29_652Z-debug-0.log I tried by clear npm, Clear npm Cache It still didn't work. same error when run 2nd one.



Solution:

The installation of TailwindCSS v3 and v4 is different. You were expecting the v3 installation, but v4 is the new latest version. For v3 installation, use:

npm install -D tailwindcss@3

However, if you want to install v4, I recommend reviewing the installation guides, as there are significant breaking changes between v3 and v4.

npm install tailwindcss @tailwindcss/vite