Skip to content
5 min read Guides Flutter

Introducing the FeedbackKit Flutter SDK

On 9 February we released Flutter SDK 1.0, the Dart package that brings FeedbackKit into a Flutter app. It gives your app a board where people read what others have asked for, vote for what they want, and file their own requests, without leaving the app. Your team then reads, sorts and answers those requests in the FeedbackKit admin app.

The short version

  • The package is feedbackkit_flutter. It needs Flutter 3.10 and Dart 3.0 or later, and depends on http and shared_preferences.
  • One wrapper, FeedbackKitProvider, and four widgets: FeedbackList, FeedbackCard, FeedbackDetailView and SubmitFeedbackView.
  • A vote button that updates before the server answers, plus badges for the six statuses: Pending, Approved, In Progress, TestFlight, Completed and Rejected.
  • A theme object with a light default and a dark preset.
  • A plain Dart client, FeedbackKit, with typed errors, for screens of your own.

What is in Flutter SDK 1.0

The provider

FeedbackKitProvider sits above your MaterialApp. It takes your project's API key, the server address, a user id and a theme, builds the client, and holds the list and vote state for every widget below it. FeedbackKitProvider.of(context) hands you the client, the list state and the theme from anywhere in the tree.

Despite the name, it is not the provider package from pub.dev, and you do not need that package. It is a StatefulWidget that publishes an InheritedWidget, and the state lives in ChangeNotifier classes, so it sits next to Riverpod, Bloc or whatever your app already uses.

Four widgets

FeedbackList is the board. It loads the project's requests when it appears and draws a card for each one with its status, category, title, three lines of description, comment count, age and vote button. It has builders for the loading, empty and error states, an onFeedbackTap callback, and an error screen with a Retry button. The list draws the requests and leaves the screen around them to you: the app bar, the route to the detail screen and the button that opens the form are yours, which keeps it at home in any navigation setup.

FeedbackDetailView shows one request in full: its badges, vote button, title, description, when it was filed, and its comments with a field for adding one. SubmitFeedbackView is the form: category chips (Feature Request, Bug Report, Improvement or Other), a title, a description and an optional email. It checks that the title and description are filled in, and calls onSubmitted with the new request. A Cancel button appears when you pass onCancel.

The pieces are public too: FeedbackCard, VoteButton in three sizes, StatusBadge and CategoryBadge. All of them are built from Flutter's widgets library rather than Material or Cupertino, so they look the same on iOS and Android and take their colours from the FeedbackKit theme, not from your ThemeData.

Voting

A tap on the vote button raises the count at once and sends the vote. If the server refuses it, the button returns to its earlier state. Voting stops when a request reaches Completed or Rejected: the button fades and ignores taps. A new request starts with its submitter's vote, so it shows a count of 1, and each user id votes once per request.

A client for your own screens

Everything the widgets do goes through the client: feedback.list(), which takes a ListFeedbackOptions with a status or category filter, feedback.get(), feedback.create(), votes.vote(), votes.unvote(), comments.list() and comments.create(). Calls return futures and throw a FeedbackKitError subclass: AuthenticationError for a bad key, ValidationError, NotFoundError, ConflictError, PaymentRequiredError, ServerError, or NetworkError with an isTimeout flag.

Users

Every request and vote belongs to a user id, which you pass to the provider. The SDK sends it with every call, and keeps it with shared_preferences when you change it through setUserId. Version 1.0 has no way to pass what a user pays, so requests filed from Flutter carry no MRR.

Theming

FeedbackKitTheme holds a primary colour, background and card colours, text colours, a border colour, a colour per status and per category, a corner radius and a spacing unit. FeedbackKitTheme.dark is a ready-made dark theme. The SDK does not switch between the two by itself, so pass the dark one when your app is in dark mode.

Known issues in 1.0

Against the FeedbackKit server of the same week, five parts of 1.0 do not work as the widgets suggest. Each one is worth knowing before you ship:

  • Pass a user id. The server needs one to file a request or record a vote, and 1.0 never makes one up. Without it, both fail with a ValidationError.
  • Removing a vote fails. The SDK puts the user id on the query string, and the server reads it from the request body. The button returns to its voted state, so in practice votes stick.
  • Adding a comment fails. The comment request carries no user id. Reading comments works, but every author shows as "Anonymous" and replies from your team lack the Team label: the server sends no author name, and marks team replies with a field the SDK does not read.
  • The email in the form does not arrive. The SDK sends it as email, and the server reads userEmail. Votes carry no email at all. So people who file or vote from Flutter 1.0 are not told by email when a request's status changes.
  • Set the server address. The built-in default is not the production server that the admin app uses. Pass the production address as baseUrl, as below.

Getting started

Add the package:

flutter pub add feedbackkit_flutter

That installs 1.0.1. It differs from 1.0.0, released the day before, in its built-in server address and its version number, and in nothing else. Then wrap your app in the provider with your project's API key, which you find in the FeedbackKit admin app under the project's settings:

import 'package:flutter/material.dart';
import 'package:feedbackkit_flutter/feedbackkit_flutter.dart';

void main() {
  runApp(const TrailmateApp());
}

class TrailmateApp extends StatelessWidget {
  const TrailmateApp({super.key});

  @override
  Widget build(BuildContext context) {
    return FeedbackKitProvider(
      apiKey: const String.fromEnvironment('FEEDBACKKIT_API_KEY'),
      baseUrl: 'https://api.feedbackkit.prod.swiftly-developed.com/api/v1',
      userId: 'trailmate-user-42',
      child: const MaterialApp(
        title: 'Trailmate',
        home: IdeasScreen(),
      ),
    );
  }
}

class IdeasScreen extends StatelessWidget {
  const IdeasScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Ideas')),
      body: const FeedbackList(),
    );
  }
}

Build with --dart-define=FEEDBACKKIT_API_KEY=sf_your_api_key to keep the key out of your repository. The Flutter SDK 1.0 tutorial walks through the rest: the detail and submit routes, user ids, theming, your own screens and testing. The reference is in the Flutter SDK docs.

How FeedbackKit fits in

The SDK is the part your users see. Your team works in the FeedbackKit admin app for iPhone, iPad and Mac, which sends a push notification for each new request and comment: read new requests, merge duplicates, reply in the comments, and move a request through Approved, In Progress and TestFlight to Completed. On Pro and up, requests can go to GitHub, Linear, Notion, ClickUp or Monday.com, and new ones can be posted to Slack. See pricing for what each plan includes.

Here is how the board looks in FeedbackKit, with sample data from a fictional hiking app:

Votes from inside the app

People vote inside your Flutter app, and voting stops when a request reaches Completed or Rejected.

What comes next

Later releases each have their own post, beginning with Flutter SDK 1.1. If you are starting today, the current Flutter tutorial covers the latest version.

Every plan includes the Flutter SDK. Start free, create a project, and put the board in your next build.

Keep reading

Collect feedback in your own app

FeedbackKit ships SDKs for Swift, Kotlin, React Native, Flutter and JavaScript, and syncs what your users ask for into the tracker your team already uses.