Open-source project
invoiceninja/admin-portal avatar
invoiceninja/admin-portal

Invoice Ninja admin portal: a Flutter client for the Invoice Ninja API

Invoice Ninja: Desktop/mobile admin portal built with Flutter

1,755 stars583 forksDartNOASSERTION

At a glance

What is it?
The Invoice Ninja admin portal is a single Flutter codebase that ships as desktop apps on Windows, macOS, Linux and mobile apps on iOS and Android, talking to an Invoice Ninja backend over its API. The code has a FOSS variant, but the licence is a custom one and the whole client depends on a server you do not control.
Who is it for?
This repository fits a team that already runs an Invoice Ninja server and wants one client that covers desktop and mobile from a single Dart codebase, or a user who wants the FOSS build of the app without the proprietary extras.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 8 days 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 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One Flutter codebase, seven distribution channels

The project describes itself as the client application for Invoice Ninja, and the distribution list in the documentation shows how wide that word client has to stretch.

The desktop builds are a Windows app from the Microsoft Store, a macOS app from the App Store, a Linux Snap and a Linux Flatpak. The mobile builds are an iOS app, an Android app on Google Play, and an F-Droid listing. That is seven distribution channels from one Dart codebase, and the top level repository listing backs it up with android, ios, linux, macos, windows and web directories, plus snap and flatpak directories for the Linux packaging and a fastlane directory for the store uploads.

The client itself is a Flutter application, which is what makes that fan-out possible. One widget tree compiles to a native binary per platform rather than needing a separate codebase per device, and for an invoicing client that matters because the forms, the data tables and the state logic are the expensive part, and they are written once.

The README also links a demo environment at demo.invoiceninja.com, and the setup section gives working credentials for it, so you can click through the interface before deciding whether a backend of your own is worth running. That is the right way to evaluate this project, because the interesting question is not how the app looks but how much of Invoice Ninja it actually exposes.

Note also the documentation links to a separate flutter-mobile repository for the screenshot images. The repository has a history of moving between mobile and admin portal repositories, and the screenshots still live in the older location, which is a small signal about how the two halves have been organised over time.

Redux state, with components paired across ui and redux folders

The application was created using the Flutter Redux Starter, and the architecture is credited to two reference projects, a Redux sample by Brian Egan and the inKino project by Iiro Krankka. That is a 2018 era choice, and it is worth understanding what it means before extending this codebase.

The file structure is documented in the README and it is unusual enough to be worth reading closely. The lib directory splits into data, redux, ui and utils. The data layer holds mock data for testing, models that represent entities, and repositories that are the source of data. The redux directory holds the app state management, and inside it a component folder with the classic Redux pieces, where actions are methods that update app state, middleware runs in response to actions and executes before the reducer, the reducer intercepts actions and is responsible for updating the state, and selectors read data from the state. The state itself is described as an immutable object that lives at the top of the widget hierarchy.

The ui directory mirrors the redux one, with a component folder containing view and edit subfolders. The documentation explains the convention: the ui and redux folders contain components paired together, so an auth folder appears in both. The pairing is the mechanism by which a view knows which actions to dispatch and which selectors to read.

The practical implication for a team picking this up is that the state layer is Redux with its boilerplate, not a modern reactive approach. There is no riverpod, no bloc, no signals. If your team is fluent in Flutter's newer state patterns, the learning curve here is real, and it is why the README links a set of blog posts, including one on handling complex forms with multiple tabs and relationships, which is where the Redux approach starts to cost you.

The FOSS build is a file copy, not a compiler flag

The most distinctive thing about this repository is that it contains two versions of itself, and the mechanism for choosing between them is a set of cp commands.

The README documents a procedure for removing the non-FOSS code, and it is literally copying alternative files over the real ones:

bash
cp lib/utils/oauth.dart.foss lib/utils/oauth.dart
cp lib/utils/app_review.dart.foss lib/utils/app_review.dart
cp lib/ui/app/upgrade_dialog.dart.foss lib/ui/app/upgrade_dialog.dart
cp lib/ui/app/pinput.dart.foss lib/ui/app/pinput.dart
cp android/app/src/main/AndroidManifest.foss.xml android/app/src/main/AndroidManifest.xml
cp android/settings.gradle.foss.kts android/settings.gradle.kts
cp pubspec.foss.yaml pubspec.yaml 
rm pubspec.lock

