This content originally appeared on DEV Community and was authored by Yashvant Singh
So this week started with me working on the buggy Interactive Book part of the app. We hit this error:
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞══
'package:flutter_markdown/src/builder.dart': Failed assertion: line 267 pos 12: '_inlines.isEmpty': is not true.
The widget causing it was:
SingleChildScrollView:file:///C:/Users/emper/Desktop/cv_app_gsoc/lib/ui/views/ib/ib_page_view.dart:501:22
This is an assertion failure in the flutter_markdown
package, and basically, the parser found some weird inline elements in our Markdown that it couldn’t handle. Either some broken tags or bad formatting. Now we were already using a sanitizeMarkdown()
function (I’d mentioned that in the last blog), to catch broken lines and tags but still, this popped up.
Digging deeper, I noticed this in our custom builders:
RegExp get pattern => RegExp(r'{:\s?(.+)\s?}');
This is for extracting content between {:
and }
. We had more such patterns, and I was trying all ways to fix things from the UI side wrapping in an error boundary, adding debug logs, etc.
Also ran into a null operator used on a null value
kind of thing, so I added proper checks for that as well.
But then… Hardik pinged
Hardik asked me to pause this bug-hunt for a while and work on releasing the app — a basic GitHub release
so others in the community can try it too.
This was literally the first time I was doing anything with CI/CD — had zero idea about it . So I just told my mentors: “Yeh we can do that, but I don’t have much knowledge on this… could you share something I can start with?”
Hardik shared this article:
Automating Flutter Builds with GitHub Actions: A Step-by-Step Guide
It was actually really well written and easy to understand. But in that article, they used a single file for the workflow.
In our project, we had three files: main.yml
, ci.yml
, and cd.yml
.
So I read more and turns out it’s perfectly fine. You use one file if your project is small, but for a bit more structure, people prefer separate files. In our case:
main.yml
: Just validates commit messages (we’re usingaction-conventional-commits
). Triggers onpull_request
.ci.yml
: Handles build, test, and code checks (formatting, analysis, coverage). Runs on every push/PR.cd.yml
: Manages release and deployment usingsemantic-release
,Fastlane
, GitHub Releases, etc. Triggers manually or onpush
tomaster
.
What I added
I added this part to build and upload the APK:
- name: Build APK (Android)
if: ${{ matrix.platform == 'ubuntu-latest' }}
run: |
flutter build apk --release \
--dart-define=FB_APP_ID=${{ secrets.FB_APP_ID }} \
--dart-define=FB_APP_NAME=${{ secrets.FB_APP_NAME }} \
--dart-define=GITHUB_OAUTH_CLIENT_ID=${{ secrets.GH_OAUTH_CLIENT_ID }} \
--dart-define=GITHUB_OAUTH_CLIENT_SECRET=${{ secrets.GH_OAUTH_CLIENT_SECRET }}
- name: Upload APK Artifact
if: ${{ matrix.platform == 'ubuntu-latest' }}
uses: actions/upload-artifact@v4
with:
name: android-apk-${{ github.run_number }}
path: build/app/outputs/apk/release/app-release.apk
if-no-files-found: error
Then I added the release section to automate GitHub release creation when code is pushed to master
:
release:
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
needs: build
runs-on: ubuntu-latest
steps:
- name: Download APK Artifact
uses: actions/download-artifact@v4
with:
name: android-apk-${{ github.run_number }}
path: ./
- name: Get Current Date
id: date
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: "v1.0.${{ github.run_number }}"
name: "Release v1.0.${{ github.run_number }} - ${{ steps.date.outputs.date }}"
body: |
Android APK release built from commit ${{ github.sha }}.
Build number: ${{ github.run_number }}
Build date: ${{ steps.date.outputs.date }}
Download and install the APK file below.
files: ./app-release.apk
draft: false
prerelease: false
generate_release_notes: true
To try what I learnt, I created a separate repo and tried out the learning here:
Learning GitHub Workflow
The PR : Enhance CI with automated releases #398
After this, Hardik made the release public in the community and shared the APK. And guess what — people actually downloaded and ran it!
And then… F-Droid?
Someone in the community — salmoneatenbybear
aka Aditya (I hope I’m right ) — mentioned F-Droid deployment. I’ll be honest, I didn’t know what F-Droid was at that moment.
So I looked it up.
Turns out:
F-Droid is a free and open-source app store for Android.
It doesn’t track users, hosts only FOSS apps, and is maintained by the community.
It’s kinda like Play Store, but for open-source-only apps. Very cool.
I tried checking the last PR related to it (or maybe it wasn’t a PR), but it didn’t really come to a conclusion. Still need to explore it more.
If you’re curious about how to contribute or deploy on F-Droid, here’s a good starting point: F-Droid Contribution Guide
Wrapping up…
So yeah — started the week trying to make Markdown behave, ended the week building CI/CD pipelines, publishing GitHub releases, and learning about app stores I never knew existed
Next up: probably explore more around F-Droid, improve release notes, and see if I can finally fix that Markdown crash for good.
Thanks for reading! If you have suggestions, feel free to drop them in comments
This content originally appeared on DEV Community and was authored by Yashvant Singh