What's New in Kotlin SDK 2.1
Kotlin SDK 2.1 was tagged on 14 October 2026, about two hours after Kotlin SDK 1.2. There is no 2.0 tag: 2.1.0 is the release after 1.2.0. It fixes each of the known issues listed in the Kotlin SDK 1.0 notes except the missing comment field, and it redraws the board in Material 3.
The 1.1.0 and 1.1.1 tags from April do not compile: their resource imports name the wrong package. So this post covers everything after 1.0.1 that reached a tag you can build: the work from the 1.1 tags, which compiles as of 1.2.0, the fixes in 1.2.0, and 2.1.0.
The short version
- New look:
FeedbackListdraws Showcase cards by default.FeedbackListScreenadds an app bar, a search field and a sort and filter sheet.listStyle = FeedbackListStyle.CLASSICkeeps the 1.x cards. - Fixed: votes no longer roll back, removing a vote works, comments load, the form's email reaches FeedbackKit, and the SDK creates a user id when you do not set one.
- New since 1.0.1: the screens in eleven languages, a mailing-list opt-in on the form, and
mrronusers.register. - Breaking:
Comment.feedbackIdis nullable,FeedbackKitErrorhas a new case, and the Compose BOM moves to 2024.06.00. - Install: Maven Central holds 1.0.0 and 1.0.1. 2.1.0 is a git tag, built from source.
Installing 2.1
com.getfeedbackkit:feedbackkit:1.0.1 is still the newest artifact on Maven Central, and there are no Gradle coordinates that resolve 2.1.0 from a repository. To use 2.1.0, check out the 2.1.0 tag of the Kotlin SDK repository and depend on its :feedbackkit module from your build. The README at the 1.2.0 tag shows com.swiftlydeveloped:feedbackkit:1.2.0. That artifact was never published, so do not copy that line.
The requirements are minSdk 24 and Java 17, as before. The Kotlin package is still com.swiftlydeveloped.feedbackkit.
From the 1.1 tags, now building
- Eleven languages. Every string the screens draw comes from Android string resources in English, Spanish, French, German, Japanese, Simplified Chinese, Brazilian Portuguese, Korean, Italian, Arabic and Russian, picked from the device.
FeedbackStatusandFeedbackCategorygaineddisplayNameResfor your own screens. Android users see the status name "TestFlight" too, since it is the status's name rather than a platform. - A mailing-list opt-in. Once someone types an email into
SubmitFeedbackView, a checkbox offers product updates, with operational and marketing sub-options.feedback.createandvotes.votetakesubscribeToMailingListandmailingListEmailTypes. Feedback.isHiddenOverLimit. On the Free plan a project holds 10 requests, and further ones are saved but hidden from the board. The flag lets your own screens tell the person who filed one that it is waiting.- New server addresses under getfeedbackkit.com.
What 1.2 fixed
- The library compiles again.
- Comments load. A request with comments used to show "Failed to load comments", because the model required a field the server never sends.
- The Official badge on your team's comments renders.
Feedback.emailis filled in from the server's reply../gradlewworks on a fresh clone of the repository.
Voting, removing a vote, the form's email and the user id still behaved as in 1.0 on the 1.2.0 tag. 2.1.0 fixes them.
The Showcase list
Each request is an elevated card: a status line with a glyph and the status name, the title, the whole description, and a footer with the category, the comment count and a vote button. The button is tonal before you vote and filled after. Completed and Rejected requests show a plain count, not a button. Each status has its own glyph shape, so status never depends on colour alone, and the cards take their surfaces and text colours from your MaterialTheme, so dynamic colour and dark theme follow the app. Status colours still come from FeedbackKitTheme, adjusted where needed to reach 4.5:1 contrast against the card.
The sort and filter sheet offers Votes, Newest, Oldest and Comments, plus Status and Category chips. The sort is saved between launches. Filters and search last for the session, and while a filter is on, a "Filtered by" row with Clear heads the list. Search matches the title and description, ignoring case and accents. TalkBack reads each card as one element with View details and Vote actions, every control has a 48dp touch target, and text is tested at font scales from 0.85 to 2.0.
FeedbackListScreen puts it all together, edge to edge:
@Composable
fun IdeasScreen(onOpen: (Feedback) -> Unit, onSuggest: () -> Unit, onBack: () -> Unit) {
FeedbackKitProvider(theme = FeedbackKitTheme.Light) {
FeedbackListScreen(
title = "Trailmate ideas",
onFeedbackClick = onOpen,
onAddFeedback = onSuggest,
onBack = onBack,
onVoteError = { error -> Log.w("Trailmate", error.message) }
)
}
}
If your app bar is your own, place FeedbackSortFilterButton(state) and FeedbackListSearchField(state) in it, share one rememberFeedbackListState(), and pass contentPadding to FeedbackList so the last card clears your bars. There is no pull-to-refresh gesture yet. refresh() and isRefreshing are there for one you supply.
What 2.1 fixed
- Votes stick. The library reads the server's reply to a vote, so the button keeps its new count instead of rolling back. A failed vote now reaches
onVoteError. - Removing a vote works. The user id travels in the request body, where the server reads it.
- The email reaches FeedbackKit. The form's address and the mailing-list choices are sent under the keys the server reads, so the person who filed a request can be emailed when its status changes.
votes.votealso takesemailandnotifyStatusChange. The oldnotifyOnStatusChangestill compiles. - A user id from the start. Leave
userIdunset andconfigure()createslocal_plus a UUID, stores it privately, and reuses it on the next launch. An id saved by an older build is kept. An id you set replaces it. - One unknown row no longer breaks the list. A request with a status or category this build does not know is skipped and the rest are shown.
- Errors carry the server's words. An expired key used to report a placeholder string. Errors now carry the server's message and its
code, and on-screen error text uses the eleven catalogs. - Quieter debug logs. With
debugon, a log line holds the method and the path, never a full URL or a response body. - A blank API key is refused when the configuration is built.
MRR on register
users.register now sends the two fields the server reads: the user id and an optional monthly revenue. Pass it from a coroutine when you know what the person pays, so your team can sort requests by the revenue behind them:
lifecycleScope.launch {
FeedbackKit.shared.users.register(userId = "trailmate-user-4821", mrr = 4.99)
}
The email, name, externalId and metadata arguments still compile, deprecated, and are no longer sent.
Still open in 2.1
- The detail screen shows comments but has no field for writing one.
comments.createworks from your own screen. - The built-in vote button does not pass an email or the status-change opt-in. Call
votes.voteyourself to offer them. users.getCurrentUser()calls a route the server does not have.
Breaking changes and how to migrate
The 2.1.0 changelog calls every API change additive. That is not quite true for code that compiles against 1.2.0:
Comment.feedbackId is nullable (1.2.0)
It is now String?, defaulting to null, because the server never sends it. Use the id of the request you loaded the comments for.
A new FeedbackKitError case (2.1.0)
FeedbackKitError is a sealed class, and it gained RateLimitError for a 429, with retryAfterSeconds when the server sends it. A 429 used to arrive as UnknownError. A when expression that listed every case without an else no longer compiles:
fun message(error: FeedbackKitError): String = when (error) {
is FeedbackKitError.NetworkError -> "You appear to be offline."
is FeedbackKitError.RateLimitError -> "Too many requests. Try again in a moment."
is FeedbackKitError.ConflictError -> "You already voted for this."
else -> error.message
}
The SDK does not retry on a 429. Message texts changed too, from placeholders to the server's own words, so branch on the class or code.
Compose BOM 2024.06.00 (2.1.0)
The library now builds against Compose BOM 2024.06.00, up from 2024.01.00. If your app pins an older BOM, Gradle resolves the newer Compose versions the library asks for, so test your own screens after the upgrade.
The default list look (2.1.0)
Existing FeedbackList calls compile unchanged, and the 1.x signatures remain as hidden overloads so compiled code still links. But they now draw Showcase cards. Pass listStyle = FeedbackListStyle.CLASSIC to keep the old cards.
How FeedbackKit fits in
Voting is the heart of the board, and on Android it now works end to end. Here is how voting looks in FeedbackKit, with sample data from a fictional hiking app:
Votes that stick
Upgrading from 1.0.1
- Replace the Maven dependency with the
:feedbackkitmodule from the 2.1.0 tag. - Fix
Comment.feedbackIdreads and addRateLimitErroror anelseto exhaustivewhenexpressions. - Choose Showcase or Classic, and try Showcase in dark theme, at large font scales and in Arabic.
- Drop any workaround for votes rolling back, removing votes or comments failing to load. If you set a placeholder user id, you can let the SDK create one, but keep passing your own id if your app has accounts.
Apps that stay on the Maven build can follow the Kotlin SDK 1.0 tutorial. The current Android tutorial is the place to start a new integration, and the reference is in the Kotlin SDK docs.
Every plan includes the Kotlin SDK. Start free and put the new board in your next build.