《Flutter 网络请求适配多环境:开发、测试、生产环境切换方案》
·
Flutter网络请求适配多环境方案
在Flutter开发中,实现多环境(开发/测试/生产)的网络请求切换,关键在于配置管理和运行时环境识别。以下是三种高效实现方案:
方案一:编译时配置(推荐)
通过--dart-define在编译时注入环境变量
// 环境配置类
enum Env { dev, test, prod }
class AppConfig {
static final Env _env = _getEnv();
static String get baseUrl {
switch(_env) {
case Env.dev: return "https://dev-api.com";
case Env.test: return "https://test-api.com";
case Env.prod: return "https://api.com";
}
}
static Env _getEnv() {
const env = String.fromEnvironment('ENV', defaultValue: 'dev');
switch(env) {
case 'test': return Env.test;
case 'prod': return Env.prod;
default: return Env.dev;
}
}
}
// 网络请求使用
final dio = Dio()
..options.baseUrl = AppConfig.baseUrl;
运行命令:
# 开发环境(默认)
flutter run
# 测试环境
flutter run --dart-define=ENV=test
# 生产环境
flutter run --dart-define=ENV=prod
优点:
- 无运行时开销
- 配置编译进包,无法篡改
- 支持CI/CD自动化构建
方案二:配置文件动态加载
使用JSON配置文件按环境加载
- 创建配置文件:
// assets/config_dev.json
{"baseUrl": "https://dev-api.com"}
// assets/config_prod.json
{"baseUrl": "https://api.com"}
- 配置加载器:
Future<Map<String, dynamic>> loadConfig() async {
const env = bool.hasEnvironment('ENV')
? const String.fromEnvironment('ENV')
: 'dev';
final configFile = await rootBundle.loadString(
'assets/config_${env}.json'
);
return json.decode(configFile);
}
// 初始化
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final config = await loadConfig();
runApp(MyApp(config: config));
}
方案三:运行时环境切换
适用于调试阶段动态切换
// 环境切换器
class EnvSwitcher extends StatelessWidget {
final Widget child;
const EnvSwitcher({super.key, required this.child});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (context) => child,
),
floatingActionButton: FloatingActionButton(
onPressed: () => _showEnvDialog(context),
child: const Icon(Icons.settings),
),
);
}
void _showEnvDialog(BuildContext context) {
showDialog(
context: context,
builder: (_) => AlertDialog(
title: const Text('切换环境'),
actions: [
_buildEnvButton(context, '开发', 'dev'),
_buildEnvButton(context, '测试', 'test'),
_buildEnvButton(context, '生产', 'prod'),
],
)
);
}
Widget _buildEnvButton(BuildContext ctx, String text, String env) {
return TextButton(
onPressed: () {
Provider.of<EnvProvider>(ctx, listen: false).setEnv(env);
Navigator.pop(ctx);
},
child: Text(text)
);
}
}
// 环境提供者
class EnvProvider with ChangeNotifier {
String _currentEnv = 'dev';
String get baseUrl {
switch(_currentEnv) {
case 'test': return "https://test-api.com";
case 'prod': return "https://api.com";
default: return "https://dev-api.com";
}
}
void setEnv(String env) {
_currentEnv = env;
notifyListeners();
}
}
最佳实践建议
-
安全隔离:
- 生产环境关闭调试日志
if (AppConfig.env != Env.prod) { dio.interceptors.add(LogInterceptor()); } -
证书校验:
dio.httpClientAdapter = IOHttpClientAdapter( onHttpClientCreate: (client) { if (AppConfig.env == Env.prod) { client.badCertificateCallback = (cert, host, port) => host == "api.com"; } return client; } ); -
环境标识:
dio.interceptors.add(InterceptorsWrapper( onRequest: (options) { options.headers['X-Env'] = AppConfig.env.name; return options; } )); -
自动化测试:
testWidgets('生产环境配置验证', (tester) async { await tester.runAsync(() async { const String.fromEnvironment('ENV', 'prod'); expect(AppConfig.baseUrl, "https://api.com"); }); });
方案对比
| 方案 | 适用场景 | 安全性 | 动态切换 |
|---|---|---|---|
| 编译时配置 | 正式发布包 | ★★★★★ | ✗ |
| 配置文件 | 需要热更新配置 | ★★★☆☆ | △ |
| 运行时切换 | 开发调试阶段 | ★★☆☆☆ | ✓ |
选择方案时需考虑:
- 是否需要上线后修改环境(选方案二)
- 是否需要防止用户篡改(选方案一)
- 是否频繁调试切换(选方案三)
更多推荐
所有评论(0)