Skip to main content

Refactoring UI Components to the New Pattern

This guide outlines the step-by-step process for refactoring existing UI components to use the new Param and MiStatelessWidget pattern, and replacing builder classes with static builder methods.

Overview of Changes

The refactoring involves:

  1. Updating components to use the new Param class
  2. Migrating from MiStatelessWidgetDeprecated to MiStatelessWidget
  3. Replacing builder classes with static builder methods
  4. Updating MiFactory to use the new builder methods

Detailed Steps

Step 1: Update the Component Class

  1. Update imports:

    // Remove deprecated imports
    import 'package:misrut_core/src/core/models/ParamDeprecated.dart';
    import 'package:misrut_core/src/core/uiComponents/MiStatelessWidgetDeprecated.dart';

    // Add new imports
    import 'package:misrut_core/src/core/models/ComponentConfig.dart';
    import 'package:misrut_core/src/core/models/Param.dart';
    import 'package:misrut_core/src/core/uiComponents/MiStatelessWidget.dart';
    import 'package:misrut_core/src/core/cubit/MiCubit.dart';
  2. Update class properties:

    // Old approach
    final Param<bool, String>? value;
    final Param<String, String>? label;

    // New approach
    final Param<bool, String> value = const Param('value');
    final Param<String, String> label = const Param('label');
  3. Add static componentName:

    static const String componentName = 'yourComponentName';

    @override
    String get getComponentName => componentName;
  4. Update constructors:

    // Old constructor
    const YourComponent({
    super.key,
    required super.cubit,
    required super.widgetArgs,
    required super.padding,
    required super.margin,
    required super.alignment,
    required super.color,
    required super.width,
    required super.height,
    required this.value,
    required this.label,
    });

    // New constructor
    const YourComponent({
    super.key,
    required super.componentConfig,
    });

    // Add static builder constructor
    const YourComponent.builder(ComponentConfig componentConfig, {super.key})
    : super(componentConfig: componentConfig);
  5. Update internal constructor:

    // Old internal constructor
    YourComponent.internal({
    super.key,
    super.cubit,
    super.widgetArgs,
    String? color,
    num? width,
    num? height,
    List? padding,
    List? margin,
    String? alignment,
    String? value,
    String? label,
    }) : value = Param(value),
    label = Param(label),
    super(
    color: Param(color, cubit?.themeRepository.helper.get),
    width: Param(width, DoubleHelper.get),
    height: Param(height, DoubleHelper.get),
    padding: Param(padding, EdgeInsetsHelper.get),
    margin: Param(margin, EdgeInsetsHelper.get),
    alignment: Param(alignment, AlignmentHelper.get),
    );

    // New internal constructor
    YourComponent.internal({
    super.key,
    MiCubit? cubit,
    Map? widgetArgs,
    String? color,
    num? width,
    num? height,
    List? padding,
    List? margin,
    String? alignment,
    String? value,
    String? label,
    }) : super(
    componentConfig: ComponentConfig(
    cubit: cubit,
    widgetArgs: widgetArgs,
    config: {
    'color': color,
    'width': width,
    'height': height,
    'padding': padding,
    'margin': margin,
    'alignment': alignment,
    'value': value,
    'label': label,
    },
    ),
    );
  6. Update buildWidget method:

    // Old approach
    Widget buildWidget(BuildContext context) {
    if (resolveParam(value) == null) {
    // ...
    }
    // ...
    }

    // New approach
    Widget buildWidget(BuildContext context) {
    final resolvedValue = resolveParam(value);
    final valueKey = getRawValue(value);

    if (resolvedValue == null) {
    // ...
    }
    // ...
    }

Step 2: Update the Builder Class

  1. Mark the builder class as deprecated:

    @Deprecated("Use YourComponent.builder instead. This class will be removed by the end of February 2025")
    class YourComponentBuilder implements IMiWidgetBuilder {
    // ...
    }
  2. Simplify the build method:

    // Old approach
    @override
    Widget build(
    Map component,
    MiFactory factory,
    MiCubit cubit,
    Map widgetArgs,
    ) {
    return YourComponent(
    cubit: cubit,
    widgetArgs: widgetArgs,
    color: Param.nullable<Color, String>(
    component['color'], cubit.themeRepository.helper.get),
    width: Param.nullable<double, num>(component['width'], DoubleHelper.get),
    // ...
    );
    }

    // New approach
    @override
    Widget build(
    Map component,
    MiFactory factory,
    MiCubit cubit,
    Map widgetArgs,
    ) {
    return YourComponent(
    componentConfig: ComponentConfig(
    config: component,
    cubit: cubit,
    widgetArgs: widgetArgs,
    ),
    );
    }

