二、使用示例
NOriginSheet(),三、实现源码
import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'package:flutter_templet_project/network/RequestConfig.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// 请求环境信息缓存 const String CACHE_REQUEST_ENV = "CACHE_REQUEST_ENV"; const String CACHE_REQUEST_ENV_DEV_ORIGIN = "CACHE_REQUEST_ENV_DEV_ORIGIN"; class CacheService { CacheService._() { init(); } static final CacheService _instance = CacheService._(); factory CacheService() => _instance; static CacheService get shard => _instance; SharedPreferences? prefs; init() async { prefs ??= await SharedPreferences.getInstance(); debugPrint("init prefs: $prefs"); } /// 清除数据 Future<bool>? remove(String key) { return prefs?.remove(key); } setString(String key, String? value) { if (value == null) { return; } prefs?.setString(key, value); } String? getString(String key) { final result = prefs?.getString(key); return result; } ...... } extension CacheServiceExt on CacheService { /// 清除登录环境 clearEnv() { CacheService().remove(CACHE_REQUEST_ENV); } /// 设置登录环境 set env(APPEnvironment? val) { if (val == null) { return; } final result = val.toString(); CacheService().setString(CACHE_REQUEST_ENV, result); } /// 获取登录环境 APPEnvironment? get env { final result = CacheService().getString(CACHE_REQUEST_ENV); final val = APPEnvironment.fromString(result); return val; } /// 设置登录环境 dev 下的域名 set devOrigin(String? val) { if (val == null || val.isEmpty) { return; } CacheService().setString(CACHE_REQUEST_ENV_DEV_ORIGIN, val); } /// 堆代码 duidaima.com /// 获取登录环境 dev 下的域名 String? get devOrigin { return CacheService().getString(CACHE_REQUEST_ENV_DEV_ORIGIN); } ...... }2. RequestConfig 源码
// // RequestConfig.dart // flutter_templet_project // // Created by shang on 2024/1/6 10:58. // Copyright © 2024/1/6 shang. All rights reserved. // import 'package:flutter_templet_project/cache/CacheService.dart'; /// 当前 api 环境 enum APPEnvironment{ /// 开发环境 dev('https://*.cn'), /// 预测试环境 beta('https://*.cn'), /// 测试环境 test('https://*.cn'), /// 预发布环境 pre('https://*.cn'), /// 生产环境 prod('https://*.cn'); const APPEnvironment(this.origin,); /// 当前枚举对应的域名 final String origin; /// 字符串转类型 static APPEnvironment? fromString(String? val) { if (val == null || !val.contains(",")) { return null; } final list = val.split(","); if (list.length != 2) { return null; } final first = list[0]; final isEnumType = APPEnvironment.values.map((e) => e.name).contains(first); if (!isEnumType) { return null; } return APPEnvironment.values.firstWhere((e) => e.name == first); } @override String toString(){ if (this == APPEnvironment.dev) { return "$name,${CacheService().devOrigin ?? origin}"; } return "$name,$origin"; } } ///request config class RequestConfig { static APPEnvironment current = APPEnvironment.dev; /// 网络请求域名 static String get baseUrl { final env = CacheService().env; if (env != null) { current = env; if (env == APPEnvironment.dev) { return CacheService().devOrigin ?? current.origin; } } return current.origin; } } class RequestMsg { static String networkSucessMsg = "操作成功"; static String networkErrorMsg = "网络连接失败,请稍后重试"; static String networkErrorSeverMsg = '服务器响应超时,请稍后再试!'; static Map<String, String> statusCodeMap = <String, String>{ '401': '验票失败!', '403': '无权限访问!', '404': '404未找到!', '500': '服务器内部错误!', '502': '服务器内部错误!', }; }3. NOriginSheet 源码
// NOriginSheet.dart // flutter_templet_project // // Created by shang on 2024/1/6 11:54. // Copyright © 2024/1/6 shang. All rights reserved. // import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_templet_project/basicWidget/n_textfield.dart'; import 'package:flutter_templet_project/cache/CacheService.dart'; import 'package:flutter_templet_project/extension/widget_ext.dart'; import 'package:flutter_templet_project/network/RequestConfig.dart'; import 'package:flutter_templet_project/util/color_util.dart'; import 'package:flutter_templet_project/util/debug_log.dart'; import 'package:flutter_templet_project/vendor/easy_toast.dart'; /// 域名选择器 class NOriginSheet extends StatefulWidget { NOriginSheet({ super.key, this.onChanged, }); /// 改变回调 final void Function(APPEnvironment env, String origin)? onChanged; @override State<NOriginSheet> createState() => _NOriginSheetState(); } class _NOriginSheetState extends State<NOriginSheet> { final textController = TextEditingController(); APPEnvironment get currentEnv{ final env = CacheService().env; final result = env ?? RequestConfig.current; return result; } @override Widget build(BuildContext context) { return buildOriginSheet(); } /// 域名选择 Widget buildOriginSheet() { if (currentEnv == APPEnvironment.prod) { return const SizedBox(); } const list = APPEnvironment.values; // final currentWidget = Column( // children: [ // // Text(RequestChannel.baseUrl,), // Text("当前域名: ${currentEnv.name}",), // Text("当前域名: ${currentEnv.origin}",), // ], // ); final currentWidget = Column( children: "${currentEnv}".split(",").map((e) { return Text(e,); }).toList(), ); return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( style: TextButton.styleFrom( foregroundColor: Colors.red, padding: EdgeInsets.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), onPressed: () { showAlertSheet( message: currentWidget, actions: list.map((e) { return ListTile( dense: true, onTap: (){ Navigator.of(context).pop(); onUpdate(env: e, origin: e.origin); }, title: Text(e.name.toString()), subtitle: Text(e.origin), ); }).toList(), ); }, child: currentWidget, ), const SizedBox(width: 4,), Opacity( opacity: currentEnv == APPEnvironment.dev ? 1 : 0, child: InkWell( onTap: (){ // YLog.d("edit"); showAlertTextField( onChanged: (String value) { DebugLog.d("showAlertTextField $value"); onUpdate(env: APPEnvironment.dev, origin: value); } ); }, child: Icon(Icons.edit, color: Colors.red,) ), ), ], ), ); } void showAlertSheet({ Widget title = const Text("请选择"), Widget? message, required List<Widget> actions, }) { CupertinoActionSheet( title: title, message: message, actions: actions, cancelButton: CupertinoActionSheetAction( isDestructiveAction: true, onPressed: () { Navigator.pop(context); }, child: const Text('取消'), ), ).toShowCupertinoModalPopup(context: context); } void showAlertTextField({ Widget? title = const Text("请选择"), Widget? message, required ValueChanged<String> onChanged, }) { textController.text = RequestConfig.baseUrl; CupertinoAlertDialog( title: title ?? const Padding( padding: EdgeInsets.only(bottom: 12), child: Text("请输入", style: TextStyle( fontWeight: FontWeight.w500, ), ), ), content: message ?? NTextField( controller: textController, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w400, color: fontColor, ), isCollapsed: true, contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), onChanged: (String value) { // YLog.d("onChanged $value"); }, onSubmitted: (String value) { // YLog.d("onSubmitted $value"); }, ), actions: ["取消", "确定"].map((e) => TextButton( style: TextButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 12), ), onPressed: () { if (e == "确定") { final val = textController.text.trim(); if (!val.startsWith("http")) { EasyToast.showToast("必须以 http 开头"); return; } onChanged(val); } Navigator.pop(context); }, child: Text(e), )).toList(), ).toShowCupertinoModalPopup(context: context); } onUpdate({ required APPEnvironment env, required String origin, }) { RequestConfig.current = env; CacheService().env = env; if (env == APPEnvironment.dev) { CacheService().devOrigin = origin; } setState(() {}); widget.onChanged?.call(env, origin); } }四、总结