Documentation
Add a feature-requests board to your Flutter app, your website, or any other app in five minutes. Your users suggest, vote, and comment - you ship the right things. Every integration talks to the same project, so votes from every platform add up on one board.
Jump to: Flutter SDK · Web widget · Hosted board · iOS, Android, React Native, KMP · REST API
1. Sign up and create a project
Sign up for a free account. From the dashboard, create a project - that's the container for one app's feature requests. Each project gets its own API key. You'll find the key in Settings → API Keys.
The free tier covers 1 project and 10 feature requests, no card required. Upgrade later if you need more.
2. Install the Flutter SDK
flutter pub add pickafeatureRequires Flutter 3.16+ and Dart 3.7+. The SDK pulls in http, shared_preferences, uuid, device_info_plus, package_info_plus, and url_launcher as transitive dependencies.
3. Initialize at app start
import 'package:flutter/material.dart';
import 'package:pickafeature/pickafeature.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await PickAFeature.initialize(
config: PickAFeatureConfig(
apiKey: 'YOUR_API_KEY',
),
);
runApp(const MyApp());
}Replace YOUR_API_KEY with the key from your project's settings page. Don't commit it to git - load from a build config or environment variable.
4. Show the feature requests screen
Push the drop-in screen wherever makes sense - usually from a settings menu or a navigation bar item.
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => PickAFeatureScreen(
config: PickAFeatureConfig(
apiKey: 'YOUR_API_KEY',
customTitle: 'What should we build next?',
customSubtitle: 'Suggest, vote, comment',
),
),
),
);The screen shows the full experience: list of approved/completed requests, voting, comments, and a "+" button that opens the submission form (separate title and description fields).
Theming and configuration
Customize via PickAFeatureConfig:
PickAFeatureConfig(
apiKey: 'YOUR_API_KEY',
primaryColor: const Color(0xFF6366F1),
backgroundColor: Colors.white,
textColor: const Color(0xFF1F2937),
borderRadius: 12.0,
customTitle: 'Feature requests',
customSubtitle: 'Tell us what you need',
customPlaceholder: 'Describe the feature you want…',
customSubmitText: 'Submit',
showEmailField: true,
)For development, set demoMode: true to render the SDK against canned data without hitting the network - useful for App Store screenshots and offline work.
Identify users
Attach the current user's email to future feedback submissions so requests can be attributed in the dashboard. Call once after sign-in.
await PickAFeature.updateUser(
email: '[email protected]',
name: 'Jane',
);
// On sign-out
await PickAFeature.clearUserData();Anonymous users still work - the SDK generates a stable device UUID stored locally with shared_preferences.
Programmatic API
Build your own UI instead of (or alongside) the drop-in screen:
// List approved + completed requests
final requests = await PickAFeature.getFeedbackRequests();
// Upvote a request
await PickAFeature.upvoteFeedback(requestId);
// Ping the backend
final ok = await PickAFeature.testConnection();Error handling
SDK methods throw ApiException on non-2xx responses and network failures. Inspect the typed properties to handle each case:
try {
await PickAFeature.upvoteFeedback(id);
} on ApiException catch (e) {
if (e.isAuthError) {
// 401/403 - bad or revoked API key
} else if (e.isRateLimit) {
// 429 - server is throttling; retry after e.retryAfter seconds
} else if (e.isNetworkError) {
// No internet / DNS failure
} else {
// Other server error - e.statusCode, e.message
}
}Web widget (any website or web app)
One script tag adds the same board to a website, SaaS dashboard, React/Next.js app, or a Flutter web build. It renders in a Shadow DOM so your CSS and ours never collide, follows light/dark mode, and shares the project with your mobile app.
<script
src="https://pickafeature.com/sdk/v1/pickafeature.js"
data-api-key="YOUR_API_KEY"
defer
></script>
<!-- Any element with this attribute opens the board -->
<button data-pickafeature-open>Feature requests</button>By default the script also renders a floating "Feedback" button. Tune it with data-launcher="false", data-position="bottom-left", data-label="Ideas", data-theme="light|dark|auto", and data-primary-color="#6366f1".
Prefer a package? npm i pickafeature ships ESM, CommonJS, and TypeScript types. This is what you want in React, Next.js, Vue, Svelte, or anywhere you know who the signed-in user is:
import PickAFeature from "pickafeature";
PickAFeature.init({
apiKey: "YOUR_API_KEY",
theme: "auto", // "light" | "dark" | "auto"
primaryColor: "#6366f1",
launcher: false, // you'll open it from your own button
user: { id: user.id, email: user.email }, // optional, votes follow the user
strings: { title: "What should we build next?" },
onSubmit: ({ id, title }) => analytics.track("feature_request_submitted", { id, title }),
});
// Open from anywhere - a settings row, a nav item, a help menu
PickAFeature.open();
// After sign-in / sign-out
PickAFeature.identify({ id: user.id, email: user.email });
PickAFeature.reset();React and Next.js (client component):
"use client";
import { useEffect } from "react";
import PickAFeature from "pickafeature";
export function FeatureRequestsButton({ user }: { user?: { id: string; email?: string } }) {
useEffect(() => {
PickAFeature.init({ apiKey: "YOUR_API_KEY", user });
return () => PickAFeature.destroy();
}, [user?.id, user?.email]);
return <button onClick={() => PickAFeature.open()}>Feature requests</button>;
}The script tag exposes the same API on window.PickAFeature if you would rather not bundle it. The package is safe to import during server rendering; it only touches the DOM when you call init. The widget is open source: github.com/pickafeature/sdk-web.
Every piece of UI text can be overridden through strings, so localizing the widget is a matter of passing your own translations. The API key is a public project key - the same kind that ships inside a mobile binary - and the server rate-limits it per project.
Want your own UI? PickAFeature.client(apiKey) returns a headless client with list(), submit(), vote(), comments(), and addComment().
Embed the board inline
Prefer a full page instead of a popup, for example a "Roadmap" or "Feedback" page in your app? Point the widget at a container and it renders there, always open, with no overlay or launcher:
<div id="feedback-board"></div>
<script
src="https://pickafeature.com/sdk/v1/pickafeature.js"
data-api-key="YOUR_API_KEY"
data-container="#feedback-board"
defer
></script>
<!-- or from JavaScript -->
<script>
PickAFeature.init({ apiKey: "YOUR_API_KEY", container: "#feedback-board" });
</script>Hosted board (no code)
Every project gets a public board at pickafeature.com/b/your-project. Open Projects in the dashboard to copy the link or change the slug. It is the same board your SDKs show: suggest, vote, and comment, with votes counted once per browser. Use it when you want a link instead of an integration:
- Link to it from your website footer, help center, or changelog.
- Share it on social when you ask "what should we build next?".
- Open it from any app on any platform, in an in-app browser. See the next section.
Boards are public by design, so anyone with the link can vote. Only requests you have moved to planned, in progress, or completed are shown; new submissions stay private until you review them.
iOS, Android, React Native, Kotlin Multiplatform
No native SDK is needed: open your hosted board in an in-app browser. It keeps the user inside your app, follows the system theme, and remembers their votes. Replace the URL with your own board link from the dashboard. Longer guides with the trade-offs for each platform: iOS, Android, React Native, Kotlin Multiplatform, and the hosted board.
React Native (Expo)
import * as WebBrowser from "expo-web-browser";
// e.g. from a "Feature requests" row in your settings screen
await WebBrowser.openBrowserAsync("https://pickafeature.com/b/your-project", {
presentationStyle: WebBrowser.WebBrowserPresentationStyle.PAGE_SHEET,
});Bare React Native: use react-native-inappbrowser-reborn or plain Linking.openURL().
iOS (Swift)
import SafariServices
let url = URL(string: "https://pickafeature.com/b/your-project")!
let safari = SFSafariViewController(url: url)
present(safari, animated: true)
// SwiftUI: wrap SFSafariViewController in UIViewControllerRepresentable,
// or use Link(destination:) for a plain Safari hand-off.Android (Kotlin)
// build.gradle: implementation("androidx.browser:browser:1.8.0")
import androidx.browser.customtabs.CustomTabsIntent
CustomTabsIntent.Builder()
.build()
.launchUrl(context, Uri.parse("https://pickafeature.com/b/your-project"))Kotlin Multiplatform
// commonMain
expect fun openFeatureRequests(url: String)
// androidMain: CustomTabsIntent as above
// iosMain:
actual fun openFeatureRequests(url: String) {
val controller = SFSafariViewController(NSURL(string = url))
UIApplication.sharedApplication.keyWindow?.rootViewController
?.presentViewController(controller, animated = true, completion = null)
}Want the board rendered natively instead? The REST API is four endpoints, and the Flutter SDK source is a reference implementation. Native SDKs are on the roadmap; tell us which platform you need and we prioritise by demand.
REST API (any other client)
Building for another platform? The backend is plain REST + JSON. Authenticate with your project's API key as the x-api-key header. Base URL is https://pickafeature.com/api/v1/sdk.
POST /feedback Submit a feature request
GET /feedback List approved + completed requests
POST /vote Upvote or remove an upvote
GET /comments?... List comments for a request
POST /comments Add a commentAll endpoints support CORS and return rate-limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset) plus Retry-After on 429s. Open an issue if you'd like a full reference page.
Help and feedback
Bugs, questions, or feature requests for the SDK itself: GitHub Issues. General product questions: [email protected].