Collect in-app product feedback with Flutter
A complete Flutter integration: provider setup, floating and custom launchers, user identification, theming, and release checks.
Upstep’s Flutter SDK gives your app a floating launcher and native bottom sheet containing feedback submission and voting. All feedback lands in the same project board used by your web product.
Before you begin
- ✓A Flutter application with Material widgets
- ✓Flutter 3.0 or later
- ✓An Upstep project API key
Create a separate feedback project
Create an Upstep project for the mobile app and copy its API key. If web and mobile should share one backlog, use the same key in both clients. Otherwise give each product its own project so triage stays focused.
- Use a second project for staging or internal QA.
- The project key is used by the client SDK; do not substitute server secrets.
Install the Flutter package
Add the maintained package from pub.dev, then fetch dependencies as normal.
flutter pub add upstep_flutter
flutter pub getWrap the app and mount the sheet once
Upstep should wrap the part of the widget tree that needs to open feedback. The sheet and button belong inside a Stack so they render above your regular page content. Mount one FeedbackSheet for the whole app.
import 'package:flutter/material.dart';
import 'package:upstep_flutter/upstep_flutter.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Upstep(
apiKey: const String.fromEnvironment('UPSTEP_API_KEY'),
child: Scaffold(
appBar: AppBar(title: const Text('My app')),
body: Stack(
children: const [
Center(child: Text('Your app content')),
FeedbackSheet(),
FeedbackButton(),
],
),
),
),
);
}
}Pass the project key at build time
Keep the key out of source control. For local development and CI, pass it with dart-define. This makes staging and production values explicit in your build workflow.
flutter run --dart-define=UPSTEP_API_KEY=upstep_your_project_key
# Example release build
flutter build appbundle --dart-define=UPSTEP_API_KEY=upstep_your_project_keyIdentify a user after authentication
When the app already knows who is signed in, update the SDK controller with a stable account ID. This improves duplicate-vote protection across devices while still allowing anonymous feedback before sign-in.
// Call after authentication succeeds.
Upstep.of(context, listen: false).identify(currentUser.id);
// Optional: clear the identity after logout.
Upstep.of(context, listen: false).identify(null);Match the launcher to your app
The default button works well for most apps. You can choose its corner, label, icon, accent color, and theme mode. For a custom menu item, call openSheet from any widget inside Upstep instead of rendering FeedbackButton.
Upstep(
apiKey: apiKey,
accentColor: const Color(0xFFE05A33),
themeMode: UpstepThemeMode.auto,
child: Stack(
children: [
const HomeScreen(),
const FeedbackSheet(),
FeedbackButton(
position: FeedbackButtonPosition.bottomRight,
label: 'Send feedback',
),
],
),
)
// From a custom button:
onPressed: () => Upstep.of(context, listen: false).openSheet(),Release checklist
Test both the visible launcher and the full submission flow on physical devices. A simulator can miss safe-area and keyboard behaviour that affects a bottom sheet.
Check before moving on
- □Test Android and iOS with the keyboard open.
- □Check that the launcher clears navigation bars and home indicators.
- □Submit feedback while logged out and while logged in.
- □Verify the selected Upstep project receives the item.
- □Check light and dark themes on a real device.
Troubleshooting
Upstep.of throws an ancestor error.+
The calling widget is outside the Upstep widget. Move the button below the Upstep provider in the tree.
The sheet never opens.+
Mount FeedbackSheet once inside the same Upstep subtree as the launcher or custom trigger.
The API key is empty in a release build.+
Pass UPSTEP_API_KEY using --dart-define in the release command, not only when running locally.