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:
- Updating components to use the new Param class
- Migrating from MiStatelessWidgetDeprecated to MiStatelessWidget
- Replacing builder classes with static builder methods
- Updating MiFactory to use the new builder methods
Detailed Steps
Step 1: Update the Component Class
-
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'; -
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'); -
Add static componentName:
static const String componentName = 'yourComponentName';
@override
String get getComponentName => componentName; -
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); -
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,
},
),
); -
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
-
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 {
// ...
} -
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
-
Add import for your component:
import 'package:misrut_core/src/core/uiComponents/YourComponent/YourComponent.dart'; -
Register the static builder method:
Map<String, Widget Function(ComponentConfig)> newBuilders = {
// Existing builders...
YourComponent.componentName: YourComponent.builder,
}; -
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
-
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 -
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
-
Missing imports: Ensure all necessary imports are included, especially for MiCubit and ComponentConfig.
-
Type errors: The new Param class uses a different constructor pattern. Make sure to update all usages.
-
Resolving parameters: Use
resolveParam(param)instead ofparam?.valueandgetRawValue(param)to get the raw key. -
Analyzer warnings: Run the analyzer to catch any issues before testing in the app.
Benefits of the New Pattern
- Consistency: All components follow the same pattern.
- Maintainability: Simpler code structure with fewer classes.
- Type safety: Better type checking with the new Param class.
- 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.