feedbackjar 1.2.0
feedbackjar: ^1.2.0 copied to clipboard
Flutter SDK for FeedbackJar — collect user feedback with automatic device metadata. You build the UI; the SDK handles submission and listing.
FeedbackJar Flutter SDK #
A lightweight Flutter SDK for collecting user feedback. You build your own form — the SDK handles submission (enriched with device metadata) and fetching the public feedback list.
- Min SDK: Dart 3.0 / Flutter 3.10
- Platforms: Android, iOS
- Package: pub.dev
- License: MIT
Installation #
Add to your pubspec.yaml:
dependencies:
feedbackjar: ^1.0.0
Then run:
flutter pub get
Setup #
Initialize once before use — call configure in main() before runApp, or in your root widget's constructor. You need your widget ID from the FeedbackJar dashboard.
import 'package:feedbackjar/feedbackjar.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
FeedbackJar.configure('your-widget-id');
runApp(const MyApp());
}
Submitting feedback #
Submissions can be anonymous, or include a submitter name/email if you collect them in your own form. Each submission automatically carries device metadata (OS version, device model, screen size, app version, locale).
async/await (recommended) #
final result = await FeedbackJar.shared.submit(userText);
switch (result) {
case FeedbackSuccess(:final value):
print('Submitted: ${value.postId} (${value.type})');
// show a success state in your UI
case FeedbackFailure(:final error):
print('Failed: $error');
// show an error state
}
Callback (no async context needed) #
FeedbackJar.shared.submitCallback(userText, (result) {
switch (result) {
case FeedbackSuccess(:final value):
// show success state
case FeedbackFailure(:final error):
// show error state
}
});
Note: the server applies rate limiting (5 submissions per 15 minutes per IP). Handle the failure case in your UI.
Custom properties #
Attach your own key/value context to a submission — merged into the auto-collected app metadata (alongside packageName, versionName, versionCode). Values should be String, num, or bool; nested maps/lists aren't supported.
final result = await FeedbackJar.shared.submit(
userText,
properties: {'flavor': 'foss', 'plan': 'pro'},
);
Checking whether to ask for name/email #
The organization's dashboard settings ("Ask for Name" / "Ask for Email") control whether submitters should be prompted. The SDK doesn't render any UI itself, so read this before building your own form:
final result = await FeedbackJar.shared.getConfig();
if (result case FeedbackSuccess(:final value)) {
showNameField = value.collectName;
showEmailField = value.collectEmail;
}
Remembering submitter identity #
Name/email passed to submit are automatically remembered and reused on later calls, so you only need to ask once. Manage this directly with setIdentity / getIdentity / clearIdentity:
await FeedbackJar.shared.setIdentity(name: 'Ada Lovelace', email: 'ada@example.com');
final identity = await FeedbackJar.shared.getIdentity();
print(identity.name);
// e.g. on logout
await FeedbackJar.shared.clearIdentity();
Listing feedback #
Fetch the public feedback feed for your organization. Supports pagination via a cursor.
async/await #
final result = await FeedbackJar.shared.listFeedback(limit: 20);
if (result case FeedbackSuccess(:final value)) {
for (final post in value.posts) {
print('${post.title} — ${post.upvotes} upvotes, ${post.status}');
}
// value.nextCursor is non-null when more pages exist
}
Pagination #
String? cursor;
Future<void> loadNextPage() async {
final result = await FeedbackJar.shared.listFeedback(
limit: 20,
cursor: cursor,
);
if (result case FeedbackSuccess(:final value)) {
renderPosts(value.posts);
cursor = value.nextCursor; // pass this back in for the next page
}
}
Callback #
FeedbackJar.shared.listFeedbackCallback(
limit: 20,
callback: (result) {
if (result case FeedbackSuccess(:final value)) {
// render value.posts
}
},
);
API reference #
FeedbackJar #
| Method | Description |
|---|---|
configure(widgetId) |
Configure the SDK. Call once before anything else. |
shared.submit(content, {email?, name?, properties?}) |
Submit feedback, optionally with custom properties merged into app metadata. Returns Future<FeedbackResult<FeedbackResponse>>. |
shared.submitCallback(content, callback, {email?, name?, properties?}) |
Callback variant. |
shared.listFeedback({boardId?, limit = 20, cursor?}) |
List public feedback. limit is clamped to 1–50. |
shared.listFeedbackCallback({boardId?, limit, cursor?, callback}) |
Callback variant. |
shared.getConfig() |
Fetch whether the org asks for name/email. Returns Future<FeedbackResult<WidgetConfig>>. |
shared.getConfigCallback(callback) |
Callback variant. |
shared.setIdentity({name?, email?}) |
Remember a submitter's name/email for future submit calls. |
shared.getIdentity() |
The currently remembered identity, if any. Returns Future<FeedbackIdentity>. |
shared.clearIdentity() |
Forget the remembered identity. |
FeedbackResult<T> #
A sealed class with two subtypes. Use Dart 3 pattern matching:
switch (result) {
case FeedbackSuccess(:final value):
// T value
case FeedbackFailure(:final error):
// Object error
}
FeedbackResponse #
class FeedbackResponse {
final String postId;
final String title; // AI-generated title for the submission
final String type; // e.g. FEEDBACK, BUG, FEATURE_REQUEST
final String boardId;
}
FeedbackPost #
class FeedbackPost {
final String id;
final String title;
final String content;
final String type;
final String status; // OPEN, IN_PROGRESS, COMPLETED, ...
final String slug;
final String boardId;
final int voteCount;
final int commentCount;
final int upvotes;
final String? authorName;
final String createdAt; // ISO-8601
final String updatedAt; // ISO-8601
}
FeedbackListResult #
class FeedbackListResult {
final List<FeedbackPost> posts;
final String? nextCursor; // null when there are no more pages
}
WidgetConfig #
class WidgetConfig {
final bool collectName; // org asks for the submitter's name
final bool collectEmail; // org asks for the submitter's email
}
FeedbackIdentity #
class FeedbackIdentity {
final String? name;
final String? email;
}
Notes #
- Feedback can be submitted anonymously, or with a name/email — the SDK never requires either.
- Name/email are persisted on-device via
shared_preferencesso they survive app restarts. - Private boards and non-public posts are never returned by
listFeedback. - All methods return a
FeedbackResult; nothing throws on network or HTTP errors. - Requires
WidgetsFlutterBinding.ensureInitialized()beforeconfigureif called beforerunApp.