Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

14 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🌟 Flutter Auto Review - Reformed Edition

Version 2.0.0 - Complete architectural rewrite following Clean Architecture + SOLID principles

πŸ“¦ Package Structure

flutter_auto_review/
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ flutter_auto_review.dart              # Main export file
β”‚   └── src/
β”‚       β”œβ”€β”€ core/
β”‚       β”‚   └── app_logger.dart                # Custom logger utility
β”‚       β”œβ”€β”€ domain/
β”‚       β”‚   β”œβ”€β”€ enums/
β”‚       β”‚   β”‚   β”œβ”€β”€ trigger_type.dart          # Trigger type definitions
β”‚       β”‚   β”‚   └── dialog_action.dart         # Dialog action types
β”‚       β”‚   β”œβ”€β”€ models/
β”‚       β”‚   β”‚   β”œβ”€β”€ rate_us_config.dart        # Configuration model
β”‚       β”‚   β”‚   β”œβ”€β”€ rate_us_state.dart         # State model
β”‚       β”‚   β”‚   └── rate_us_analytics.dart     # Analytics interface
β”‚       β”‚   └── services/
β”‚       β”‚       β”œβ”€β”€ rate_us_manager.dart       # Main manager (Facade)
β”‚       β”‚       └── rate_us_dialog_strategy.dart # Decision engine
β”‚       β”œβ”€β”€ data/
β”‚       β”‚   └── repositories/
β”‚       β”‚       └── rate_us_repository.dart    # Storage repository
β”‚       └── presentation/
β”‚           └── widgets/
β”‚               └── rate_us_custom_dialog.dart # Custom UI dialog
β”œβ”€β”€ example/
β”‚   └── lib/
β”‚       └── main.dart                          # Complete example app
β”œβ”€β”€ test/
β”‚   └── rate_us_manager_test.dart             # Unit tests
β”œβ”€β”€ pubspec.yaml
└── README.md

πŸš€ Installation

Add to your pubspec.yaml:

dependencies:
  flutter_auto_review: ^2.0.0
  
  # Required dependencies (already included):
  # - shared_preferences: ^2.2.0
  # - in_app_review: ^2.0.8
  # - url_launcher: ^6.2.0
  # - package_info_plus: ^5.0.0

πŸ“– Quick Start

1. Initialize in main.dart

import 'package:flutter_auto_review/flutter_auto_review.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await RateUsManager().init(
    config: const RateUsConfig(
      rateUsInitialize: 1,      // 1 = enabled
      minAppOpens: 2,            // Trigger after 2 opens
      minEvents: 3,              // Trigger after 3 custom events
      autoTrigger: 10,           // Trigger after 10 screen changes
      exitTrigger: 1,            // Show on app exit
      cooldownDays: 2,           // 2 day cooldown after dismiss
      maxCustomPerDay: 3,        // Max 3 custom dialogs per day
      appStoreId: 'YOUR_ID',     // iOS App Store ID
    ),
    analytics: RateUsAnalytics(
      onEvent: (eventName, parameters) {
        // Send to Firebase Analytics
        FirebaseAnalytics.instance.logEvent(
          name: eventName,
          parameters: parameters,
        );
      },
    ),
  );

  runApp(const MyApp());
}

2. Add Lifecycle Observer

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  final RateUsManager _manager = RateUsManager();

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    
    // Check app open trigger
    _manager.onAppOpen(context);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.paused) {
      _manager.onAppExit(context);
    }
  }
}

3. Trigger on Custom Events

// After user completes an action
await _manager.onCustomEvent(context);

// On screen transitions
await _manager.onScreenTransition(context);

// Manual trigger from settings
await _manager.onSettingsTrigger(context);

🎯 How It Works

Decision Flow (Reformed Strategy)

β”Œβ”€ ANY TRIGGER OCCURS
β”‚
β”œβ”€ [GATE 1] Feature enabled? β†’ NO: ABORT
β”œβ”€ [GATE 2] Already rated custom? β†’ YES: NATIVE_ONLY_BRANCH
β”œβ”€ [GATE 3] In cooldown? β†’ YES: ABORT
β”œβ”€ [GATE 4] Native called today? β†’ YES: CUSTOM_BRANCH
β”‚
└─ Execute: NATIVE_FIRST_BRANCH
   β”œβ”€ Call native dialog
   β”œβ”€ Wait 2 minutes
   └─ Show custom dialog (if still in session)