Step 3: Update MiFactory

  1. Add import for your component:

    import 'package:misrut_core/src/core/uiComponents/YourComponent/YourComponent.dart';
  2. Register the static builder method:

    Map<String, Widget Function(ComponentConfig)> newBuilders = {
    // Existing builders...
    YourComponent.componentName: YourComponent.builder,
    };
  3. Comment out or remove the old builder from _coreBuilders:

    static const Map<String, IMiWidgetBuilder> _coreBuilders = {
    // Other builders...
    // YourComponentBuilder.componentName: YourComponentBuilder(), // Comment out or remove this line
    // Other builders...
    };

Step 4: Testing

  1. Run the analyzer to check for issues:

    dart analyze lib\src\core\uiComponents\YourComponent\YourComponent.dart
    dart analyze lib\src\core\uiComponents\YourComponent\YourComponentBuilder.dart
    dart analyze lib\src\core\MiFactory.dart
  2. Test the component in the app to ensure it works correctly with both the old and new approaches.

Example: Before and After

Before Refactoring

// YourComponent.dart
import 'package:flutter/material.dart';
import 'package:misrut_core/src/core/models/ParamDeprecated.dart';
import 'package:misrut_core/src/core/uiComponents/MiStatelessWidgetDeprecated.dart';

class YourComponent extends MiStatelessWidget {
final Param<bool, String>? value;

@override
String get componentName => 'yourComponent';

const YourComponent({
super.key,
required super.cubit,
required super.widgetArgs,
required super.padding,
required super.margin,
required super.alignment,
required super.color,
required super.width,
required super.height,
required this.value,
});

// ...
}

// YourComponentBuilder.dart
class YourComponentBuilder implements IMiWidgetBuilder {
static const componentName = 'yourComponent';

@override
Widget build(
Map component,
MiFactory factory,
MiCubit cubit,
Map widgetArgs,
) {
return YourComponent(
cubit: cubit,
widgetArgs: widgetArgs,
// ...
);
}
}

After Refactoring

// YourComponent.dart
import 'package:flutter/material.dart';
import 'package:misrut_core/src/core/models/ComponentConfig.dart';
import 'package:misrut_core/src/core/models/Param.dart';
import 'package:misrut_core/src/core/uiComponents/MiStatelessWidget.dart';

class YourComponent extends MiStatelessWidget {
final Param<bool, String> value = const Param('value');

static const String componentName = 'yourComponent';
@override
String get getComponentName => componentName;

const YourComponent({
super.key,
required super.componentConfig,
});

const YourComponent.builder(ComponentConfig componentConfig, {super.key})
: super(componentConfig: componentConfig);

// ...
}

// YourComponentBuilder.dart
@Deprecated("Use YourComponent.builder instead. This class will be removed by the end of February 2025")
class YourComponentBuilder implements IMiWidgetBuilder {
static const componentName = 'yourComponent';

@override
Widget build(
Map component,
MiFactory factory,
MiCubit cubit,
Map widgetArgs,
) {
return YourComponent(
componentConfig: ComponentConfig(
config: component,
cubit: cubit,
widgetArgs: widgetArgs,
),
);
}
}

// MiFactory.dart
// In _coreBuilders map
static const Map<String, IMiWidgetBuilder> _coreBuilders = {
// Other builders...
// YourComponentBuilder.componentName: YourComponentBuilder(), // Commented out
// Other builders...
};

// In newBuilders map
Map<String, Widget Function(ComponentConfig)> newBuilders = {
// Existing builders...
YourComponent.componentName: YourComponent.builder,
};

Common Issues and Solutions

  1. Missing imports: Ensure all necessary imports are included, especially for MiCubit and ComponentConfig.

  2. Type errors: The new Param class uses a different constructor pattern. Make sure to update all usages.

  3. Resolving parameters: Use resolveParam(param) instead of param?.value and getRawValue(param) to get the raw key.

  4. Analyzer warnings: Run the analyzer to catch any issues before testing in the app.

Benefits of the New Pattern

  1. Consistency: All components follow the same pattern.
  2. Maintainability: Simpler code structure with fewer classes.
  3. Type safety: Better type checking with the new Param class.
  4. Future-proofing: Deprecated classes will be removed by February 2025.

By following this guide, you can successfully refactor your UI components to use the new pattern and ensure your code remains maintainable and up-to-date.