should_review 0.2.5 copy "should_review: ^0.2.5" to clipboard
should_review: ^0.2.5 copied to clipboard

A flutter package to determine if you should prompt users to rate your app based on some set metrics.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:should_review/should_review.dart';

// For Test Purposes.
import 'package:should_review/extensions/date_time.dart';
import 'package:should_review/extensions/should_review.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Should Review Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Should Review Example'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // TextEditing Controllers.
  final TextEditingController _minDaysController = TextEditingController();
  final TextEditingController _coolDownDaysController = TextEditingController();

  // Keys
  final GlobalKey<FormState> _daysCriteriaFormKey = GlobalKey();

  bool _hasPromptedRateApp = false;

  @override
  void initState() {
    super.initState();
    ShouldReviewExtension
        .reset(); // Never call this in real use. This is just for testing.
    _minDaysController.text = '7';
    _coolDownDaysController.text = '2';
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SingleChildScrollView(
        child: Container(
          padding: const EdgeInsets.all(10),
          alignment: Alignment.center,
          child: Form(
            key: _daysCriteriaFormKey,
            child: Column(
              children: [
                const Text(
                    "Determine if user should rate app by past days after first launch"),
                const SizedBox(
                  height: 20,
                ),
                Row(
                  children: [
                    Expanded(
                      child: TextFormField(
                        controller: _minDaysController,
                        keyboardType: TextInputType.number,
                        inputFormatters: [
                          FilteringTextInputFormatter.allow(RegExp("[0-9]")),
                        ],
                        validator: (value) {
                          if (value == null ||
                              value.isEmpty ||
                              int.tryParse(value) == null) {
                            return 'Please enter a number';
                          }
                          return null;
                        },
                        decoration: const InputDecoration(
                            label: Text('Minimum Days before First Prompt'),
                            border:
                                OutlineInputBorder(borderSide: BorderSide())),
                      ),
                    ),
                    const SizedBox(
                      width: 10,
                    ),
                    Expanded(
                      child: TextFormField(
                        controller: _coolDownDaysController,
                        keyboardType: TextInputType.number,
                        inputFormatters: [
                          FilteringTextInputFormatter.allow(RegExp("[0-9]")),
                        ],
                        validator: (value) {
                          if (value == null ||
                              value.isEmpty ||
                              int.tryParse(value) == null) {
                            return 'Please enter a number';
                          }
                          return null;
                        },
                        decoration: const InputDecoration(
                            label: Text('Interval Days after First Prompt.'),
                            border:
                                OutlineInputBorder(borderSide: BorderSide())),
                      ),
                    )
                  ],
                ),
                const SizedBox(
                  height: 10,
                ),
                MaterialButton(
                  onPressed: _setHypotethicalTodaysDate,
                  color: Colors.blue,
                  child: const Text(
                    "Set Hypotethical Today's Date",
                    style: TextStyle(color: Colors.white),
                  ),
                ),
                const SizedBox(
                  height: 10,
                ),
                Text(DateTimeExtension.customTime == null
                    ? "Same as System"
                    : DateTimeExtension.customTime.toString()),
                const SizedBox(
                  height: 10,
                ),
                Text(
                  "Has Prompted user to Rate App: " +
                      (_hasPromptedRateApp ? "True" : "False"),
                ),
                const SizedBox(
                  height: 10,
                ),
                MaterialButton(
                  onPressed: _testShouldRateByDaysCriteria,
                  color: Colors.blue,
                  child: const Text(
                    "Test",
                    style: TextStyle(color: Colors.white),
                  ),
                ),
                const SizedBox(
                  height: 10,
                ),
                const SizedBox(
                  height: 10,
                ),
                MaterialButton(
                  onPressed: _reset,
                  color: Colors.blue,
                  child: const Text(
                    "Reset",
                    style: TextStyle(color: Colors.white),
                  ),
                ),
                const SizedBox(
                  height: 10,
                ),
                MaterialButton(
                  onPressed: _viewLaunchTimesCriteriaExample,
                  color: Colors.blue,
                  child: const Text(
                    "View Launch Times Criteria Example",
                    style: TextStyle(color: Colors.white),
                  ),
                ),
                MaterialButton(
                  onPressed: _viewCustomCriteriaExample,
                  color: Colors.blue,
                  child: const Text(
                    "Custom Criteria Example",
                    style: TextStyle(color: Colors.white),
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }

  void _reset() async {
    await ShouldReviewExtension.reset();
    setState(() {
      _hasPromptedRateApp = false;
      DateTimeExtension.customTime = null;
    });
  }

  void _setHypotethicalTodaysDate() async {
    DateTime? dateTime = await showDatePicker(
      context: context,
      initialDate: DateTimeExtension.customTime == null
          ? DateTime.now()
          : DateTimeExtension.customTime!,
      firstDate: DateTime.now(),
      lastDate: DateTime(2030),
    );

    if (dateTime != null) {
      setState(() => DateTimeExtension.customTime = dateTime);
    }
  }

  void _testShouldRateByDaysCriteria() async {
    // Validate Form Field.
    if (!_daysCriteriaFormKey.currentState!.validate()) {
      return;
    }

    // ignore: avoid_print
    print("Checking Prompt Review Possibility");

    // Should Review.
    if (await ShouldReview.shouldReview(
        criteria: Criteria.days,
        minDays: int.parse(_minDaysController.text),
        coolDownDays: int.parse(_coolDownDaysController.text))) {
      // A good place to use the in_app_review plugin.
      // ignore: avoid_print
      print("Retruned True");
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text(
              "User has been shown a hypotetical review dialog, this logic will now return true only on the cool down days interval."),
          duration: Duration(seconds: 10),
        ),
      );
      // Call `ShouldReview.neverReview();` to ensure the shouldReview function never returns true again.
      setState(() => _hasPromptedRateApp = true);
    } else {
      // ignore: avoid_print
      print("Returned False");
    }
  }

  void _viewLaunchTimesCriteriaExample() {
    Navigator.of(context).push(MaterialPageRoute(
        builder: (context) => const LaunchTimesCriteriaExample()));
  }

  void _viewCustomCriteriaExample() {
    Navigator.of(context).push(
        MaterialPageRoute(builder: (context) => const CustomCriteriaExample()));
  }
}

class LaunchTimesCriteriaExample extends StatefulWidget {
  const LaunchTimesCriteriaExample({Key? key}) : super(key: key);

  @override
  _LaunchTimesCriteriaExampleState createState() =>
      _LaunchTimesCriteriaExampleState();
}

class _LaunchTimesCriteriaExampleState
    extends State<LaunchTimesCriteriaExample> {
  // TextEditing Controllers.
  final TextEditingController _minLaunchTimesController =
      TextEditingController();
  final TextEditingController _coolDownLaunchTimesController =
      TextEditingController();

  // Keys
  final GlobalKey<FormState> _launchTimesCriteriaFormKey = GlobalKey();

  bool _hasPromptedRateApp = false;
  int _timesAppLaunched = 0;

  @override
  void initState() {
    super.initState();
    _reset().then((_) => _updateAppLaunchTimesInUI());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Launch Times Criteria Example"),
      ),
      body: Container(
        padding: const EdgeInsets.all(10),
        alignment: Alignment.center,
        child: Form(
          key: _launchTimesCriteriaFormKey,
          child: Column(
            children: [
              const Text(
                  "Determine if user should rate app by past days after first launch"),
              const SizedBox(
                height: 20,
              ),
              Row(
                children: [
                  Expanded(
                    child: TextFormField(
                      controller: _minLaunchTimesController,
                      keyboardType: TextInputType.number,
                      inputFormatters: [
                        FilteringTextInputFormatter.allow(RegExp("[0-9]")),
                      ],
                      validator: (value) {
                        if (value == null ||
                            value.isEmpty ||
                            int.tryParse(value) == null) {
                          return 'Please enter a number';
                        }
                        return null;
                      },
                      decoration: const InputDecoration(
                          label: Text('Minimum app launch times'),
                          border: OutlineInputBorder(borderSide: BorderSide())),
                    ),
                  ),
                  const SizedBox(
                    width: 10,
                  ),
                  Expanded(
                    child: TextFormField(
                      controller: _coolDownLaunchTimesController,
                      keyboardType: TextInputType.number,
                      inputFormatters: [
                        FilteringTextInputFormatter.allow(RegExp("[0-9]")),
                      ],
                      validator: (value) {
                        if (value == null ||
                            value.isEmpty ||
                            int.tryParse(value) == null) {
                          return 'Please enter a number';
                        }
                        return null;
                      },
                      decoration: const InputDecoration(
                          label: Text(
                              'Launch times in interval after minimum app launch times have been met'),
                          border: OutlineInputBorder(borderSide: BorderSide())),
                    ),
                  ),
                ],
              ),
              const SizedBox(
                height: 10,
              ),
              Text(
                "Has Prompted user to Rate App: " +
                    (_hasPromptedRateApp ? "True" : "False"),
              ),
              const SizedBox(
                height: 10,
              ),
              Text(
                "App Launch Times: $_timesAppLaunched",
              ),
              const SizedBox(
                height: 10,
              ),
              MaterialButton(
                onPressed: _recordLaunch,
                color: Colors.blue,
                child: const Text(
                  "Record App Launch",
                  style: TextStyle(color: Colors.white),
                ),
              ),
              const SizedBox(
                height: 10,
              ),
              MaterialButton(
                onPressed: _testShouldRateByLaunchTimesCriteria,
                color: Colors.blue,
                child: const Text(
                  "Test",
                  style: TextStyle(color: Colors.white),
                ),
              ),
              const SizedBox(
                height: 10,
              ),
              MaterialButton(
                onPressed: () =>
                    _reset().then((_) => _updateAppLaunchTimesInUI()),
                color: Colors.blue,
                child: const Text(
                  "Reset",
                  style: TextStyle(color: Colors.white),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Future<void> _reset() async {
    await ShouldReviewExtension.reset();
    setState(() {
      _hasPromptedRateApp = false;
    });
  }

  /// Record App Launch.
  void _recordLaunch() async {
    await ShouldReview.recordLaunch();
    _updateAppLaunchTimesInUI();
  }

  /// Upate App Launch Times in UI.
  void _updateAppLaunchTimesInUI() {
    ShouldReview.getTimesAppLaunched().then(
        (int launchTimes) => setState(() => _timesAppLaunched = launchTimes));
  }

  void _testShouldRateByLaunchTimesCriteria() async {
    // Validate Form Field.
    if (!_launchTimesCriteriaFormKey.currentState!.validate()) {
      return;
    }

    // ignore: avoid_print
    print("Checking Prompt Review Possibility");

    // Should Review.
    if (await ShouldReview.shouldReview(
        criteria: Criteria.timesLaunched,
        minLaunchTimes: int.parse(_minLaunchTimesController.text),
        coolDownLaunchTimes: int.parse(_coolDownLaunchTimesController.text))) {
      // A good place to use the in_app_review plugin.
      // ignore: avoid_print
      print("Retruned True");
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text(
              "User has been shown a hypotetical review dialog, this logic will now return true only on the cool down launch times interval."),
          duration: Duration(seconds: 10),
        ),
      );
      // Call `ShouldReview.neverReview();` to ensure the shouldReview function never returns true again.
      setState(() => _hasPromptedRateApp = true);
    } else {
      // ignore: avoid_print
      print("Returned False");
    }
  }
}

class CustomCriteriaExample extends StatefulWidget {
  const CustomCriteriaExample({Key? key}) : super(key: key);

  @override
  _CustomCriteriaExampleState createState() => _CustomCriteriaExampleState();
}

class _CustomCriteriaExampleState extends State<CustomCriteriaExample> {
  GlobalKey<FormState> _customCriteriaFormKey = GlobalKey();

  final TextEditingController _minCustomCriteriaValueController =
      TextEditingController();
  final TextEditingController _coolDownCustomCriteriaIntervalValueController =
      TextEditingController();
  final TextEditingController _customCriteriaKeyController =
      TextEditingController();

  bool _hasPromptedRateApp = false;
  int _timesCutsomCriteriaWasMet = 0;

  @override
  void initState() {
    super.initState();
    _reset().then((_) => _updateTimesCustomCriteriaWasMetInUI());
    _customCriteriaKeyController.text = 'made_purchase';
    _minCustomCriteriaValueController.text = '4';
    _coolDownCustomCriteriaIntervalValueController.text = '2';
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Custom Criteria Example"),
      ),
      body: Container(
        padding: const EdgeInsets.all(10),
        alignment: Alignment.center,
        child: Form(
          key: _customCriteriaFormKey,
          child: Column(
            children: [
              const Text(
                  "Determine if user should rate app by a custom criterion"),
              const SizedBox(
                height: 20,
              ),
              Row(
                children: [
                  Expanded(
                    child: TextFormField(
                      controller: _minCustomCriteriaValueController,
                      keyboardType: TextInputType.number,
                      inputFormatters: [
                        FilteringTextInputFormatter.allow(RegExp("[0-9]")),
                      ],
                      validator: (value) {
                        if (value == null ||
                            value.isEmpty ||
                            int.tryParse(value) == null) {
                          return 'Please enter a number';
                        }
                        return null;
                      },
                      decoration: const InputDecoration(
                          label: Text('Minimum custom criteria value'),
                          border: OutlineInputBorder(borderSide: BorderSide())),
                    ),
                  ),
                  const SizedBox(
                    width: 10,
                  ),
                  Expanded(
                    child: TextFormField(
                      controller:
                          _coolDownCustomCriteriaIntervalValueController,
                      keyboardType: TextInputType.number,
                      inputFormatters: [
                        FilteringTextInputFormatter.allow(RegExp("[0-9]")),
                      ],
                      validator: (value) {
                        if (value == null ||
                            value.isEmpty ||
                            int.tryParse(value) == null) {
                          return 'Please enter a number';
                        }
                        return null;
                      },
                      decoration: const InputDecoration(
                          label: Text('Custom criteria interval value'),
                          border: OutlineInputBorder(borderSide: BorderSide())),
                    ),
                  ),
                ],
              ),
              const SizedBox(
                height: 10,
              ),
              TextFormField(
                controller: _customCriteriaKeyController,
                keyboardType: TextInputType.number,
                inputFormatters: [
                  FilteringTextInputFormatter.allow(RegExp("[0-9]")),
                ],
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Please enter a valid key';
                  }
                  return null;
                },
                decoration: const InputDecoration(
                    label: Text('Custom criteria key'),
                    border: OutlineInputBorder(borderSide: BorderSide())),
              ),
              const SizedBox(
                height: 10,
              ),
              Text(
                "Has Prompted user to Rate App: " +
                    (_hasPromptedRateApp ? "True" : "False"),
              ),
              const SizedBox(
                height: 10,
              ),
              Text(
                "Times custom criteria was met: $_timesCutsomCriteriaWasMet",
              ),
              const SizedBox(
                height: 10,
              ),
              MaterialButton(
                onPressed: _recordCustomCriteriaFailed,
                color: Colors.blue,
                child: const Text(
                  "Record Custom Criteria Met",
                  style: TextStyle(color: Colors.white),
                ),
              ),
              const SizedBox(
                height: 10,
              ),
              MaterialButton(
                onPressed: _testShouldRateByLaunchTimesCriteria,
                color: Colors.blue,
                child: const Text(
                  "Test",
                  style: TextStyle(color: Colors.white),
                ),
              ),
              const SizedBox(
                height: 10,
              ),
              MaterialButton(
                onPressed: () => _reset()
                    .then((_) => _updateTimesCustomCriteriaWasMetInUI()),
                color: Colors.blue,
                child: const Text(
                  "Reset",
                  style: TextStyle(color: Colors.white),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  /// Record Custom Criteria Met.
  void _recordCustomCriteriaFailed() async {
    // Validate Form Field.
    if (!_customCriteriaFormKey.currentState!.validate()) {
      return;
    }

    await ShouldReview.recordCustomCriteriaMet(
        _customCriteriaKeyController.text);
    _updateTimesCustomCriteriaWasMetInUI();
  }

  /// Upate times custom criteria was met in UI.
  void _updateTimesCustomCriteriaWasMetInUI() {
    ShouldReview.getTimesCustomCriteriaWasMet(_customCriteriaKeyController.text)
        .then(
            (int times) => setState(() => _timesCutsomCriteriaWasMet = times));
  }

  /// Reset flags.
  Future<void> _reset() async {
    await ShouldReviewExtension.reset(
        customCriteriaKeys: [_customCriteriaKeyController.text]);
    setState(() {
      _hasPromptedRateApp = false;
    });
  }

  void _testShouldRateByLaunchTimesCriteria() async {
    // Validate Form Field.
    if (!_customCriteriaFormKey.currentState!.validate()) {
      return;
    }

    // ignore: avoid_print
    print("Checking Prompt Review Possibility");

    // Should Review.
    if (await ShouldReview.shouldReview(
        criteria: Criteria.custom,
        customCriteriaKey: _customCriteriaKeyController.text,
        minCustomCriteriaValue:
            int.parse(_minCustomCriteriaValueController.text),
        coolDownCustomCriteriaInterval:
            int.parse(_coolDownCustomCriteriaIntervalValueController.text))) {
      // A good place to use the in_app_review plugin.
      // ignore: avoid_print
      print("Retruned True");
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text(
              "User has been shown a hypotetical review dialog, this logic will now return true only on the cool down launch times interval."),
          duration: Duration(seconds: 10),
        ),
      );
      // Call `ShouldReview.neverReview();` to ensure the shouldReview function never returns true again.
      setState(() => _hasPromptedRateApp = true);
    } else {
      // ignore: avoid_print
      print("Returned False");
    }
  }
}
8
likes
140
pub points
66%
popularity

Publisher

verified publisherfrancisilechukwu.com

A flutter package to determine if you should prompt users to rate your app based on some set metrics.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (LICENSE)

Dependencies

flutter, shared_preferences

More

Packages that depend on should_review