Open-source project
moffatman/chan avatar
moffatman/chan

Chance: a Flutter imageboard browser for iOS and Android

Flutter imageboard browser for iOS and Android

537 stars28 forksDartGPL-3.0

At a glance

What is it?
Chance is a Flutter imageboard client with multi-column layouts, a media gallery, saved collections, regular-expression filters, archive support and a thread watcher, and it ships a deliberately forked engine so it can keep up with site changes. It is not in the App Store and needs an experimental Dart flag to build.
Who is it for?
Adopt Chance if you want an imageboard browser on iOS or Android with features no generic browser gives you, such as saved collections, regex filters and a thread watcher. Do not expect it to build on a stable Flutter toolchain, because the project requires the flutter master branch and the --enable-experiment=records flag.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Dart, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Chance is, and the feature list that defines its scope

Chance is an imageboard browser built using Flutter, intended for use on iOS and Android. That single sentence hides most of what you need to know, so the feature list is where the real shape appears.

The list is unusually specific about the things that make an imageboard client different from a web browser. Multi-column layout for larger screens, WEBM playback and conversion, and the ability to pick images through an in-app web search are all things a normal browser cannot do for you inside the app. A media gallery view and the ability to save threads, posts and attachments to a local collection are the other two that matter.

Then there is a set of features that are less about viewing and more about handling the social layer of an imageboard. Gestures to navigate through replies, the ability to switch between multiple browsing tabs, and a thread watcher to check for replies. Regular-expression filters to hide or highlight posts are the one that stands out, because it turns the client from a reader into something you can actually moderate your own view with.

Two entries describe interaction rather than capability. Automatically aligns the captcha slider is a convenience feature for imageboards with a slider captcha. And optional and automatic adjustments for mouse usage suggests the app is designed for both touch and pointer input rather than only being a phone app.

Support for archives to access deleted threads or search for posts is the last item, and it matters for a particular reason: on an imageboard, content disappears. Threads get deleted, posts get filtered, and the archive is the only remaining copy. A client that can read an archive is not just a convenience, it is the difference between having a record of something and not having one.

The primary language is Dart, the licence is GPL-3.0, the default branch is master, and the recent releases are v1.2.9+125-forked-engine from 2026-09-28, v1.2.9+124-forked-engine from 2026-08-28 and v1.2.9+122-forked-engine from 2026-08-26. The last push was on 2026-10-17, so the project is being worked on.

Those release names are informative on their own. Every recent tag carries the same suffix, forked-engine, which tells you the release cadence is tied to changes in the underlying engine rather than to new user-facing features.

The forked engine, and why the release names say so

Every recent release tag ends in forked-engine, and that is the most informative thing about the project's versioning.

An imageboard client does not have a stable API to depend on. The sites it reads are run by people, the markup changes when a theme is updated, and the endpoints a client relies on get renamed or restructured. A client that tracks upstream conventions directly breaks every time the site changes.

So the engine is forked. The repository tree shows the pieces. There is a go/ directory, which is where a Go-based engine would live, and a Chance.user.js file at the root, which is a userscript. Both suggest that the client is not purely a Flutter reimplementation but wraps or extends an existing engine, and the fork is the project's answer to the churn problem described above.

The go/ directory is also the reason this project can support the archive feature and the regular-expression filters in a way a thin client could not. Doing the work server-side, in an engine you control, is what lets the app treat content as structured data rather than as HTML to be scraped. It also means the regular-expression filters can run over parsed posts rather than over markup, which is the difference between a filter that hides a post and one that hides a paragraph inside a post.

That said, the README does not describe the engine's internals, so the exact boundary between the Flutter UI and the forked engine is not something the material settles. What is clear is that the fork is a maintained artefact, since the release counters move with it, and that a fork means the app's behaviour is pinned to a snapshot of the engine rather than following upstream automatically.

