319 lines
11 KiB
Dart
319 lines
11 KiB
Dart
|
|
import 'package:flutter/material.dart';
|
||
|
|
import '../../core/config/environment.dart';
|
||
|
|
import '../../core/services/storage_service.dart';
|
||
|
|
|
||
|
|
/// 开发者设置页面
|
||
|
|
class DeveloperSettingsPage extends StatefulWidget {
|
||
|
|
const DeveloperSettingsPage({Key? key}) : super(key: key);
|
||
|
|
|
||
|
|
@override
|
||
|
|
State<DeveloperSettingsPage> createState() => _DeveloperSettingsPageState();
|
||
|
|
}
|
||
|
|
|
||
|
|
class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
|
||
|
|
late Environment _selectedEnvironment;
|
||
|
|
final TextEditingController _customUrlController = TextEditingController();
|
||
|
|
bool _useCustomUrl = false;
|
||
|
|
|
||
|
|
@override
|
||
|
|
void initState() {
|
||
|
|
super.initState();
|
||
|
|
_selectedEnvironment = EnvironmentConfig.current;
|
||
|
|
_loadCustomUrl();
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> _loadCustomUrl() async {
|
||
|
|
final customUrl = StorageService.getString('custom_api_url');
|
||
|
|
if (customUrl != null && customUrl.isNotEmpty) {
|
||
|
|
setState(() {
|
||
|
|
_useCustomUrl = true;
|
||
|
|
_customUrlController.text = customUrl;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> _saveSettings() async {
|
||
|
|
if (_useCustomUrl) {
|
||
|
|
// 保存自定义URL
|
||
|
|
await StorageService.setString('custom_api_url', _customUrlController.text);
|
||
|
|
// 可以在这里动态更新API配置
|
||
|
|
} else {
|
||
|
|
// 清除自定义URL
|
||
|
|
await StorageService.remove('custom_api_url');
|
||
|
|
// 使用预设环境
|
||
|
|
EnvironmentConfig.setEnvironment(_selectedEnvironment);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (mounted) {
|
||
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
||
|
|
const SnackBar(content: Text('设置已保存,重启应用后生效')),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
return Scaffold(
|
||
|
|
appBar: AppBar(
|
||
|
|
title: const Text('开发者设置'),
|
||
|
|
actions: [
|
||
|
|
IconButton(
|
||
|
|
icon: const Icon(Icons.save),
|
||
|
|
onPressed: _saveSettings,
|
||
|
|
tooltip: '保存设置',
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
body: ListView(
|
||
|
|
padding: const EdgeInsets.all(16),
|
||
|
|
children: [
|
||
|
|
// 当前环境信息
|
||
|
|
Card(
|
||
|
|
child: Padding(
|
||
|
|
padding: const EdgeInsets.all(16),
|
||
|
|
child: Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
children: [
|
||
|
|
const Text(
|
||
|
|
'当前环境信息',
|
||
|
|
style: TextStyle(
|
||
|
|
fontSize: 18,
|
||
|
|
fontWeight: FontWeight.bold,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
const SizedBox(height: 16),
|
||
|
|
_buildInfoRow('环境', EnvironmentConfig.environmentName),
|
||
|
|
_buildInfoRow('API地址', EnvironmentConfig.baseUrl),
|
||
|
|
_buildInfoRow('连接超时', '${EnvironmentConfig.connectTimeout}ms'),
|
||
|
|
_buildInfoRow('接收超时', '${EnvironmentConfig.receiveTimeout}ms'),
|
||
|
|
_buildInfoRow('日志', EnvironmentConfig.enableLogging ? '启用' : '禁用'),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
const SizedBox(height: 16),
|
||
|
|
|
||
|
|
// 环境选择
|
||
|
|
Card(
|
||
|
|
child: Padding(
|
||
|
|
padding: const EdgeInsets.all(16),
|
||
|
|
child: Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
children: [
|
||
|
|
const Text(
|
||
|
|
'环境选择',
|
||
|
|
style: TextStyle(
|
||
|
|
fontSize: 18,
|
||
|
|
fontWeight: FontWeight.bold,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
const SizedBox(height: 16),
|
||
|
|
RadioListTile<Environment>(
|
||
|
|
title: const Text('开发环境 (Development)'),
|
||
|
|
subtitle: Text(EnvironmentConfig.developmentConfig['baseUrl'] ?? ''),
|
||
|
|
value: Environment.development,
|
||
|
|
groupValue: _selectedEnvironment,
|
||
|
|
onChanged: _useCustomUrl
|
||
|
|
? null
|
||
|
|
: (value) {
|
||
|
|
if (value != null) {
|
||
|
|
setState(() {
|
||
|
|
_selectedEnvironment = value;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
),
|
||
|
|
RadioListTile<Environment>(
|
||
|
|
title: const Text('预发布环境 (Staging)'),
|
||
|
|
subtitle: Text(EnvironmentConfig.stagingConfig['baseUrl'] ?? ''),
|
||
|
|
value: Environment.staging,
|
||
|
|
groupValue: _selectedEnvironment,
|
||
|
|
onChanged: _useCustomUrl
|
||
|
|
? null
|
||
|
|
: (value) {
|
||
|
|
if (value != null) {
|
||
|
|
setState(() {
|
||
|
|
_selectedEnvironment = value;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
),
|
||
|
|
RadioListTile<Environment>(
|
||
|
|
title: const Text('生产环境 (Production)'),
|
||
|
|
subtitle: Text(EnvironmentConfig.productionConfig['baseUrl'] ?? ''),
|
||
|
|
value: Environment.production,
|
||
|
|
groupValue: _selectedEnvironment,
|
||
|
|
onChanged: _useCustomUrl
|
||
|
|
? null
|
||
|
|
: (value) {
|
||
|
|
if (value != null) {
|
||
|
|
setState(() {
|
||
|
|
_selectedEnvironment = value;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
const SizedBox(height: 16),
|
||
|
|
|
||
|
|
// 自定义API地址
|
||
|
|
Card(
|
||
|
|
child: Padding(
|
||
|
|
padding: const EdgeInsets.all(16),
|
||
|
|
child: Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
children: [
|
||
|
|
Row(
|
||
|
|
children: [
|
||
|
|
const Expanded(
|
||
|
|
child: Text(
|
||
|
|
'自定义API地址',
|
||
|
|
style: TextStyle(
|
||
|
|
fontSize: 18,
|
||
|
|
fontWeight: FontWeight.bold,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
Switch(
|
||
|
|
value: _useCustomUrl,
|
||
|
|
onChanged: (value) {
|
||
|
|
setState(() {
|
||
|
|
_useCustomUrl = value;
|
||
|
|
});
|
||
|
|
},
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
const SizedBox(height: 16),
|
||
|
|
TextField(
|
||
|
|
controller: _customUrlController,
|
||
|
|
enabled: _useCustomUrl,
|
||
|
|
decoration: const InputDecoration(
|
||
|
|
labelText: 'API基础URL',
|
||
|
|
hintText: 'http://localhost:8080/api/v1',
|
||
|
|
border: OutlineInputBorder(),
|
||
|
|
helperText: '输入完整的API基础URL',
|
||
|
|
),
|
||
|
|
),
|
||
|
|
const SizedBox(height: 8),
|
||
|
|
const Text(
|
||
|
|
'提示:开发环境常用地址\n'
|
||
|
|
'• Web: http://localhost:8080/api/v1\n'
|
||
|
|
'• Android模拟器: http://10.0.2.2:8080/api/v1\n'
|
||
|
|
'• iOS模拟器: http://localhost:8080/api/v1\n'
|
||
|
|
'• 真机: http://你的电脑IP:8080/api/v1',
|
||
|
|
style: TextStyle(
|
||
|
|
fontSize: 12,
|
||
|
|
color: Colors.grey,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
const SizedBox(height: 16),
|
||
|
|
|
||
|
|
// 预设地址快捷按钮
|
||
|
|
Card(
|
||
|
|
child: Padding(
|
||
|
|
padding: const EdgeInsets.all(16),
|
||
|
|
child: Column(
|
||
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||
|
|
children: [
|
||
|
|
const Text(
|
||
|
|
'快捷设置',
|
||
|
|
style: TextStyle(
|
||
|
|
fontSize: 18,
|
||
|
|
fontWeight: FontWeight.bold,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
const SizedBox(height: 16),
|
||
|
|
Wrap(
|
||
|
|
spacing: 8,
|
||
|
|
runSpacing: 8,
|
||
|
|
children: [
|
||
|
|
ElevatedButton.icon(
|
||
|
|
onPressed: () {
|
||
|
|
setState(() {
|
||
|
|
_useCustomUrl = true;
|
||
|
|
_customUrlController.text = 'http://localhost:8080/api/v1';
|
||
|
|
});
|
||
|
|
},
|
||
|
|
icon: const Icon(Icons.computer),
|
||
|
|
label: const Text('Localhost'),
|
||
|
|
),
|
||
|
|
ElevatedButton.icon(
|
||
|
|
onPressed: () {
|
||
|
|
setState(() {
|
||
|
|
_useCustomUrl = true;
|
||
|
|
_customUrlController.text = 'http://10.0.2.2:8080/api/v1';
|
||
|
|
});
|
||
|
|
},
|
||
|
|
icon: const Icon(Icons.phone_android),
|
||
|
|
label: const Text('Android模拟器'),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
const SizedBox(height: 32),
|
||
|
|
|
||
|
|
// 警告信息
|
||
|
|
const Card(
|
||
|
|
color: Colors.orange,
|
||
|
|
child: Padding(
|
||
|
|
padding: EdgeInsets.all(16),
|
||
|
|
child: Row(
|
||
|
|
children: [
|
||
|
|
Icon(Icons.warning, color: Colors.white),
|
||
|
|
SizedBox(width: 16),
|
||
|
|
Expanded(
|
||
|
|
child: Text(
|
||
|
|
'注意:修改环境设置需要重启应用才能生效。\n此页面仅在开发模式下可用。',
|
||
|
|
style: TextStyle(color: Colors.white),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
Widget _buildInfoRow(String label, String value) {
|
||
|
|
return Padding(
|
||
|
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||
|
|
child: Row(
|
||
|
|
children: [
|
||
|
|
SizedBox(
|
||
|
|
width: 100,
|
||
|
|
child: Text(
|
||
|
|
'$label:',
|
||
|
|
style: const TextStyle(fontWeight: FontWeight.w500),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
Expanded(
|
||
|
|
child: Text(
|
||
|
|
value,
|
||
|
|
style: const TextStyle(color: Colors.grey),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
void dispose() {
|
||
|
|
_customUrlController.dispose();
|
||
|
|
super.dispose();
|
||
|
|
}
|
||
|
|
}
|