package_flutter_env 0.0.22+1
package_flutter_env: ^0.0.22+1 copied to clipboard
Using MIAppBarWidget to create a custom app bar.
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
// lib/data/dto/theme_setting_dto.dart
import 'package:json_annotation/json_annotation.dart';
part 'theme_setting_dto.g.dart';
// ─────────────────────────────────────────────────────────────────────────────
// Root response wrapper
// ─────────────────────────────────────────────────────────────────────────────
@JsonSerializable(explicitToJson: true)
class ThemeSettingResponse {
final ThemeSettingDto data;
const ThemeSettingResponse({required this.data});
factory ThemeSettingResponse.fromJson(Map<String, dynamic> json) =>
_$ThemeSettingResponseFromJson(json);
Map<String, dynamic> toJson() => _$ThemeSettingResponseToJson(this);
}
// ─────────────────────────────────────────────────────────────────────────────
// ThemeSettingDto
// ─────────────────────────────────────────────────────────────────────────────
@JsonSerializable(explicitToJson: true)
class ThemeSettingDto {
@JsonKey(name: 'login_bg_screen')
final LoginBgScreenDto loginBgScreen;
@JsonKey(name: 'home_icons')
final HomeIconsDto homeIcons;
@JsonKey(name: 'splash_image')
final SplashImageDto splashImage;
const ThemeSettingDto({
required this.loginBgScreen,
required this.homeIcons,
required this.splashImage,
});
factory ThemeSettingDto.fromJson(Map<String, dynamic> json) =>
_$ThemeSettingDtoFromJson(json);
Map<String, dynamic> toJson() => _$ThemeSettingDtoToJson(this);
}
// ─────────────────────────────────────────────────────────────────────────────
// login_bg_screen → { "en": "url", "zh": "url", "ko": "url" }
//
// The whole object is a free-key map (lang → url), so we use a custom
// JsonConverter instead of field-level code-gen.
// ─────────────────────────────────────────────────────────────────────────────
class _LangUrlMapConverter
implements JsonConverter<Map<String, String>, Map<String, dynamic>> {
const _LangUrlMapConverter();
@override
Map<String, String> fromJson(Map<String, dynamic> json) =>
json.map((k, v) => MapEntry(k, v as String));
@override
Map<String, dynamic> toJson(Map<String, String> map) => map;
}
@JsonSerializable(explicitToJson: true)
class LoginBgScreenDto {
final Map<String, String> byLang;
const LoginBgScreenDto({required this.byLang});
factory LoginBgScreenDto.fromJson(Map<String, dynamic> json) =>
LoginBgScreenDto(byLang: const _LangUrlMapConverter().fromJson(json));
Map<String, dynamic> toJson() => const _LangUrlMapConverter().toJson(byLang);
/// Returns the URL for [lang]; falls back to 'en' when not found.
String? getUrl(String lang) => byLang[lang] ?? byLang['en'];
}
// ─────────────────────────────────────────────────────────────────────────────
// home_icons → { "vip": { "icon1": "url", … }, "normal": { … } }
// ─────────────────────────────────────────────────────────────────────────────
// Top-level helpers are required by @JsonKey — they cannot be instance methods.
Map<String, String> _stringMapFromJson(Map<String, dynamic> json) =>
json.map((k, v) => MapEntry(k, v as String));
Map<String, dynamic> _stringMapToJson(Map<String, String> map) => map;
@JsonSerializable(explicitToJson: true)
class HomeIconsDto {
@JsonKey(fromJson: _stringMapFromJson, toJson: _stringMapToJson)
final Map<String, String> vip;
@JsonKey(fromJson: _stringMapFromJson, toJson: _stringMapToJson)
final Map<String, String> normal;
const HomeIconsDto({required this.vip, required this.normal});
factory HomeIconsDto.fromJson(Map<String, dynamic> json) =>
_$HomeIconsDtoFromJson(json);
Map<String, dynamic> toJson() => _$HomeIconsDtoToJson(this);
/// Returns the icon map for [userType] ('vip' or 'normal').
/// Falls back to [normal] when [userType] is unrecognised.
Map<String, String> getIcons(String userType) =>
userType == 'vip' ? vip : normal;
}
// ─────────────────────────────────────────────────────────────────────────────
// splash_image → { "ios": { "en": "url", … }, "android": { … } }
// ─────────────────────────────────────────────────────────────────────────────
@JsonSerializable(explicitToJson: true)
class SplashImageDto {
@JsonKey(fromJson: _stringMapFromJson, toJson: _stringMapToJson)
final Map<String, String> ios;
@JsonKey(fromJson: _stringMapFromJson, toJson: _stringMapToJson)
final Map<String, String> android;
const SplashImageDto({required this.ios, required this.android});
factory SplashImageDto.fromJson(Map<String, dynamic> json) =>
_$SplashImageDtoFromJson(json);
Map<String, dynamic> toJson() => _$SplashImageDtoToJson(this);
/// Returns URL for [platform] ('ios'/'android') + [lang].
/// Falls back to 'en' when [lang] is not found.
String? getUrl(String platform, String lang) {
final byLang = platform == 'ios' ? ios : android;
return byLang[lang] ?? byLang['en'];
}
}
// lib/models/theme_setting_model.dart
import 'package:scoped_model/scoped_model.dart';
import '../data/remote/theme_setting_service.dart';
import '../data/dto/theme_setting_dto.dart';
enum ThemeLoadState { idle, loading, success, error }
mixin ThemeSettingModel on Model {
final _themeService = ThemeSettingService();
ThemeLoadState _themeState = ThemeLoadState.idle;
ThemeSettingDto? _themeSetting;
String? _themeError;
// ── State getters ──────────────────────────────────────────────────────────
ThemeLoadState get themeState => _themeState;
bool get isThemeLoading => _themeState == ThemeLoadState.loading;
bool get hasThemeError => _themeState == ThemeLoadState.error;
bool get isThemeLoaded => _themeState == ThemeLoadState.success;
String? get themeError => _themeError;
// ── Data getters ───────────────────────────────────────────────────────────
/// Login background URL for [lang] (e.g. 'en', 'zh', 'ko').
/// Falls back to 'en' if [lang] is not found.
String? getLoginBg(String lang) =>
_themeSetting?.loginBgScreen.getUrl(lang);
/// Home icon map for [userType] ('vip' or 'normal').
/// Returns empty map if theme data is not yet loaded.
Map<String, String> getHomeIcons(String userType) =>
_themeSetting?.homeIcons.getIcons(userType) ?? {};
/// Splash image URL for [platform] ('ios' or 'android') and [lang].
/// Falls back to 'en' if [lang] is not found.
String? getSplashImage(String platform, String lang) =>
_themeSetting?.splashImage.getUrl(platform, lang);
// ── API call ───────────────────────────────────────────────────────────────
Future<void> loadThemeSetting() async {
// Prevent duplicate in-flight requests
if (_themeState == ThemeLoadState.loading) return;
_themeState = ThemeLoadState.loading;
_themeError = null;
notifyListeners();
try {
_themeSetting = await _themeService.fetchThemeSetting();
_themeState = ThemeLoadState.success;
} catch (e) {
_themeState = ThemeLoadState.error;
_themeError = e.toString();
}
notifyListeners();
}
/// Call this to retry after an error.
Future<void> retryThemeSetting() async {
_themeState = ThemeLoadState.idle;
await loadThemeSetting();
}
}
// lib/data/remote/theme_setting_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../dto/theme_setting_dto.dart';
class ThemeSettingService {
static const _url = 'https://abc/123';
Future<ThemeSettingDto> fetchThemeSetting() async {
final response = await http.get(Uri.parse(_url));
if (response.statusCode == 200) {
final json = jsonDecode(response.body) as Map<String, dynamic>;
// ThemeSettingResponse unwraps the top-level 'data' key automatically.
return ThemeSettingResponse.fromJson(json).data;
}
throw Exception('Failed to load theme settings [${response.statusCode}]');
}
}