A Flutter package for collecting in-app user feedback — drop-in widget, voice input, offline queue, screenshot capture, and a pluggable backend interface.
FeedbackButton— FAB that opens a feedback form in a bottom sheetFeedbackWidget— inline form for embedding anywhere in your UI- 8 categories — Bug, Suggestion, UI/UX, Performance, Translation, Feature Request, Accessibility, Other
- Voice input — optional speech-to-text with
SpeechRecognitionService - Screenshot attachment — gallery picker + screen capture callback (up to 5 images)
- Offline queue —
QueuedBackend+SharedPrefsQueuepersist feedback across restarts - Pluggable backend — implement
FeedbackBackendfor any service - Built-in
WebhookBackend— POST to Slack, Discord, n8n, or any HTTPS endpoint - Custom theming —
FeedbackThemeData/FeedbackThemefor colours, padding, corner radius LocalFeedbackBackend+FeedbackDevViewer— debug-only local sink with in-app viewer
dependencies:
flutter_feedback_kit: ^0.1.0Android — add to AndroidManifest.xml if using voice input:
<uses-permission android:name="android.permission.RECORD_AUDIO"/>iOS — add to Info.plist if using voice input:
<key>NSSpeechRecognitionUsageDescription</key>
<string>Used for voice feedback input</string>
<key>NSMicrophoneUsageDescription</key>
<string>Used for voice feedback input</string>import 'package:flutter_feedback_kit/flutter_feedback_kit.dart';
Scaffold(
floatingActionButton: FeedbackButton(
backend: WebhookBackend(url: 'https://your-webhook.example.com/feedback'),
appVersion: '1.0.0',
onSuccess: () => print('Sent!'),
onError: (e) => print('Error: $e'),
),
body: MyApp(),
);FeedbackWidget(
backend: WebhookBackend(url: 'https://your-webhook.example.com/feedback'),
appVersion: '1.0.0',
onSuccess: () => Navigator.pop(context),
)FeedbackWidget(
backend: myBackend,
appVersion: '1.0.0',
speechService: SpeechRecognitionService(), // mic button appears automatically
)Wrap any backend with QueuedBackend — entries are persisted when offline and
flushed on the next call when connectivity is restored.
final backend = QueuedBackend(
backend: WebhookBackend(url: 'https://example.com/feedback'),
queue: SharedPrefsQueue(),
);
// Later, when connectivity is restored:
await (backend as QueuedBackend).flushQueue();WebhookBackend(
url: 'https://hooks.slack.com/services/...',
payloadBuilder: (entry) => {
'text': '*[${entry.category.label}]* ${entry.message}\n'
'_${entry.platform} • v${entry.appVersion}_',
},
)class FirebaseBackend implements FeedbackBackend {
@override
Future<void> submit(FeedbackEntry entry) async {
await FirebaseFirestore.instance
.collection('feedback')
.add(entry.toJson());
}
}FeedbackButton(
backend: myBackend,
appVersion: '1.0.0',
theme: FeedbackThemeData(
submitButtonColor: Colors.teal,
backgroundColor: const Color(0xFF1E1E2E),
contentPadding: const EdgeInsets.all(24),
sheetBorderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
),
)Or wrap with FeedbackTheme for inherited theming:
FeedbackTheme(
data: FeedbackThemeData(submitButtonColor: Colors.deepPurple),
child: FeedbackWidget(backend: myBackend, appVersion: '1.0.0'),
)Import from the separate local export to keep the main package web-compatible:
import 'package:path_provider/path_provider.dart';
import 'package:flutter_feedback_kit/local.dart'; // LocalFeedbackBackend + FeedbackDevViewer
final dir = await getApplicationDocumentsDirectory();
final feedbackDir = Directory('${dir.path}/feedback');
// Use LocalFeedbackBackend in debug builds:
final backend = kDebugMode
? LocalFeedbackBackend(directory: feedbackDir)
: WebhookBackend(url: 'https://example.com/feedback');
// Open the in-app viewer:
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => FeedbackDevViewer(directory: feedbackDir),
),
);| Field | Type | Description |
|---|---|---|
category |
FeedbackCategory |
Selected category |
message |
String |
User message (max 2 000 chars) |
platform |
String |
android / ios / web |
appVersion |
String |
App version string |
createdAt |
DateTime |
UTC submission timestamp |
screenshots |
List<String> |
Base64-encoded PNG images |
bug · suggestion · ui · performance · translation · featureRequest · accessibility · other
| Property | Type | Default |
|---|---|---|
backgroundColor |
Color? |
ColorScheme.surface |
submitButtonColor |
Color? |
ColorScheme.primary |
contentPadding |
EdgeInsets |
EdgeInsets.all(16) |
sheetBorderRadius |
BorderRadius |
vertical(top: Radius.circular(16)) |