The practical consequence for anyone evaluating it is that the release number tells you the engine revision, not the app's feature set. Three of the last four releases moved the engine counter and nothing else, so a version bump in the app tells you very little about what changed in the client.

Building it: Flutter master, an experiment flag and generated code

The build instructions are unusually honest about the toolchain requirements, which saves a lot of time.

The first step is to install Flutter. Then clone and enter the repository.

bash
git clone https://github.com/moffatman/chan.git
cd chan

Fetch the dependencies next.

bash
flutter pub get

Then an optional step that matters more than it looks: modify the package name, find-and-replace com.moffatman.chan with your own package identifier. The README explains why, and it is not a cosmetic instruction. Changing the package name lets you create signing keys and create signed builds, which is necessary to install the app on your own iOS devices, or to distribute Android APKs. Without it you are building with the upstream author's identity, which you cannot sign.

After that, some generated Dart code is needed, produced by build_runner.

bash
flutter pub run build_runner build

The release builds then differ per platform.

bash
flutter build apk --split-per-abi --release --enable-experiment=records
flutter build ios --release --enable-experiment=records

Building for iOS needs a Mac and Xcode, and development mode is flutter run --enable-experiment=records with the device connected.

The --enable-experiment=records argument is not optional in any of these commands, and the README explains why at the end of the build section. Chance is developed using the flutter master branch, and it takes advantage of some preview Dart 3 features, so the argument is required to run it. The advice if you get errors while building or running is to try flutter channel dev or flutter channel master.

That is the whole cost of building this project, and it is a real one. A consumer of the released APK never touches it, but anyone who wants to modify the app, contribute to it or audit it needs the Flutter master channel and a preview language feature.

The repository layout backs up the picture of a multi-platform Flutter project, with android/, ios/, linux/, macos/, web/ and windows/ directories alongside lib/ for the Dart code, integration_test/ and test/ for the test suites, test_driver/ for driver tests, assets/ for bundled resources, and pubspec.yaml with a committed pubspec.lock. There is also a firebase.json, and a devtools_options.yaml, which is the Flutter DevTools configuration.

Installing it on a phone, and the App Store constraint

For end users the install routes are different on the two platforms.

On Android, APKs are available on the releases page. That is the straightforward path: download the APK, install it, done. No store involved, no review, and the release page carries the version tags with the engine counters described earlier.

On iOS, Chance has not yet been submitted to the App Store, and the README points to a TestFlight beta testing group link instead. So iOS users join the beta group and install through TestFlight, which means accepting a beta rather than installing a stable release.

That iOS situation has a consequence the README explains further down in the FAQ, and it is the kind of detail that only surfaces once you have built the thing. The question Why are some settings on a web page has the answer: to comply with Apple App Store guidelines on user-generated content, not all settings are modifiable in the app.

That is worth dwelling on. The guideline reference is to the user-generated-content section of the Apple review guidelines, and the consequence is that some of the app's configuration lives on a web page rather than in the app's own settings screen. For an imageboard client, that is where you would expect to find things like the board address and content preferences, and having some of them hosted on a page rather than in the app is a workaround for review rather than a design choice.

It also implies the app depends on something it does not fully control. If the web-hosted settings page is unreachable, the settings it covers are unreachable too, and the user cannot fix that from inside the app.

The other FAQ answers are more practical. Why isn't it displaying the content I expect? Because by default the app is set up to browse a test server, and in the app settings you can edit your content preferences to browse a different imageboard. That default is worth knowing about, since an app that opens on someone else's test server rather than a board you chose is a surprising first-run behaviour.

For navigation there are two shortcuts: tapping the status bar scrolls quickly to the top of a thread, and holding on the post counter at the bottom-right scrolls to the bottom. Both are the kind of detail that only matters once you have used the app a lot.

Support runs through the app itself. There is an imageboard available in the settings page of Chance that is used for meta-discussion, and that is where the README says to ask for help, report a bug or request a feature.

Where Chance is the wrong choice

There are three limits, and the first is about the toolchain rather than the app.