The files being replaced give a fairly clear picture of what the non-free parts are. The OAuth implementation, the app review helper, an upgrade dialog, and an input widget, plus the Android manifest and the Gradle settings, and the whole dependency manifest. That pattern suggests the proprietary elements are the OAuth sign in path, the store review prompts, the in-app upgrade prompt, and some custom input behaviour, most likely licensed components in the input widget. The repository listing confirms the shape, with a pubspec.foss.yaml at the top level next to the ordinary pubspec.yaml.

This has an operational consequence that the README does not spell out. The switch is destructive and manual: it overwrites tracked files and deletes the lock file, and there is no script in the repository to undo it. Anyone who runs it has a working tree that no longer matches the branch, and the difference has to be reconstructed by hand or by a clean checkout. For a team that wants to contribute upstream, that is friction on every pull request.

There is also a plain risk in the arrangement. A build that is supposed to be free of proprietary code depends on a developer remembering to run seven copy commands, and on a reviewer noticing if they forgot. The pubspec.foss.yaml is the load-bearing one, since it is the dependency list, but the Android manifest and the Dart sources matter as much.

Setting up a development environment takes three commands

The setup section of the documentation is short and concrete. The config file is initialised by copying an example into place, the Android build can be switched to an unsigned development variant, and the app is then launched with flutter run.

bash
cp lib/.env.dart.example lib/.env.dart
bash
cp android/app/build.gradle.dev.kts android/app/build.gradle.kts
bash
flutter run

The .env.dart file is where the API endpoint and other configuration live, and the example file is the template for it. The Android build copy is what allows running unsigned on a device, which matters because the app store signed build will not install alongside a development build otherwise.

If you do not have a backend, the documentation gives credentials for the public demo, with the email, a password and the URL, so that a new contributor can verify the app connects and renders data before touching any configuration of their own.

Two more workflows are documented for later. Code generation for the model files is run through build_runner with the delete-conflicting-outputs flag, which also removes the old generated files so that stale artefacts do not collide, and the test suite is run with flutter drive against a test_driver target named all_it.dart. The repository also has a run_all_it_tests.sh script and a test_driver directory, so the integration tests are driven against a running device rather than being pure unit tests, which for a client app is the more useful kind of test.

The custom licence is the thing to read before anything else

The licence field for this repository is not one GitHub recognises, and the file is named LICENSE.txt rather than LICENSE. That is a deliberate choice by the project and it is the single most consequential fact for an organisation considering this code.

Why it exists is inferable from the FOSS procedure. The project ships a client with proprietary components in it, and the FOSS variant exists so that the free parts can be distributed under terms the community can rely on. That means the default build is under terms chosen by Invoice Ninja, not under a standard open source licence, and the naming of the licence file points to a file you need to read rather than a licence you can assume.

What this means in practice depends on what the file says, which is not quoted in the documentation, so it is worth being precise about the limits of what can be concluded here. You can conclude that a custom licence governs the default build, that the project intends a genuinely free alternative to exist, and that the FOSS variant is the artefact to look at if you need standard terms. You cannot conclude anything about attribution, redistribution, warranty or patent terms without reading LICENSE.txt, and this page will not give you legal advice on that.

The rest of the repository is a mixture of open and commercial in the ordinary way for this kind of product. The client talks to the Invoice Ninja server, and the server is a separate project with its own licensing. The client is useless on its own, which means an organisation is also adopting a dependency on the server, and the server's terms are a separate question to answer.

The client is only as current as the server it talks to

The release cadence is the operational risk here, because the client and the server are versioned independently and a mismatch shows up as broken behaviour rather than as a failed install.

The recent tags on this repository are v5.0.198 from 2026-10-08, v5.0.194 from 2026-05-14 and v5.0.191 from 2026-05-06. The last push was on 2026-10-15, and the repository is not archived. So the client is being worked on, and it is on a 5.0 line, with a version number that increments on every build rather than on a schedule.

The consequence is that the client talks to whatever Invoice Ninja server you point it at, and a server two major versions behind will have a different API surface. A client that assumes a recent endpoint returns an error the user sees as a blank screen, and the error handling lives in code you did not write.

