Custom script for generating environment-specific code for different platforms

Features

The flutter_env.dart file appears to be a custom script for generating environment-specific code for different platforms (Dart, Objective-C, and Gradle for Android). It reads from an environment file (.env by default), and generates code based on the key-value pairs in the environment file.

Here's a basic usage guide:

  1. Create an environment file: Create a .env file in your project root (or specify a different file using the envfile argument). This file should contain key-value pairs, one per line, like this:
    API_KEY=123456
    BASE_URL=https://api.example.com

2.Run the script: You can run the DotENV class with the command-line arguments. For example:

    void main(List<String> args) {
    DotENV(args);
    }

You can pass arguments to specify the platform (platform), the environment file (envfile), and the directory name (dirname). If not specified, it will use default values.

3.Generated code: The script will generate a Dart file (lib/env.dart by default) with a class ENV that contains static string properties for each key-value pair in the environment file. For example:

    class ENV {
        static String API_KEY = "123456";
        static String BASE_URL = "https://api.example.com";
    }

You can then import this file in your Flutter code and access the environment variables like this: ENV.API_KEY.

Please note that this is a basic guide and the actual usage may vary depending on your project setup and requirements. Also, remember to exclude your environment files from version control to avoid committing sensitive data.

Unit Test Command

At the root of the project, run the following command:

 flutter  test test/dotenv_test.dart

Generate the env.dart file

At the root of the example project, run the following command: like: /Users/danli/Desktop/2024/packages/package_flutter_env/example Because test is the development environment, the generated file is in the lib folder.

    flutter test test/env_test.dart
// inactivity_timer_model.dart
import 'dart:async';
import 'package:flutter/foundation.dart';

class InactivityTimerModel extends ChangeNotifier {
  static const int totalSeconds = 30;
  static const int warningSeconds = 15;

  int _secondsRemaining = totalSeconds;
  Timer? _timer;
  bool _isWarningShown = false;

  int get secondsRemaining => _secondsRemaining;
  bool get isWarningShown => _isWarningShown;
  bool get isExpired => _secondsRemaining == 0;

  void start() {
    _timer?.cancel();
    _timer = Timer.periodic(const Duration(seconds: 1), _onTick);
  }

  void _onTick(Timer timer) {
    if (_secondsRemaining > 0) {
      _secondsRemaining--;
      notifyListeners();
    } else {
      _timer?.cancel();
      notifyListeners(); // triggers logout
    }
  }

  void resetTimer() {
    _secondsRemaining = totalSeconds;
    _isWarningShown = false;
    notifyListeners();
  }

  void dismissWarning() {
    _isWarningShown = false;
    resetTimer();
  }

  void markWarningShown() {
    _isWarningShown = true;
  }

  void stop() {
    _timer?.cancel();
  }

  @override
  void dispose() {
    _timer?.cancel();
    super.dispose();
  }
}
// inherited_inactivity_timer.dart
import 'package:flutter/widgets.dart';
import 'inactivity_timer_model.dart';

class InheritedInactivityTimer extends InheritedNotifier<InactivityTimerModel> {
  const InheritedInactivityTimer({
    super.key,
    required InactivityTimerModel model,
    required super.child,
  }) : super(notifier: model);

  static InactivityTimerModel of(BuildContext context) {
    return context
        .dependOnInheritedWidgetOfExactType<InheritedInactivityTimer>()!
        .notifier!;
  }
}
// inactivity_detector.dart
import 'package:flutter/material.dart';
import 'inherited_inactivity_timer.dart';

class InactivityDetector extends StatefulWidget {
  final Widget child;

  const InactivityDetector({super.key, required this.child});

  @override
  State<InactivityDetector> createState() => _InactivityDetectorState();
}