The first is that the app requires the Flutter master branch and a preview Dart 3 feature behind the records experiment flag. The README is candid that this is because the project takes advantage of some preview Dart 3 features. In practice this means that building, contributing to or auditing the source needs a moving toolchain, and a build that worked last month may not build today because the master channel moved. The Flutter team's own release cycles are the reference point for how quickly that happens.

The second is the App Store constraint on settings, which is a real functional limit rather than a packaging detail. Not all settings are modifiable in the app because of the user-generated-content guideline, so some configuration is on a web page the app does not control. That is a dependency on an external endpoint for core configuration, and it is the kind of arrangement that makes an app harder to reason about when something is misconfigured.

The third is the dependency on the imageboards themselves. The forked engine exists precisely because those sites change, and while forking insulates the client from upstream churn, it also means the client is pinned to a snapshot of how the sites worked at the time of the fork. When a board changes something the fork has not yet adapted to, the app is the place where it shows. There is also a threshold question of scale: the feature list covers the common cases well, but the ways in which imageboards differ from each other are numerous, and the README does not claim coverage of every board.

The alternative to a dedicated client is the browser, and the honest comparison is that the browser is always current with the site's markup and never needs an engine fork. What Chance adds over that is a set of capabilities a browser cannot offer: the media gallery, saved local collections of threads and attachments, regular-expression filters over posts, the thread watcher, and the multi-column layout on a large screen. If you browse one board occasionally, a browser is the right answer and a client is overhead.

For the other case, a different client with a different architecture is the comparison point. The difference in approach is in where the parsing happens: an app that renders content in an embedded web view inherits whatever the site's own JavaScript does and stays current, at the cost of being a browser in a window, while an app with its own engine gives you the gallery, the filters and the structured post data, at the cost of needing to track the site. Chance sits firmly on the second side of that line, and the fork is the price of the choice.

There is a third option for the very different case of a board with its own API, where a client that speaks it directly is both simpler and more reliable than either.

Editorial conclusion

Adopt Chance if you want an imageboard browser on iOS or Android with features no generic browser gives you, such as saved collections, regex filters and a thread watcher. Do not expect it to build on a stable Flutter toolchain, because the project requires the flutter master branch and the --enable-experiment=records flag. Before you start, read the FAQ section on the default test server and the App Store settings restriction, and note the last push was on 2026-10-17.

Frequently asked questions

What is Chance and what platforms does it run on?

Chance is an imageboard browser built using Flutter, intended for use on iOS and Android. It includes a multi-column layout for larger screens, WEBM playback and conversion, a media gallery, local saving of threads, posts and attachments, regex filters, archive support and a thread watcher.

How do I install Chance on Android and iOS?

On Android, APKs are available on the repository releases page. On iOS, Chance has not yet been submitted to the App Store, so users join the beta testing group through the TestFlight link given in the README and install from there.

How do I build Chance from source?

Install Flutter, clone and enter the repository, run flutter pub get, optionally replace the com.moffatman.chan package identifier with your own so you can create signing keys, then run flutter pub run build_runner build. The release builds are flutter build apk --split-per-abi --release --enable-experiment=records and flutter build ios --release --enable-experiment=records.

Why does building Chance require the Flutter master branch and an experiment flag?

The README states that Chance is developed using the flutter master branch and takes advantage of some preview Dart 3 features, so the --enable-experiment=records argument is required to run it. If you hit errors while building or running, it suggests trying flutter channel dev or flutter channel master.

Why are some settings on a web page in Chance rather than in the app?

The README explains that this is to comply with the Apple App Store guidelines on user-generated content, and that not all settings are modifiable in the app as a result. The app also opens by default pointed at a test server, and you edit your content preferences in app settings to browse a different imageboard.

Official sources

  1. Issues
  2. License: GPL-3.0
  3. moffatman/chan on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/moffatman-chan.svg)](https://hysenlabs.com/projects/moffatman-chan)

Was this useful?