February has 28 days. A tenant whose rent is due on the 31st still owes rent in February.
That one sentence caused more changes in Rent Folio than any feature request. Rent Folio is our offline Flutter app for landlords and small property managers: properties, tenants, leases, rent payments and repair costs, all stored on the phone. No server, no account, no ads, no analytics.
When we rebuilt it for version 1.2.0, we kept running into the same handful of questions. Here's how we answered them.
"Is this tenant paid up?"
The tempting design is a status field on the tenant that you flip to "paid" when money comes in. We didn't do that. A stored flag goes stale the moment the calendar moves, and someone has to remember to flip it back.
Rent Folio works the status out every time from three things: the tenant's due day, their lease dates and the payments recorded against them. The result is one of four values: Paid, Due soon, Overdue or Upcoming.
A few rules fall out of that:
- Due days 29 to 31 land on the last day of a shorter month.
- Rent is owed only from the day the tenant is added, or from the lease start if that's later.
- A partial payment is recorded, but the month stays overdue until it's fully paid.
- After an optional lease end date, expected rent and reminders stop.
// simplified DateTime dueDateFor(int year, int month, int dueDay) { final lastDay = DateTime(year, month + 1, 0).day; return DateTime(year, month, dueDay > lastDay ? lastDay : dueDay); } RentStatus statusFor(Tenant t, DateTime today) { final due = dueDateFor(today.year, today.month, t.dueDay); if (t.paidFor(today.year, today.month) >= t.monthlyRent) { return RentStatus.paid; } if (today.isAfter(due)) return RentStatus.overdue; return isDueSoon(due, today) ? RentStatus.dueSoon : RentStatus.upcoming; } DateTime(year, month + 1, 0) is the old trick for "last day of this month". Day zero of next month rolls back one day. It's the whole short-month fix in a line.
The same thinking drives the reports. Rent counts in the month it covers, not the day it arrived, and a repair counts in the month it was completed. A tenant who pays March's rent on 28 February shows up in March. Landlords read reports by month, so that's what the numbers follow.
"What does 1.200,50 mean?"
Depending on where your users live, it's either one thousand two hundred and a half, or nonsense. An earlier version of Rent Folio rounded payments when they were edited, which is the kind of bug that makes an accountant close the app forever.
Amount fields now accept 1,200.50, 1.200,50 or 12,50 and never round them. The approach is to decide which separator is the decimal one before turning the text into a number:
// simplified String normalizeAmount(String raw) { final s = raw.replaceAll(' ', ''); final lastComma = s.lastIndexOf(','); final lastDot = s.lastIndexOf('.'); final decimalSep = lastComma > lastDot ? ',' : '.'; final thousandsSep = decimalSep == ',' ? '.' : ','; return s.replaceAll(thousandsSep, '').replaceAll(decimalSep, '.'); } There's a set of unit tests behind it. The project ships 41 of them, covering rent status, due dates, reminder scheduling, amount parsing and the backup format. Date maths and money parsing are exactly where we didn't trust ourselves.
"Which database survives the next Play Store rule?"
Version 1.1 used isar 3.1.0. For Google Play's 16 KB page size rule, 1.2.0 replaced it with the maintained isar_community 3.3.2. The schemas are still generated, and existing 1.1.0 data is kept on upgrade. Older 1.1.0 backups still restore.
// simplified import 'package:isar_community/isar.dart'; part 'payment.g.dart'; @collection class Payment { Id id = Isar.autoIncrement; late int tenantId; late DateTime paidOn; late DateTime monthCovered; } Restore is all-or-nothing now. The whole backup is checked before anything changes, and damaged files or files from a newer version are rejected. Deleting a property cascades to its tenants, payments, alerts and repairs, and cancels their reminders, so nothing is left pointing at a property that no longer exists.
Any payment can become a PDF rent receipt with a receipt number, tenant, property, payment date, month covered and amount. It opens in the system print and preview screen, where the landlord prints it or saves it as a PDF.
Where it stops
- Android and iOS only. Web and desktop targets were removed in 1.2.0. The web preview is a browser build with sample data, and notifications and the print dialog only behave natively on a device.
- One phone, no accounts. There's no backend, so a co-owner can't log in from their own phone. Moving data means a JSON backup file you share yourself.
- Reminders are local. A phone notification 2 days before each due date, scheduled on the device. No server or push service is involved.
- No monetization built in. No ads and no analytics, which also means nothing to add if you want them.
Try it
- Web preview: https://guide.stacklynlabs.com/rentfolio/preview/
- Android APK: https://drive.google.com/file/d/1-hHP-_x3oi1qYUJ_Lpm0fog7GJ7qTi-7/view?usp=sharing
- Product page: https://stacklynlabs.com/product/rent-folio/
It's built on Provider and isar_community for Android 7.0+ and iOS 14.0+, and the Regular licence is $29.
How do you handle a due date of the 31st in your own code: clamp to the end of the month, or roll it into the next one?





