appero_flutter 0.1.1
appero_flutter: ^0.1.1 copied to clipboard
Flutter plugin for Appero in-app feedback collection SDK
Appero SDK for Flutter #
The in-app feedback widget that drives organic growth.
Appero helps you capture user feedback at the right moments in your app journey. Built natively for iOS and Android with a Flutter-friendly API.
Features #
✅ Automatic Feedback Prompts - Smart triggers based on user experience ✅ Native UI Components - High-performance native dialogs on both platforms ✅ Offline Support - Queues experiences when offline, syncs automatically ✅ Customizable Themes - Full control over colors, typography, and rating icons ✅ Custom Fonts - Support for brand-consistent fonts on iOS and Android ✅ Analytics Integration - Easy integration with Firebase, GA4, and custom platforms ✅ Cross-Platform - Single Dart API for iOS (16.0+) and Android (7.0+)
Installation #
Add to your pubspec.yaml:
dependencies:
appero_flutter: ^0.1.1
Then run:
flutter pub get
Quick Start #
1. Initialize the SDK #
Initialize in your main() function before running your app:
import 'package:flutter/material.dart';
import 'package:appero_flutter/appero.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Appero.instance.initialize(
apiKey: 'your_api_key',
userId: 'user_123', // Optional but recommended
debug: true, // Enable debug logging
);
runApp(MyApp());
}
2. Log User Experiences #
Track user sentiment at key moments in your app:
// After successful action
await Appero.instance.log(
rating: ExperienceRating.positive,
context: 'Purchase completed successfully',
);
// After error
await Appero.instance.log(
rating: ExperienceRating.negative,
context: 'Checkout failed',
);
3. That's it! #
The SDK automatically handles feedback prompts based on logged experiences.
Core Concepts #
Experience Logging #
The SDK tracks user experiences and automatically determines when to show a feedback prompt based on the Experience Threshold configured in the Appero Dashboard.
Experience Ratings #
Five levels of sentiment tracking:
| Rating | Value | Emoji | Use Case |
|---|---|---|---|
ExperienceRating.strongPositive |
5 | 😄 | Very satisfied - Feature delights user |
ExperienceRating.positive |
4 | 🙂 | Satisfied - Smooth completion |
ExperienceRating.neutral |
3 | 😐 | Neutral - Completed but suboptimal |
ExperienceRating.negative |
2 | 🙁 | Dissatisfied - Failed operations |
ExperienceRating.strongNegative |
1 | 😡 | Very dissatisfied - Critical failures |
Feedback Flows #
Three distinct flows based on user sentiment:
- Positive Flow (😄 / 🙂): Thank you message, optional text feedback
- Neutral Flow (😐): Asks what could be improved
- Negative Flow (😡 / 🙁): Apologizes, asks what went wrong
When to Log Experiences #
Positive Experiences (😄 / 🙂):
- Successful completion of user flows
- Feature usage that delights users
- Smooth transactions or purchases
- Positive responses to content
Negative Experiences (😡 / 🙁):
- Failed operations or error states
- Server errors or timeouts
- Abandoned flows
- User frustration indicators
Neutral Experiences (😐):
- Completed but suboptimal flows
- Feature discovery without engagement
- Canceled operations
⚠️ Important: Avoid logging sensitive information (addresses, phone numbers, emails, payment details) in the context field.
API Reference #
Initialization #
await Appero.instance.initialize({
required String apiKey, // Your Appero API key
String? userId, // Optional user identifier
bool debug = false, // Enable debug logging
});
Logging Experiences #
await Appero.instance.log({
required ExperienceRating rating, // User sentiment level
String? context, // Optional context description
});
Manual Feedback Prompt #
// Show feedback UI manually
await Appero.instance.showFeedbackPrompt();
// Dismiss feedback UI
await Appero.instance.dismissPrompt();
Posting Feedback #
final success = await Appero.instance.postFeedback({
required int rating, // 1-5
String? feedback, // Optional text feedback
});
Theme Customization #
await Appero.instance.setTheme(ApperoTheme theme);
// Reset to system default (auto light/dark)
await Appero.instance.setTheme(null);
Reset SDK Data #
// Clear all locally stored data
await Appero.instance.reset();
Analytics Integration #
Track user feedback interactions in your analytics platform (Firebase Analytics, Mixpanel, GA4, etc.).
Setup Analytics Delegate #
import 'package:appero_flutter/appero.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
class MyAnalyticsDelegate implements ApperoAnalyticsDelegate {
final FirebaseAnalytics analytics = FirebaseAnalytics.instance;
@override
void onRatingSelected(int rating) {
// Called when user selects a rating (1-5)
analytics.logEvent(
name: 'appero_rating_selected',
parameters: {'rating': rating},
);
}
@override
void onFeedbackSubmitted(int rating, String? feedback) {
// Called when user submits feedback
analytics.logEvent(
name: 'appero_feedback_submitted',
parameters: {
'rating': rating,
'has_feedback': feedback != null,
'feedback_length': feedback?.length ?? 0,
},
);
}
}
// Set the delegate (typically in main())
Appero.instance.setAnalyticsDelegate(MyAnalyticsDelegate());
// Remove the delegate
Appero.instance.setAnalyticsDelegate(null);
Analytics Events #
The delegate receives two types of events:
-
Rating Selected - User taps a rating icon (1-5)
- Fires immediately when rating is selected
- Provides rating value only
-
Feedback Submitted - User submits feedback form
- Fires when user completes and submits feedback
- Provides rating and optional text feedback
Theme Customization #
Using Predefined Themes #
import 'package:appero_flutter/appero.dart';
// System theme (auto light/dark mode)
await Appero.instance.setTheme(systemTheme);
// Custom theme 1 (purple/lime yellow)
await Appero.instance.setTheme(customTheme1);
// Custom theme 2 (rose/purple with custom font)
await Appero.instance.setTheme(customTheme2);
// Reset to default
await Appero.instance.setTheme(null);
Creating Custom Themes #
const myTheme = ApperoTheme(
colors: ApperoColors(
surface: Color(0xFFFFFFFF), // Background
onSurface: Color(0xFF000000), // Primary text
onSurfaceVariant: Color(0xFF666666), // Secondary text
primary: Color(0xFF6200EE), // Accent color
onPrimary: Color(0xFFFFFFFF), // Text on accent
textFieldBackground: Color(0xFFF5F5F5), // Optional text field background override
cursor: Color(0xFF6200EE), // Optional cursor color override
),
typography: ApperoTypography(
titleLargeFontSize: 22.0,
titleLargeFontWeight: FontWeight.bold,
bodyMediumFontSize: 16.0,
bodyMediumFontWeight: FontWeight.normal,
labelLargeFontSize: 16.0,
labelLargeFontWeight: FontWeight.w500,
bodySmallFontSize: 14.0,
bodySmallFontWeight: FontWeight.normal,
fontFamily: null, // Use system font
),
usesSystemMaterial: false, // Disable frosted glass/system material effect
);
await Appero.instance.setTheme(myTheme);
Material Effect Control #
ApperoTheme includes a usesSystemMaterial flag:
true(default): enable system material styling where supportedfalse: disable system material styling
Optional Color Overrides #
ApperoColors supports optional overrides:
textFieldBackground: custom text field background colorcursor: custom text cursor color
If omitted, native defaults are used.
Platform support:
- iOS: supported
- Android: currently ignored by the native bridge until SDK parity is added
Custom Rating Images #
const myRatingImages = ApperoRatingImages(
strongNegative: 'assets/rating_1.png',
negative: 'assets/rating_2.png',
neutral: 'assets/rating_3.png',
positive: 'assets/rating_4.png',
strongPositive: 'assets/rating_5.png',
);
const myTheme = ApperoTheme(
colors: ApperoColors(...),
typography: ApperoTypography(...),
ratingImages: myRatingImages,
);
await Appero.instance.setTheme(myTheme);
Custom Fonts #
The SDK supports custom fonts on both iOS and Android.
Step 1: Add Font Files #
Create a fonts directory and add your font files:
your_app/
fonts/
YourFont-Regular.ttf
YourFont-Bold.ttf
Step 2: Register in pubspec.yaml #
flutter:
fonts:
- family: your_font_name
fonts:
- asset: fonts/YourFont-Regular.ttf
weight: 400
- asset: fonts/YourFont-Bold.ttf
weight: 700
Step 3: Setup for Android #
Copy the font to Android resources with lowercase, underscore-separated naming:
mkdir -p android/app/src/main/res/font
cp fonts/YourFont-Regular.ttf android/app/src/main/res/font/your_font_name.ttf
Android naming rules:
- Must be lowercase
- Use underscores instead of hyphens or spaces
- Should match the
fontFamilyname in your theme
Step 4: Use in Theme #
const myTheme = ApperoTheme(
colors: ApperoColors(...),
typography: ApperoTypography(
fontFamily: 'your_font_name', // Must match pubspec.yaml family name
),
);
await Appero.instance.setTheme(myTheme);
Platform Details #
iOS:
- ✅ Fonts loaded automatically from Flutter assets
- ✅ PostScript name extracted and registered dynamically
- ✅ No Info.plist configuration needed
- ✅ Supports TrueType (.ttf) and OpenType (.otf)
Android:
- ✅ Fonts must exist in
android/app/src/main/res/font/ - ✅ Filename must be lowercase with underscores
- ✅ Filename should match
fontFamilyvalue
Troubleshooting Fonts #
Font not displaying:
- Check font is declared in
pubspec.yaml - Run
flutter cleanand rebuild - Verify font file is not corrupted
- Check platform-specific logs for errors
Android font issues:
- Verify file exists in
android/app/src/main/res/font/ - Ensure lowercase filename with underscores
- Confirm filename matches
fontFamilyvalue
Example App #
The included example demonstrates all SDK features:
cd example
flutter run
Features demonstrated:
- SDK initialization with debug mode
- Analytics delegate integration
- Theme switching (System, Theme 1, Theme 2)
- Experience logging with all rating levels
- Manual feedback prompt triggering
- Error handling with SnackBar notifications
Architecture #
Flutter Layer #
- Language: Dart 3.0+
- State Management: Streams and StreamSubscription
- Platform Channels: MethodChannel & EventChannel
iOS Native #
- Language: Swift
- UI Framework: SwiftUI
- Reactive: Combine framework
- Minimum Version: iOS 16.0+
Android Native #
- Language: Kotlin
- UI Framework: Jetpack Compose
- Coroutines: kotlinx.coroutines
- Minimum SDK: 24 (Android 7.0)
Requirements #
- Flutter: >=3.24.0
- Dart: >=3.0.0 <4.0.0
- iOS: >=16.0
- Android: SDK 24+ (Android 7.0 Nougat)
Offline Support #
The SDK handles offline scenarios gracefully:
- ✅ Experiences logged while offline are queued locally
- ✅ Automatic retry when connection is restored
- ✅ Network state monitoring
- ✅ JSON file storage in secure app storage
Best Practices #
1. Initialize Early #
Initialize in main() before running your app to ensure the SDK is ready.
2. Use Meaningful Context #
Provide descriptive context when logging experiences to help identify patterns:
// Good ✅
await Appero.instance.log(
rating: ExperienceRating.positive,
context: 'Checkout completed - 3 items',
);
// Avoid ❌
await Appero.instance.log(
rating: ExperienceRating.positive,
context: 'success',
);
3. Set User ID #
Provide a user ID during initialization to track feedback across sessions:
await Appero.instance.initialize(
apiKey: 'your_key',
userId: userAuthId, // From your auth system
);
4. Analytics Integration #
Set up analytics delegate to track engagement metrics:
Appero.instance.setAnalyticsDelegate(MyAnalyticsDelegate());
5. Handle Errors Gracefully #
Wrap SDK calls in try-catch blocks:
try {
await Appero.instance.log(rating: rating, context: context);
} catch (e) {
// Handle error appropriately
debugPrint('Appero error: $e');
}
6. Theme Consistency #
Apply your theme once during initialization:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Appero.instance.initialize(apiKey: 'key');
await Appero.instance.setTheme(myCustomTheme);
runApp(MyApp());
}
7. Don't Log PII #
Never log personally identifiable information in the context field:
// Never do this ❌
await Appero.instance.log(
rating: rating,
context: 'User email: ${user.email}', // ❌ Don't log PII
);
// Do this instead ✅
await Appero.instance.log(
rating: rating,
context: 'User profile updated', // ✅ Generic context
);
Sample Use Cases #
E-commerce App #
class CheckoutPage extends StatelessWidget {
Future<void> _completeCheckout() async {
try {
await processPayment();
// Log positive experience
await Appero.instance.log(
rating: ExperienceRating.strongPositive,
context: 'Checkout completed',
);
navigateToConfirmation();
} catch (e) {
// Log negative experience
await Appero.instance.log(
rating: ExperienceRating.negative,
context: 'Checkout failed',
);
showError(e);
}
}
}
Content App #
class VideoPlayer extends StatefulWidget {
void _onVideoComplete() {
// User watched entire video - positive signal
Appero.instance.log(
rating: ExperienceRating.positive,
context: 'Video watched to completion',
);
}
void _onVideoError(String error) {
// Playback failed - negative signal
Appero.instance.log(
rating: ExperienceRating.strongNegative,
context: 'Video playback error',
);
}
}
Social App #
class PostCreation extends StatelessWidget {
Future<void> _publishPost() async {
final result = await api.createPost(content);
if (result.success) {
Appero.instance.log(
rating: ExperienceRating.strongPositive,
context: 'Post published successfully',
);
} else {
Appero.instance.log(
rating: ExperienceRating.negative,
context: 'Post publication failed',
);
}
}
}
Contributing #
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
License #
MIT License - See LICENSE for details.
Support #
- Issues: GitHub Issues
- Email: support@appero.co.uk
- Documentation: This README
Credits #
Developed by Pocketworks Mobile
Made with ❤️ for Flutter developers