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:
- 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'),
),
],
),
),
);
}
}