There is also a deployment asymmetry that follows from the distribution model. Store builds update on the store's schedule, so a Windows user gets the Microsoft Store version, a macOS user gets the App Store version, a Linux user on Snap or Flatpak gets that channel's build, and an Android user on Google Play gets a different build again, and none of them update at the same moment. The F-Droid listing updates on its own schedule. That is normal for a cross platform client and it does mean bug fixes reach different users on different days.

For a self hosted business running its own server, the practical advice is to update the server first, check the client release notes, and then roll the client out. The repository has a bump_version.sh script and a docs directory, so the process exists on paper, and the codemagic.yaml file at the root suggests the builds are run in CI rather than by hand.

Where the admin portal is the wrong choice

Three situations rule it out.

The first is a team that wants invoicing without running a server. The admin portal is explicitly a client application for a separate backend project, and the setup section tells you what to do if you do not have an Invoice Ninja backend, which is to use the demo. The documentation describes the app only as a client for a backend, and nothing in it suggests an offline mode. If you wanted something that runs on a laptop and prints a PDF, this is the wrong shape of software entirely.

The second is an organisation that has a policy requiring a standard open source licence for anything in its supply chain. The default build here is under a custom licence, and while the FOSS variant exists, adopting it means maintaining a patch procedure that overwrites tracked files and deletes the lock file, which is a permanent tax on the codebase. That is a defensible arrangement, but it is not the same thing as shipping a standard MIT or Apache build.

The third is a team whose developers are new to Flutter. This is a large application in a framework that is still relatively young, built on a Redux pattern from 2018 that most new Flutter developers will not know, with generated model files and a code generation step, and with integration tests that need a device or emulator to run. None of that is a reason to avoid it, but it is a real onboarding cost that a smaller or newer interface would not have.

What the project is genuinely good at is the opposite: an organisation that already runs Invoice Ninja, has a Flutter capable team, and wants one client that covers Windows, macOS, Linux, iOS and Android without maintaining five codebases.

Editorial conclusion

This repository fits a team that already runs an Invoice Ninja server and wants one client that covers desktop and mobile from a single Dart codebase, or a user who wants the FOSS build of the app without the proprietary extras. It does not fit a team that wants a stand-alone invoicing tool, because the client has no value without a backend, and it does not fit an organisation that requires an unambiguously open source licence, because the project ships under a custom licence that GitHub cannot classify. Before adopting it, read LICENSE.txt yourself, decide whether the FOSS variant of pubspec.yaml and the Android manifest is acceptable for your distribution channels, and confirm the version of the server you run matches the version of the client you install, since the two ship on different cadences.

Frequently asked questions

What is the Invoice Ninja admin portal?

It is the Flutter client application for Invoice Ninja, published as desktop apps for Windows, macOS and Linux through the Microsoft Store, the App Store, Snap and Flatpak, and as mobile apps for iOS, Android and F-Droid. It requires a separate Invoice Ninja backend to work.

How do I run the Invoice Ninja admin portal from source?

Copy lib/.env.dart.example to lib/.env.dart, copy android/app/build.gradle.dev.kts over android/app/build.gradle.kts if you want to run unsigned on Android, then run flutter run. Without your own backend you can sign in with the demo credentials given in the documentation.

How do I build the free and open source version of the admin portal?

The documentation gives a set of copy commands that replace the proprietary OAuth, app review, upgrade dialog and input files with their .foss counterparts, swap in the FOSS Android manifest and Gradle settings, replace pubspec.yaml with pubspec.foss.yaml and delete pubspec.lock.

What licence does the Invoice Ninja admin portal use?

The repository ships a custom licence in a file named LICENSE.txt, which is why GitHub does not classify it as a standard open source licence. The FOSS variant of the app exists separately for builds that need open terms, so the file itself is the reference to check.

How do I run the tests and regenerate models in the admin portal?

Model files are regenerated with flutter packages pub run build_runner build --delete-conflicting-outputs, and the test suite runs with flutter drive --target=test_driver/all_it.dart. The repository also carries a run_all_it_tests.sh script and a samples directory with form and redux examples.

Official sources

  1. invoiceninja/admin-portal on GitHub
  2. Issues
  3. Project website
  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/invoiceninja-admin-portal.svg)](https://hysenlabs.com/projects/invoiceninja-admin-portal)

Was this useful?