Key Improvements Over v1.0

Feature v1.0 (Old) v2.0 (Reformed)
Native attempts Random 1x per day, strategic
Custom dialog shows <0.01% 70-85% of users
Architecture Monolithic Clean Architecture
State management Mixed Repository pattern
Decision logic Scattered Centralized strategy
Daily reset Manual Automatic at midnight
Cooldown Broken Working properly
Analytics Basic 15+ detailed events

πŸ“Š Analytics Events

Gate Events

  • rate_us_gate_1_failed - Feature disabled
  • rate_us_gate_2_bypass - User already rated via custom
  • rate_us_gate_3_cooldown - In cooldown period
  • rate_us_gate_4_native_today - Native already called today

Dialog Events

  • rate_us_native_called - Native dialog attempted
  • rate_us_native_failed - Native dialog unavailable
  • rate_us_custom_shown - Custom dialog displayed
  • rate_us_custom_submit - User clicked "Rate on Play Store"
  • rate_us_custom_dismiss - User dismissed dialog
  • rate_us_playstore_redirect - Play Store URL opened

Trigger Events

  • rate_us_trigger - Trigger condition met (with type)
  • rate_us_initialized - Manager initialized
  • rate_us_daily_reset - Daily flags reset

🎨 Custom Dialog Features

Enhanced UX

  • ⭐ Interactive 5-star rating
  • 🎯 Contextual messages based on trigger type
  • βœ… Pre-qualification (β‰₯4 stars β†’ Play Store)
  • πŸ“ Feedback capture (<4 stars β†’ Private feedback)
  • 🎨 Modern Material 3 design

Sample Dialog Flow

User Action          | Result
---------------------|--------------------------------
Select 5 stars       | "Share on Play Store!" button
Click submit         | β†’ Redirect to Play Store
                     | β†’ Set assumed_rated_custom = true
                     | β†’ Only native dialogs from now on
---------------------|--------------------------------
Select 3 stars       | "Send us feedback" button
Click feedback       | β†’ Show feedback form (private)
                     | β†’ Protects public rating
---------------------|--------------------------------
Click "Later"        | β†’ Apply 2-day cooldown
                     | β†’ Increment dismissal count

πŸ§ͺ Testing Guide

Reset for Testing

// Reset all stored data
await RateUsManager().reset();

// Check current state
final state = await RateUsManager().getState();
print('Native called today: ${state.nativeCalledToday}');
print('Assumed rated: ${state.assumedRatedCustom}');
print('In cooldown: ${state.isInCooldown}');

Test Scenarios

// Scenario 1: Day 1 New User
await manager.onAppOpen(context);
// β†’ Shows native immediately
// β†’ After 2 min, shows custom

// Scenario 2: Returning User (Day 2)
await manager.onCustomEvent(context);
// β†’ Shows native (first of day)
// Next trigger same day:
// β†’ Shows custom only

// Scenario 3: User Rated via Custom
// State: assumed_rated_custom = true
await manager.onAppOpen(context);
// β†’ Shows native only (respectful follow-up)
// β†’ No more custom dialogs

// Scenario 4: User Dismissed 3 Times
// State: customDismissalCount = 3
await manager.onAppOpen(context);
// β†’ Permanent opt-out from custom
// β†’ Only native (1x/day) continues

πŸ“ˆ Expected Performance

Before Reform (Your Current Data)

Total Users:     1,182
Native Triggered: 350 (30%)
Custom Shown:     2 (<0.01%)  ← PROBLEM
Play Store Reviews: 18 (1.5%)

After Reform (Projected)

Total Users:     1,182
Native Triggered: 1,182 (100%)
Custom Shown:     1,000 (85%)  ← FIXED!
Play Store Redirects: 750 (63%)
Actual Reviews:   150-200 (12-17%)

Result: 8-10x improvement in reviews

πŸ”§ Configuration Options