class _InactivityDetectorState extends State<InactivityDetector> {
  void _onInteraction() {
    InheritedInactivityTimer.of(context).resetTimer();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      // Detects taps, horizontal and vertical swipes
      onTap: (_) => _onInteraction(),
      onPanUpdate: (_) => _onInteraction(),       // drag in any direction
      onHorizontalDragUpdate: (_) => _onInteraction(),
      onVerticalDragUpdate: (_) => _onInteraction(),
      behavior: HitTestBehavior.translucent,       // captures gestures on empty space too
      child: widget.child,
    );
  }
}
// inactivity_warning_dialog.dart
import 'package:flutter/material.dart';
import 'inherited_inactivity_timer.dart';

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

  @override
  Widget build(BuildContext context) {
    final model = InheritedInactivityTimer.of(context);

    return AlertDialog(
      title: const Text('Still there?'),
      content: Text(
        'You will be logged out in ${model.secondsRemaining} seconds due to inactivity.',
      ),
      actions: [
        ElevatedButton(
          onPressed: () {
            model.dismissWarning();
            Navigator.of(context).pop();
          },
          child: const Text('Yes, keep me logged in'),
        ),
      ],
    );
  }
}
// inactivity_timer_listener.dart
import 'package:flutter/material.dart';
import 'inactivity_timer_model.dart';
import 'inherited_inactivity_timer.dart';
import 'inactivity_warning_dialog.dart';

class InactivityTimerListener extends StatefulWidget {
  final Widget child;

  const InactivityTimerListener({super.key, required this.child});

  @override
  State<InactivityTimerListener> createState() => _InactivityTimerListenerState();
}

class _InactivityTimerListenerState extends State<InactivityTimerListener> {
  late InactivityTimerModel _model;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _model = InheritedInactivityTimer.of(context);
    _model.addListener(_handleTimerChange);
    _model.start(); // start timer when app loads
  }

  void _handleTimerChange() {
    if (!mounted) return;

    // Trigger logout
    if (_model.isExpired) {
      _dismissWarningIfOpen();
      debugPrint('logout'); // replace with your logout logic
      return;
    }

    // Show warning dialog at 15 seconds remaining
    if (_model.secondsRemaining <= InactivityTimerModel.warningSeconds &&
        !_model.isWarningShown) {
      _model.markWarningShown();
      _showWarningDialog();
    }
  }

  void _dismissWarningIfOpen() {
    if (Navigator.of(context).canPop()) {
      Navigator.of(context).pop();
    }
  }

  void _showWarningDialog() {
    showDialog(
      context: context,
      barrierDismissible: false, // force user to tap button
      builder: (_) => InheritedInactivityTimer(
        model: _model,
        child: const InactivityWarningDialog(),
      ),
    );
  }

  @override
  void dispose() {
    _model.removeListener(_handleTimerChange);
    _model.stop();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => widget.child;
}
/// main.dart
import 'package:flutter/material.dart';
import 'inactivity_timer_model.dart';
import 'inherited_inactivity_timer.dart';
import 'inactivity_timer_listener.dart';
import 'inactivity_detector.dart';

void main() {
  runApp(
    InheritedInactivityTimer(
      model: InactivityTimerModel(),
      child: const MyApp(),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: InactivityTimerListener(   // listens for logout / warning triggers
        child: InactivityDetector(     // detects all user interactions globally
          child: const HomeScreen(),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final model = InheritedInactivityTimer.of(context);

    return Scaffold(
      appBar: AppBar(title: const Text('Inactivity Timer Demo')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // Live countdown display
            AnimatedBuilder(
              animation: model,
              builder: (context, _) => Text(
                '${model.secondsRemaining}s remaining',
                style: TextStyle(
                  fontSize: 48,
                  fontWeight: FontWeight.bold,
                  color: model.secondsRemaining <= InactivityTimerModel.warningSeconds
                      ? Colors.red
                      : Colors.green,
                ),
              ),
            ),
            const SizedBox(height: 24),
            ElevatedButton.icon(
              onPressed: model.resetTimer,
              icon: const Icon(Icons.refresh),
              label: const Text('Reset Timer'),
            ),
          ],
        ),
      ),
    );
  }
}