Flutter参数管理插件parameters的使用
Flutter参数管理插件parameters的使用
参数宏(parameters macro)
实验性的Dart参数组实现通过Dart静态元编程(macro)实现的构造函数和函数参数列表作为隐式参数组 #2234。
请注意:
- 这是一项实验性功能的实验性实现。可能会出问题。
- 此包不工作,仅用于验证参数组提案。
- 由于其有限且暂时的状态,许可证也是有限的。以后可能会更改。
目标
在面向对象语言中,类和功能部分有良好的工具支持(如extends, implements, override, super等)。但构造函数和方法参数列表却没有。结果是长而重复的代码段。
此宏改变了我们处理参数的方式:所有构造函数、方法和函数定义都是隐式的参数组。它们中的任何一个都可以通过宏注解添加到其他构造函数、方法和函数定义中。宏会执行以下操作:
- 将源参数添加到目标参数列表。
- 通过多次覆盖具有相同名称的参数来清除冲突的参数项。
- 处理this和super。
- 处理默认值。
示例1:Flutter FloatingActionButton
在floating_action_button.dart文件中,实际类共有558行,包括201行注释,所以实际代码为357行。默认构造函数参数列表为23行,还有3个附加构造函数:
- FloatingActionButton.small: 21行
- FloatingActionButton.large: 21行
- FloatingActionButton.extended: 26行
难以跟踪,容易遗漏。
const FloatingActionButton({
super.key,
this.child,
this.tooltip,
this.foregroundColor,
this.backgroundColor,
this.focusColor,
this.hoverColor,
this.splashColor,
this.heroTag = const _DefaultHeroTag(),
this.elevation,
this.focusElevation,
this.hoverElevation,
this.highlightElevation,
this.disabledElevation,
required this.onPressed,
this.mouseCursor,
this.mini = false,
this.shape,
this.clipBehavior = Clip.none,
this.focusNode,
this.autofocus = false,
this.materialTapTargetSize,
this.isExtended = false,
this.enableFeedback,
}) : //asserts removed for clarity.
_floatingActionButtonType = mini ? _FloatingActionButtonType.small : _FloatingActionButtonType.regular,
_extendedLabel = null,
extendedIconLabelSpacing = null,
extendedPadding = null,
extendedTextStyle = null;
const FloatingActionButton.small({
super.key,
this.child,
this.tooltip,
this.foregroundColor,
this.backgroundColor,
this.focusColor,
this.hoverColor,
this.splashColor,
this.heroTag = const _DefaultHeroTag(),
this.elevation,
this.focusElevation,
this.hoverElevation,
this.highlightElevation,
this.disabledElevation,
required this.onPressed,
this.mouseCursor,
this.shape,
this.clipBehavior = Clip.none,
this.focusNode,
this.autofocus = false,
this.materialTapTargetSize,
this.enableFeedback,
}) : // asserts removed
_floatingActionButtonType = _FloatingActionButtonType.small,
mini = true,
isExtended = false,
_extendedLabel = null,
extendedIconLabelSpacing = null,
extendedPadding = null,
extendedTextStyle = null;
// two more similar...
使用此宏包,我们可以重写构造函数:不再重复整个参数列表,而是让宏添加所有来自FloatingActionButton构造函数的参数。
[@ParamFrom](/user/ParamFrom)('FloatingActionButton.')
OptimizedFloatingActionButton.small({
this.autofocus = false,
this.clipBehavior = Clip.none,
})
[@ParamFrom](/user/ParamFrom)('FloatingActionButton.')
将复制所有来自默认构造函数的参数,并用默认值覆盖两个参数。
我们并没有丢失任何东西。增强后的代码包含所有参数:
// 生成的代码
augment class OptimizedFloatingActionButton {
augment OptimizedFloatingActionButton.small({
prefix0.Key? key,
prefix1.Widget? child,
prefix2.String? tooltip,
prefix3.Color? foregroundColor,
prefix3.Color? backgroundColor,
prefix3.Color? focusColor,
prefix3.Color? hoverColor,
prefix3.Color? splashColor,
prefix2.Object? heroTag,
prefix2.double? elevation,
prefix2.double? focusElevation,
prefix2.double? hoverElevation,
prefix2.double? highlightElevation,
prefix2.double? disabledElevation,
required void Function()? onPressed,
prefix4.MouseCursor? mouseCursor,
prefix2.bool mini,
prefix5.ShapeBorder? shape,
prefix3.Clip clipBehavior,
prefix6.FocusNode? focusNode,
prefix2.bool autofocus,
prefix7.MaterialTapTargetSize? materialTapTargetSize,
prefix2.bool isExtended,
prefix2.bool? enableFeedback,});
}
使用参数宏,我们可以节省3 * 21 = 63行代码 => 占总代码的17%。如果进一步考虑,默认构造函数的FloatingActionButton有17个命令字段来自RawMaterialButton构造函数。这样可以避免80行重复代码,节省22%。
生成的代码不仅更小,而且更容易理解,因为它清楚地表明哪些是相同的,哪些是不同的。这是一个Dart特性,但Flutter将从中受益匪浅。
示例2:Flutter TextFormField
它将两个世界结合起来:FormField和TextField,你可以在其构造函数中看到这一点。
TextFormField({
super.key,
this.controller,
String? initialValue,
FocusNode? focusNode,
InputDecoration? decoration = const InputDecoration(),
TextInputType? keyboardType,
TextCapitalization textCapitalization = TextCapitalization.none,
TextInputAction? textInputAction,
TextStyle? style,
StrutStyle? strutStyle,
TextDirection? textDirection,
TextAlign textAlign = TextAlign.start,
TextAlignVertical? textAlignVertical,
bool autofocus = false,
bool readOnly = false,
@Deprecated(
'Use `contextMenuBuilder` instead. '
'This feature was deprecated after v3.3.0-0.5.pre.',
)
ToolbarOptions? toolbarOptions,
bool? showCursor,
String obscuringCharacter = '•',
bool obscureText = false,
bool autocorrect = true,
SmartDashesType? smartDashesType,
SmartQuotesType? smartQuotesType,
bool enableSuggestions = true,
MaxLengthEnforcement? maxLengthEnforcement,
int? maxLines = 1,
int? minLines,
bool expands = false,
int? maxLength,
this.onChanged,
GestureTapCallback? onTap,
bool onTapAlwaysCalled = false,
TapRegionCallback? onTapOutside,
VoidCallback? onEditingComplete,
ValueChanged<String>? onFieldSubmitted,
super.onSaved,
super.validator,
List<TextInputFormatter>? inputFormatters,
bool? enabled,
bool? ignorePointers,
double cursorWidth = 2.0,
double? cursorHeight,
Radius? cursorRadius,
Color? cursorColor,
Color? cursorErrorColor,
Brightness? keyboardAppearance,
EdgeInsets scrollPadding = const EdgeInsets.all(20.0),
bool? enableInteractiveSelection,
TextSelectionControls? selectionControls,
InputCounterWidgetBuilder? buildCounter,
ScrollPhysics? scrollPhysics,
Iterable<String>? autofillHints,
AutovalidateMode? autovalidateMode,
ScrollController? scrollController,
super.restorationId,
bool enableIMEPersonalizedLearning = true,
MouseCursor? mouseCursor,
EditableTextContextMenuBuilder? contextMenuBuilder = _defaultContextMenuBuilder,
SpellCheckConfiguration? spellCheckConfiguration,
TextMagnifierConfiguration? magnifierConfiguration,
UndoHistoryController? undoController,
AppPrivateCommandCallback? onAppPrivateCommand,
bool? cursorOpacityAnimates,
ui.BoxHeightStyle selectionHeightStyle = ui.BoxHeightStyle.tight,
ui.BoxWidthStyle selectionWidthStyle = ui.BoxWidthStyle.tight,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
ContentInsertionConfiguration? contentInsertionConfiguration,
MaterialStatesController? statesController,
Clip clipBehavior = Clip.hardEdge,
bool scribbleEnabled = true,
bool canRequestFocus = true,
})
此宏可以简化代码如下:
[@ParamFrom](/user/ParamFrom)('TextField.', library: 'text_field.dart')
[@ParamFrom](/user/ParamFrom)('FormField.', library: 'package:flutter/widgets.dart')
TextFormField()
更多关于Flutter参数管理插件parameters的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter参数管理插件parameters的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在 Flutter 中,parameters
并不是一个内置的插件或库,但在开发过程中,你可能需要管理应用的配置参数、环境变量或其他动态数据。为了高效管理这些参数,Flutter 社区提供了一些插件或工具来实现这一点。以下是几种常见的方式来管理参数:
1. 使用 flutter_dotenv
管理环境变量
flutter_dotenv
是一个用于加载和管理环境变量的插件。你可以将配置参数存储在 .env
文件中,然后在应用中动态加载。
步骤如下:
-
添加依赖:
dependencies: flutter_dotenv: ^5.1.0
运行
flutter pub get
安装依赖。 -
创建
.env
文件:API_KEY=your_api_key BASE_URL=https://example.com
-
在
pubspec.yaml
中声明.env
文件:flutter: assets: - .env
-
在代码中加载并使用环境变量:
import 'package:flutter_dotenv/flutter_dotenv.dart'; Future<void> main() async { await dotenv.load(fileName: ".env"); runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { final apiKey = dotenv.env['API_KEY']; final baseUrl = dotenv.env['BASE_URL']; return MaterialApp( home: Scaffold( body: Center( child: Text('API Key: $apiKey, Base URL: $baseUrl'), ), ), ); } }
2. 使用 provider
或 riverpod
管理应用状态和参数
如果你需要在应用中动态管理参数,可以使用状态管理工具如 provider
或 riverpod
。
以 provider
为例:
-
添加依赖:
dependencies: provider: ^6.0.0
-
创建一个参数管理类:
class AppParameters with ChangeNotifier { String _apiKey = 'default_api_key'; String get apiKey => _apiKey; void updateApiKey(String newKey) { _apiKey = newKey; notifyListeners(); } }
-
在应用中使用
Provider
提供参数:void main() { runApp( ChangeNotifierProvider( create: (context) => AppParameters(), child: MyApp(), ), ); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { final parameters = Provider.of<AppParameters>(context); return MaterialApp( home: Scaffold( body: Center( child: Text('API Key: ${parameters.apiKey}'), ), ), ); } }
3. 使用 shared_preferences
持久化参数
shared_preferences
是一个用于持久化简单数据的插件,适合存储用户偏好或应用配置。
步骤如下:
-
添加依赖:
dependencies: shared_preferences: ^2.0.6
-
保存和读取参数:
import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; class SettingsPage extends StatefulWidget { @override _SettingsPageState createState() => _SettingsPageState(); } class _SettingsPageState extends State<SettingsPage> { final _prefs = SharedPreferences.getInstance(); String _apiKey = ''; @override void initState() { super.initState(); _loadSettings(); } Future<void> _loadSettings() async { final prefs = await _prefs; setState(() { _apiKey = prefs.getString('api_key') ?? 'default_api_key'; }); } Future<void> _saveSettings(String newKey) async { final prefs = await _prefs; await prefs.setString('api_key', newKey); _loadSettings(); // Reload after saving } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Settings')), body: Column( children: [ Text('API Key: $_apiKey'), TextField( onSubmitted: _saveSettings, decoration: InputDecoration( labelText: 'Enter API Key', ), ), ], ), ); } }
4. 自定义参数管理
如果需要更灵活的参数管理,可以创建一个单独的类来封装所有参数,并使用单例模式或依赖注入来访问它。
class AppParameters {
static final AppParameters _instance = AppParameters._internal();
String apiKey = 'default_api_key';
String baseUrl = 'https://example.com';
factory AppParameters() {
return _instance;
}
AppParameters._internal();
}
void main() {
final parameters = AppParameters();
runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: Text('API Key: ${parameters.apiKey}'),
),
),
),
);
}