Skip to content
Back to archipelago.banuacoder.com

Feature SDKs

How to collect in-app feedback in a Flutter app ​

Pro Reference for feedback_sdk: every variable, option and package it generates. Command: archipelago generate feedback_sdk

Generate the Feedback SDK, set the endpoint through a DI module, submit feedback and handle the three results, use the offline queue, and add feature voting.

What you'll learn ​

  • Generating the Feedback SDK with optional feature voting
  • Configuring the feedback endpoint through a DI module
  • Submitting feedback and handling the three result types
  • Using the offline queue and shake-to-feedback
  • Letting users vote on feature requests

Prerequisites ​

The brick generates storage_sdk and package_info_provider for you if they are missing. You do not install them first.

Step 1: Generate the Feedback SDK ​

bash
archipelago generate feedback_sdk

The prompts are:

  • includeFeatureVoting: true turns on feature-request listing and voting (default true)
  • defaultEndpoint: your feedback API URL (default empty, set it later in DI)

Or use a config file:

json
{
  "includeFeatureVoting": true,
  "defaultEndpoint": ""
}
bash
archipelago generate feedback_sdk --config feedback_config.json

Step 2: Understand the Generated Structure ​

text
features/feedback/
├── feedback_api/
│   └── lib/src/
│       ├── feedback_sdk.dart
│       └── models/        # Submission, category, config, results, feature request
└── feedback_impl/
    └── lib/src/
        ├── feedback_sdk_impl.dart
        ├── data/          # Remote datasource and the local queue
        └── di/

The post-generation hook registers FeedbackImplPackageModule in the app injector. The SDK is a lazy singleton. Nothing runs at boot.

Step 3: Configure the Endpoint ​

The generated FeedbackRegisterModule provides a default FeedbackConfig from your prompts. To change it, provide your own config in a DI module:

dart
@module
abstract class AppFeedbackModule {
  @lazySingleton
  FeedbackConfig get feedbackConfig => const FeedbackConfig(
    endpoint: 'https://api.yourapp.com/feedback',
    attachAppVersion: true,
    attachDeviceInfo: true,
    allowAnonymous: true,
    queueLimit: 50,
    featureVotingEnabled: true,
  );
}

Register this module before FeedbackImplPackageModule. The SDK uses its own Dio registered as @Named('feedback'), so it does not clash with the app Dio.

Step 4: Submit Feedback ​

Take FeedbackSDK by constructor and call submit():

dart
final result = await _feedback.submit(
  FeedbackSubmission(
    text: 'The checkout button is hard to find',
    category: FeedbackCategory.bug,
    screenshotPath: screenshotPath,
  ),
);

switch (result) {
  case FeedbackSubmitResultSubmitted():
    showSnackbar('Thanks for your feedback.');
  case FeedbackSubmitResultQueued():
    showSnackbar('Saved. We will send it when you are online.');
  case FeedbackSubmitResultFailed():
    showSnackbar('We could not send your feedback.');
}

FeedbackCategory has bug, suggestion, praise and other. Leave screenshotPath empty to send no attachment.

Step 5: Use the Offline Queue ​

The SDK queues submissions and votes in storage_sdk under the feedback namespace. It queues when the device is offline or the server returns a 5xx. A 4xx is a failure and is not queued.

Wire in connectivity so the queue drains when the network returns:

dart
connectivitySDK.stateStream.listen((state) {
  feedbackSDK.recordConnectivity(online: state.isOnline);
});

The app flushes the queue. Nothing runs at boot. You can also use getQueuedCount(), flushQueue() and clearQueue().

Step 6: Shake-to-Feedback (Optional) ​

The shake package is not a brick dependency. Add it to your host app pubspec.yaml:

yaml
dependencies:
  shake: ^2.2.0

Start the detector after DI is ready:

dart
ShakeDetector.autoStart(
  onPhoneShake: () async {
    final text = await promptUserForText();
    await feedbackSDK.submit(
      FeedbackSubmission(text: text, category: FeedbackCategory.other),
    );
  },
);

promptUserForText() shows a dialog. Use the UI pattern of your app.

Step 7: Feature Voting ​

This works when featureVotingEnabled is true:

dart
final requests = await feedbackSDK.getFeatureRequests(page: 1, pageSize: 20);
final result = await feedbackSDK.voteOnFeatureRequest(requests.first.id);
await feedbackSDK.submitFeatureRequest(title: 'Dark mode', description: '...');

Votes are deduplicated on the device. A second vote for the same request returns FeatureRequestVoteResultAlreadyVoted with no network call.

dart
switch (result) {
  case FeatureRequestVoteResultRecorded():
    showSnackbar('Thanks for your vote.');
  case FeatureRequestVoteResultAlreadyVoted():
    showSnackbar('You already voted for this.');
  case FeatureRequestVoteResultFailed(:final reason):
    showSnackbar('Could not vote: $reason');
}

With voting off, getFeatureRequests returns an empty list. The vote and submit calls return a failed result with the reason featureVotingDisabled.

Common Customizations ​

CustomizationWhere to Change
Endpoint and queue limitFeedbackConfig in your DI module
Turn off version or device infoattachAppVersion and attachDeviceInfo
Rate-limit shakeTrack the last submission time before submit()
Disable shake in release buildsGuard ShakeDetector.autoStart with !kReleaseMode

Next Steps ​