class RateUsConfig {
  final int rateUsInitialize;    // 1=on, 0=off
  final int minAppOpens;         // Default: 2
  final int minEvents;           // Default: 3
  final int autoTrigger;         // Default: 10
  final int exitTrigger;         // 1=on, 0=off
  final int cooldownDays;        // Default: 2 days
  final int maxCustomPerDay;     // Default: 3
  final String? appStoreId;      // iOS only
}

Removed Parameters

  • ❌ minDaysSinceInstall - Every day matters for new apps
  • ❌ Days-based conditions - Replaced with smarter triggers

πŸ—οΈ Architecture Principles

Clean Architecture Layers

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Presentation Layer                 β”‚
β”‚  └─ rate_us_custom_dialog.dart     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Domain Layer                       β”‚
β”‚  β”œβ”€ rate_us_manager.dart (Facade)  β”‚
β”‚  β”œβ”€ rate_us_dialog_strategy.dart   β”‚
β”‚  └─ Models + Enums                  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Data Layer                         β”‚
β”‚  └─ rate_us_repository.dart        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

SOLID Principles Applied

Single Responsibility

  • RateUsManager - Facade/Coordinator
  • RateUsDialogStrategy - Decision logic only
  • RateUsRepository - Storage operations only
  • RateUsCustomDialog - UI rendering only

Open/Closed

  • Extend via strategy pattern
  • New triggers don't require manager changes

Liskov Substitution

  • All dialog actions implement consistent interface

Interface Segregation

  • Analytics optional (not forced)
  • Config modular (pick what you need)

Dependency Inversion

  • Manager depends on abstractions (repository)
  • Not on concrete implementations

πŸ› Debugging

Enable Verbose Logging

// AppLogger automatically logs in debug mode
// Logs are color-coded and structured

// Example output:
// [14:23:45] [βœ… SUCCESS] [Manager] Manager initialized
// [14:23:50] [πŸ” DEBUG] [Strategy] Trigger: custom_event
// [14:23:50] [⚠️  WARN] [Strategy] Gate 3 FAILED: In cooldown

Check Storage Keys

// Access repository directly for debugging
final state = await manager.getState();

debugPrint('First install: ${state.firstInstallDate}');
debugPrint('Last native: ${state.lastNativeAttemptDate}');
debugPrint('Last custom: ${state.lastCustomShownDate}');
debugPrint('Native today: ${state.nativeCalledToday}');
debugPrint('Rated custom: ${state.assumedRatedCustom}');
debugPrint('Cooldown: ${state.isInCooldown}');
debugPrint('Daily custom count: ${state.dailyCustomCount}');
debugPrint('Total dismissals: ${state.customDismissalCount}');

πŸ” Privacy & Best Practices

  1. Respect User Choice

    • 3 dismissals = permanent custom opt-out
    • Cooldown enforced strictly
    • Manual trigger always available
  2. Non-Intrusive

    • Max 1 native per day
    • Max 3 custom per day
    • Smart trigger timing
  3. Transparent

    • All events logged to analytics
    • State visible for debugging
    • Clear user messages

πŸ“ Migration from v1.0

Breaking Changes

// OLD v1.0
await manager.onMinAppOpens();
await manager.onCustomEvent();

// NEW v2.0
await manager.onAppOpen(context);
await manager.onCustomEvent(context);

// All methods now require BuildContext
// This enables immediate dialog display

Removed Methods

  • ❌ onMinDaysSinceInstall() - No longer needed
  • ❌ showRateDialog() - Use triggers instead

New Methods

  • βœ… onTrigger() - Unified trigger method
  • βœ… getState() - Debug current state

🀝 Contributing

We welcome contributions! Please:

  1. Follow the existing architecture
  2. Add tests for new features
  3. Update documentation
  4. Use AppLogger for logging

πŸ“„ License

MIT License - See LICENSE file

πŸ™ Credits

Reformed by: [Romit Donga] Original package: flutter_auto_review v1.0 Inspired by: Real-world analytics data (18 reviews β†’ 150+ goal)


πŸ“ž Support


Made with ❀️ for the Flutter community

"From 18 reviews to 200+ - Because every user's voice matters"

About

easy to get more review

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages