smart_review_prompter 1.1.0 copy "smart_review_prompter: ^1.1.0" to clipboard
smart_review_prompter: ^1.1.0 copied to clipboard

A smart way to show in-app review prompts based on user engagement, such as app open count and days since install.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:smart_review_prompter/smart_review_prompter.dart';

void main() async {
  // Required for asynchronous initialization
  WidgetsFlutterBinding.ensureInitialized();

  // 1. Initialize the prompter package
  await SmartReviewPrompter.instance.initialize();

  // 2. Increment the app open count automatically on startup
  await SmartReviewPrompter.instance.incrementAppOpenCount();

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Smart Review Prompter',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF2CAC5C), // Brand Green Color
          primary: const Color(0xFF2CAC5C),
          secondary: const Color(0xFF2563EB), // Royal Blue
        ),
      ),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  int _appOpenCount = 0;
  int _daysSinceInstall = 0;
  int _promptCount = 0;
  bool _hasAlreadyRated = false;
  String _lastPromptDate = 'Never';

  // Controller inputs for customizable testing
  final TextEditingController _minOpensController = TextEditingController(text: '3');
  final TextEditingController _minDaysController = TextEditingController(text: '0');
  final TextEditingController _maxPromptsController = TextEditingController(text: '3');
  final TextEditingController _daysCooldownController = TextEditingController(text: '0');
  final TextEditingController _opensCooldownController = TextEditingController(text: '2');

  @override
  void initState() {
    super.initState();
    _refreshStats();
  }

  @override
  void dispose() {
    _minOpensController.dispose();
    _minDaysController.dispose();
    _maxPromptsController.dispose();
    _daysCooldownController.dispose();
    _opensCooldownController.dispose();
    super.dispose();
  }

  /// Re-fetch and refresh all prompter statistics
  Future<void> _refreshStats() async {
    final opens = await SmartReviewPrompter.instance.getAppOpenCount();
    final days = await SmartReviewPrompter.instance.getDaysSinceInstall();
    final prompts = await SmartReviewPrompter.instance.getPromptCount();
    final rated = await SmartReviewPrompter.instance.hasUserAlreadyRated();
    final lastDate = await SmartReviewPrompter.instance.getLastPromptDate();

    setState(() {
      _appOpenCount = opens;
      _daysSinceInstall = days;
      _promptCount = prompts;
      _hasAlreadyRated = rated;
      _lastPromptDate = lastDate != null ? lastDate.toLocal().toString().split('.')[0] : 'Never';
    });
  }

  void _showSnackBar(String message, {bool isError = false}) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(message),
        backgroundColor: isError ? Colors.red : const Color(0xFF2CAC5C),
        behavior: SnackBarBehavior.floating,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF8FAFC), // Off-white modern surface
      appBar: AppBar(
        title: const Text(
          'Smart Review Playground',
          style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
        ),
        backgroundColor: const Color(0xFF2CAC5C), // Brand green
        centerTitle: true,
        elevation: 2,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // --- STATS CARD (Glassmorphic / Aesthetic Feel) ---
            Card(
              elevation: 4,
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
              color: Colors.white,
              child: Padding(
                padding: const EdgeInsets.all(20.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Row(
                      children: [
                        Icon(Icons.dashboard, color: Color(0xFF2CAC5C)),
                        SizedBox(width: 8),
                        Text(
                          'Prompter Status & Stats',
                          style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFF0F172A)),
                        ),
                      ],
                    ),
                    const Divider(height: 24),
                    _buildStatRow('App Open Count', '$_appOpenCount times', Colors.blue.shade700),
                    _buildStatRow('Days Since Install', '$_daysSinceInstall days', Colors.orange.shade700),
                    _buildStatRow('Total Prompt Count', '$_promptCount times', Colors.purple.shade700),
                    _buildStatRow('User Already Rated?', _hasAlreadyRated ? 'YES' : 'NO', _hasAlreadyRated ? Colors.green.shade700 : Colors.red.shade700),
                    _buildStatRow('Last Prompt Date', _lastPromptDate, const Color(0xFF64748B)),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),

            // --- CONTROLLER CARDS ---
            Card(
              elevation: 4,
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
              color: Colors.white,
              child: Padding(
                padding: const EdgeInsets.all(20.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Row(
                      children: [
                        Icon(Icons.tune, color: Color(0xFF2563EB)),
                        SizedBox(width: 8),
                        Text(
                          'Test Parameters Configuration',
                          style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFF0F172A)),
                        ),
                      ],
                    ),
                    const SizedBox(height: 16),
                    Row(
                      children: [
                        Expanded(child: _buildTextField('Min Opens', _minOpensController)),
                        const SizedBox(width: 12),
                        Expanded(child: _buildTextField('Min Days', _minDaysController)),
                      ],
                    ),
                    const SizedBox(height: 12),
                    Row(
                      children: [
                        Expanded(child: _buildTextField('Max Prompts', _maxPromptsController)),
                        const SizedBox(width: 12),
                        Expanded(child: _buildTextField('Days Cooldown', _daysCooldownController)),
                      ],
                    ),
                    const SizedBox(height: 12),
                    _buildTextField('Opens Cooldown', _opensCooldownController),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 20),

            // --- INTERACTIVE ACTIONS ---
            const Text(
              'Interactive Actions',
              style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF64748B)),
            ),
            const SizedBox(height: 10),

            // Simulate App Open
            ElevatedButton.icon(
              icon: const Icon(Icons.add_circle_outline),
              label: const Text('Simulate App Open (Increment)'),
              onPressed: () async {
                await SmartReviewPrompter.instance.incrementAppOpenCount();
                await _refreshStats();
                _showSnackBar('App Open count incremented!');
              },
              style: ElevatedButton.styleFrom(
                backgroundColor: const Color(0xFF2563EB), // Royal Blue
                foregroundColor: Colors.white,
                padding: const EdgeInsets.symmetric(vertical: 12),
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
              ),
            ),
            const SizedBox(height: 12),

            // Try Showing Review (With Limit / Reprompt - NEW)
            ElevatedButton.icon(
              icon: const Icon(Icons.replay_outlined),
              label: const Text('Try Showing with Limits (New Method)'),
              onPressed: () async {
                final int minOpens = int.tryParse(_minOpensController.text) ?? 3;
                final int minDays = int.tryParse(_minDaysController.text) ?? 0;
                final int maxPrompts = int.tryParse(_maxPromptsController.text) ?? 3;
                final int cooldownDays = int.tryParse(_daysCooldownController.text) ?? 0;
                final int cooldownOpens = int.tryParse(_opensCooldownController.text) ?? 2;

                _showSnackBar('Evaluating multiple-times conditions...');
                
                await SmartReviewPrompter.instance.tryShowingReviewWithLimit(
                  conditions: SmartReviewConditions(
                    minAppOpens: minOpens,
                    minDaysSinceInstall: minDays,
                  ),
                  maxPromptCount: maxPrompts,
                  minDaysBetweenPrompts: cooldownDays,
                  minOpensBetweenPrompts: cooldownOpens,
                );
                
                await _refreshStats();
              },
              style: ElevatedButton.styleFrom(
                backgroundColor: const Color(0xFF2CAC5C), // Brand green
                foregroundColor: Colors.white,
                padding: const EdgeInsets.symmetric(vertical: 14),
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
              ),
            ),
            const SizedBox(height: 12),

            // Try Showing Review (Legacy One-time - OLD)
            OutlinedButton.icon(
              icon: const Icon(Icons.looks_one),
              label: const Text('Try Showing One-Time Only (Legacy)'),
              onPressed: () async {
                final int minOpens = int.tryParse(_minOpensController.text) ?? 3;
                final int minDays = int.tryParse(_minDaysController.text) ?? 0;

                _showSnackBar('Evaluating legacy one-time conditions...');
                
                await SmartReviewPrompter.instance.tryShowingReview(
                  SmartReviewConditions(
                    minAppOpens: minOpens,
                    minDaysSinceInstall: minDays,
                  ),
                );
                
                await _refreshStats();
              },
              style: OutlinedButton.styleFrom(
                foregroundColor: const Color(0xFF0F172A),
                side: const BorderSide(color: Color(0xFF64748B)),
                padding: const EdgeInsets.symmetric(vertical: 12),
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
              ),
            ),
            const SizedBox(height: 12),

            // Mark user as Rated
            ElevatedButton.icon(
              icon: const Icon(Icons.star),
              label: const Text('Mark User as Rated (Permanently Disable)'),
              onPressed: () async {
                await SmartReviewPrompter.instance.markUserAsRated();
                await _refreshStats();
                _showSnackBar('User marked as already rated. Prompts disabled!');
              },
              style: ElevatedButton.styleFrom(
                backgroundColor: const Color(0xFF10B981), // Emerald Accent
                foregroundColor: Colors.white,
                padding: const EdgeInsets.symmetric(vertical: 12),
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
              ),
            ),
            const SizedBox(height: 12),

            // Reset All Cache
            ElevatedButton.icon(
              icon: const Icon(Icons.delete_forever),
              label: const Text('Reset Prompter Storage Cache'),
              onPressed: () async {
                await SmartReviewPrompter.instance.reset();
                await _refreshStats();
                _showSnackBar('All local prompter storage has been reset!', isError: true);
              },
              style: ElevatedButton.styleFrom(
                backgroundColor: const Color(0xFFEF4444), // Error Red
                foregroundColor: Colors.white,
                padding: const EdgeInsets.symmetric(vertical: 12),
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildStatRow(String label, String value, Color valueColor) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 6.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(
            label,
            style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFF64748B)),
          ),
          Text(
            value,
            style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: valueColor),
          ),
        ],
      ),
    );
  }

  Widget _buildTextField(String label, TextEditingController controller) {
    return TextField(
      controller: controller,
      keyboardType: TextInputType.number,
      decoration: InputDecoration(
        labelText: label,
        labelStyle: const TextStyle(fontSize: 12, color: Color(0xFF64748B)),
        border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
        contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
      ),
    );
  }
}
7
likes
160
points
71
downloads

Documentation

API reference

Publisher

verified publishersatish.studio

Weekly Downloads

A smart way to show in-app review prompts based on user engagement, such as app open count and days since install.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, in_app_review, shared_preferences

More

Packages that depend on smart_review_prompter