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

1 回复

更多关于Flutter参数管理插件parameters的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在 Flutter 中,parameters 并不是一个内置的插件或库,但在开发过程中,你可能需要管理应用的配置参数、环境变量或其他动态数据。为了高效管理这些参数,Flutter 社区提供了一些插件或工具来实现这一点。以下是几种常见的方式来管理参数:


1. 使用 flutter_dotenv 管理环境变量

flutter_dotenv 是一个用于加载和管理环境变量的插件。你可以将配置参数存储在 .env 文件中,然后在应用中动态加载。

步骤如下:

  1. 添加依赖:

    dependencies:
      flutter_dotenv: ^5.1.0
    

    运行 flutter pub get 安装依赖。

  2. 创建 .env 文件:

    API_KEY=your_api_key
    BASE_URL=https://example.com
    
  3. pubspec.yaml 中声明 .env 文件:

    flutter:
      assets:
        - .env
    
  4. 在代码中加载并使用环境变量:

    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. 使用 providerriverpod 管理应用状态和参数

如果你需要在应用中动态管理参数,可以使用状态管理工具如 providerriverpod

provider 为例:

  1. 添加依赖:

    dependencies:
      provider: ^6.0.0
    
  2. 创建一个参数管理类:

    class AppParameters with ChangeNotifier {
      String _apiKey = 'default_api_key';
    
      String get apiKey => _apiKey;
    
      void updateApiKey(String newKey) {
        _apiKey = newKey;
        notifyListeners();
      }
    }
    
  3. 在应用中使用 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 是一个用于持久化简单数据的插件,适合存储用户偏好或应用配置。

步骤如下:

  1. 添加依赖:

    dependencies:
      shared_preferences: ^2.0.6
    
  2. 保存和读取参数:

    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}'),
        ),
      ),
    ),
  );
}
回到顶部