review_etiquette 1.0.0 copy "review_etiquette: ^1.0.0" to clipboard
review_etiquette: ^1.0.0 copied to clipboard

Asks for an in-app review at the right moment, after your app delivered value instead of on launch, following the Apple and Google rules.

example/lib/main.dart

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

void main() => runApp(const ReviewEtiquetteExampleApp());

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

  @override
  Widget build(BuildContext context) => MaterialApp(
    title: 'review_etiquette',
    theme: ThemeData(useMaterial3: true),
    home: const ExamplePage(),
  );
}

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

  @override
  State<ExamplePage> createState() => _ExamplePageState();
}

class _ExamplePageState extends State<ExamplePage> {
  // Editable so the "one prompt per version" condition can be walked through by
  // hand: type a new version and the next success event asks again.
  final TextEditingController _appVersion = TextEditingController(
    text: '1.0.0',
  );
  final TextEditingController _appStoreId = TextEditingController();

  bool _shortCooldown = true;
  ReviewRequestOutcome? _outcome;
  String? _storeError;

  @override
  void dispose() {
    _appVersion.dispose();
    _appStoreId.dispose();
    super.dispose();
  }

  // A real app builds this once, in whatever it uses for dependency injection.
  // Here it is rebuilt per call so the version field stays editable.
  ReviewEtiquette get _etiquette => ReviewEtiquette(
    appVersion: _appVersion.text.trim(),
    cooldown: _shortCooldown ? Duration.zero : const Duration(days: 120),
  );

  Future<void> _onSuccessEvent() async {
    final outcome = await _etiquette.requestReview();

    if (!mounted) {
      return;
    }
    setState(() => _outcome = outcome);
  }

  Future<void> _onRateTapped() async {
    final id = _appStoreId.text.trim();

    try {
      await _etiquette.openStoreListing(appStoreId: id.isEmpty ? null : id);
      if (!mounted) {
        return;
      }
      setState(() => _storeError = null);
    } on ReviewEtiquetteException catch (error) {
      if (!mounted) {
        return;
      }
      setState(() => _storeError = error.message);
    }
  }

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(title: const Text('review_etiquette')),
    body: ListView(
      padding: const EdgeInsets.all(16),
      children: <Widget>[
        TextField(
          controller: _appVersion,
          decoration: const InputDecoration(
            labelText: 'App version',
            helperText: 'The user visible version, not a build number.',
          ),
        ),
        const SizedBox(height: 16),
        SwitchListTile(
          contentPadding: EdgeInsets.zero,
          value: _shortCooldown,
          onChanged: (bool value) => setState(() => _shortCooldown = value),
          title: const Text('Zero cooldown'),
          subtitle: const Text(
            'On, so the version condition can be tried without waiting 120 days.',
          ),
        ),
        const SizedBox(height: 8),
        FilledButton(
          onPressed: _onSuccessEvent,
          child: const Text('Pretend the app just delivered value'),
        ),
        if (_outcome case final ReviewRequestOutcome outcome) ...<Widget>[
          const SizedBox(height: 12),
          Text('Outcome: ${outcome.name}'),
          if (outcome == ReviewRequestOutcome.requested)
            const Text(
              'This means we asked. Neither store reports whether a prompt '
              'was actually shown.',
            ),
        ],
        const Divider(height: 48),
        TextField(
          controller: _appStoreId,
          decoration: const InputDecoration(
            labelText: 'App Store id',
            helperText: 'Required on iOS, ignored on Android.',
          ),
        ),
        const SizedBox(height: 16),
        OutlinedButton(
          onPressed: _onRateTapped,
          child: const Text('Rate this app'),
        ),
        if (_storeError case final String message) ...<Widget>[
          const SizedBox(height: 12),
          Text(message),
        ],
      ],
    ),
  );
}
1
likes
160
points
164
downloads

Documentation

API reference

Publisher

verified publisherboundfoxstudios.com

Weekly Downloads

Asks for an in-app review at the right moment, after your app delivered value instead of on launch, following the Apple and Google rules.

Repository (GitHub)
View/report issues

Topics

#in-app-review #rating #review #storekit #play-store

License

MIT (license)

Dependencies

clock, flutter, meta, shared_preferences

More

Packages that depend on review_etiquette

Packages that implement review_etiquette