|
| 1 | +# Adding a New Widget to the SDUI Package |
| 2 | + |
| 3 | +This guide explains how to add support for a new widget to the Flutter SDUI package, including JSON and proto (gRPC) support. The process ensures your widget can be described by the server, parsed on the client, and rendered as a real Flutter widget. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## 1. Create the SDUI Widget Class |
| 8 | + |
| 9 | +- **Where:** `lib/src/widgets/` |
| 10 | +- **What:** Create a Dart class (e.g., `SduiMyWidget`) that extends `SduiWidget`. |
| 11 | +- **Why:** This class acts as the runtime representation of your widget in the SDUI system. It bridges the data-driven world (JSON/proto) and the actual Flutter widget tree. |
| 12 | +- **How:** |
| 13 | + - Add all properties your widget needs (e.g., text, color, children). |
| 14 | + - Implement the `toFlutterWidget()` method to convert your SDUI widget to a real Flutter widget. |
| 15 | + - If your widget has children, make sure to recursively call `toFlutterWidget()` on them. |
| 16 | + |
| 17 | +**Example:** |
| 18 | +```dart |
| 19 | +import 'package:flutter/widgets.dart'; |
| 20 | +import 'sdui_widget.dart'; |
| 21 | +
|
| 22 | +class SduiMyWidget extends SduiWidget { |
| 23 | + final String title; |
| 24 | + final Color? color; |
| 25 | + final List<SduiWidget> children; |
| 26 | + // Add other properties as needed |
| 27 | +
|
| 28 | + SduiMyWidget({ |
| 29 | + required this.title, |
| 30 | + this.color, |
| 31 | + this.children = const [], |
| 32 | + }); |
| 33 | +
|
| 34 | + @override |
| 35 | + Widget toFlutterWidget() { |
| 36 | + return MyWidget( |
| 37 | + title: title, |
| 38 | + color: color, |
| 39 | + children: children.map((c) => c.toFlutterWidget()).toList(), |
| 40 | + ); |
| 41 | + } |
| 42 | +} |
| 43 | +``` |
| 44 | +*Tip: Look at existing SDUI widgets for structure and naming conventions. Try to keep your API as close as possible to the real Flutter widget for familiarity.* |
| 45 | + |
| 46 | +--- |
| 47 | + |
| 48 | +## 2. Update the Parser(s) |
| 49 | + |
| 50 | +### a. JSON Parsing |
| 51 | + |
| 52 | +- **Where:** `lib/src/parser/sdui_proto_parser.dart` |
| 53 | +- **What:** Add logic to parse your widget from JSON and serialize it back. |
| 54 | +- **Why:** This allows the SDUI system to construct your widget from server-provided JSON, and to serialize it back for debugging or round-tripping. |
| 55 | +- **How:** |
| 56 | + - Add a case for your widget in the `parseJSON` method (e.g., `case 'my_widget': return _parseJsonMyWidget(data);`). |
| 57 | + - Implement a `_parseJsonMyWidget(Map<String, dynamic> data)` method to extract all properties from the JSON map and construct your SDUI widget. |
| 58 | + - Update the `toJson` and `_toJsonMyWidget` methods to support serialization (convert your SDUI widget back to a JSON map). |
| 59 | + - If your widget has enums or complex types, add helper methods for parsing/serializing them. |
| 60 | + |
| 61 | +**Example:** |
| 62 | +```dart |
| 63 | +static SduiMyWidget _parseJsonMyWidget(Map<String, dynamic> data) { |
| 64 | + return SduiMyWidget( |
| 65 | + title: data['title'] ?? '', |
| 66 | + color: _parseJsonColor(data['color']), |
| 67 | + children: (data['children'] as List<dynamic>? ?? []) |
| 68 | + .map((child) => parseJSON(child as Map<String, dynamic>)) |
| 69 | + .toList(), |
| 70 | + ); |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +### b. Proto Parsing |
| 75 | + |
| 76 | +- **Where:** `lib/src/parser/sdui_proto_parser.dart` |
| 77 | +- **What:** Add logic to parse your widget from proto and serialize it back. |
| 78 | +- **Why:** This allows the SDUI system to construct your widget from gRPC/proto data, and to serialize it back for server communication or round-tripping. |
| 79 | +- **How:** |
| 80 | + - Add a case for your widget in the `parseProto` and `fromProto` methods. |
| 81 | + - Implement `_parseProtoMyWidget(SduiWidgetData data)` and `myWidgetFromProto` to extract all properties from the proto message and construct your SDUI widget. |
| 82 | + - Add `myWidgetToProto` for proto serialization (convert your SDUI widget to a proto message). |
| 83 | + - Use helper methods for enums, colors, and nested children as needed. |
| 84 | + |
| 85 | +**Example:** |
| 86 | +```dart |
| 87 | +static SduiMyWidget myWidgetFromProto(SduiWidgetData data) { |
| 88 | + return SduiMyWidget( |
| 89 | + title: data.stringAttributes['title'] ?? '', |
| 90 | + color: data.hasColor() ? _parseProtoColor(data.color) : null, |
| 91 | + children: data.children.map((c) => SduiParser.parseProto(c)).toList(), |
| 92 | + ); |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +*Tip: Use the helpers and patterns from other widgets to handle enums, colors, and nested children. Consistency makes the codebase easier to maintain.* |
| 97 | + |
| 98 | +--- |
| 99 | + |
| 100 | +## 3. Update the Widget Type Enum |
| 101 | + |
| 102 | +- **Where:** `sdui.proto` (your proto definitions) |
| 103 | +- **What:** Add your widget to the `WidgetType` enum. |
| 104 | +- **Why:** This allows the server and client to communicate about your new widget type in a type-safe way. |
| 105 | +- **How:** |
| 106 | + - Add a new value (e.g., `MY_WIDGET`) to the `WidgetType` enum in your proto file. |
| 107 | + - Regenerate Dart code from your proto files (see README for instructions, usually a script in `tool/`). |
| 108 | + |
| 109 | +**Example:** |
| 110 | +```protobuf |
| 111 | +enum WidgetType { |
| 112 | + // ... existing types ... |
| 113 | + MY_WIDGET = 42; |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | +*Tip: Make sure the enum value is unique and does not conflict with existing types.* |
| 118 | + |
| 119 | +--- |
| 120 | + |
| 121 | +## 4. Add to Flutter-to-SDUI Converter |
| 122 | + |
| 123 | +- **Where:** `lib/src/parser/flutter_to_sdui.dart` |
| 124 | +- **What:** Add a case to convert a real Flutter widget to your SDUI widget. |
| 125 | +- **Why:** This enables tools and tests to convert existing Flutter code to SDUI format, and helps with migration or round-trip testing. |
| 126 | +- **How:** |
| 127 | + - Add an `else if` block for your widget type. |
| 128 | + - Map all relevant properties from the Flutter widget to your SDUI widget. |
| 129 | + - If your widget is not supported for conversion, throw an `UnimplementedError` with a clear message. |
| 130 | + |
| 131 | +**Example:** |
| 132 | +```dart |
| 133 | +else if (widget is MyWidget) { |
| 134 | + return SduiMyWidget( |
| 135 | + title: widget.title, |
| 136 | + color: widget.color, |
| 137 | + children: widget.children.map(flutterToSdui).toList(), |
| 138 | + ); |
| 139 | +} |
| 140 | +``` |
| 141 | + |
| 142 | +--- |
| 143 | + |
| 144 | +## 5. Test |
| 145 | + |
| 146 | +- **What:** Ensure your widget works end-to-end and is robust. |
| 147 | +- **Why:** Testing catches bugs early and ensures your widget behaves as expected in all supported formats. |
| 148 | +- **How:** |
| 149 | + - Add unit tests for JSON and proto parsing/serialization. |
| 150 | + - Add tests for Flutter-to-SDUI conversion. |
| 151 | + - Create sample JSON and proto definitions for your widget and verify they render correctly in a demo app or test harness. |
| 152 | + - Test edge cases (missing properties, nulls, invalid values). |
| 153 | + |
| 154 | +*Tip: Use the sample files and test cases for existing widgets as a template. Automated tests are preferred, but manual testing in a demo app is also valuable.* |
| 155 | + |
| 156 | +--- |
| 157 | + |
| 158 | +## 6. Document |
| 159 | + |
| 160 | +- **What:** Make your widget discoverable and easy to use for others. |
| 161 | +- **Why:** Good documentation helps others understand and use your widget, and encourages contributions. |
| 162 | +- **How:** |
| 163 | + - Update the main `README.md` to mention your new widget and its supported properties. |
| 164 | + - Optionally, add usage examples or sample JSON/proto snippets. |
| 165 | + - Document any limitations or special behaviors. |
| 166 | + |
| 167 | +--- |
| 168 | + |
| 169 | +## Example Checklist |
| 170 | + |
| 171 | +- [ ] Widget class in `lib/src/widgets/` |
| 172 | +- [ ] JSON parse/serialize in `sdui_proto_parser.dart` |
| 173 | +- [ ] Proto parse/serialize in `sdui_proto_parser.dart` |
| 174 | +- [ ] Enum in proto and regenerated Dart code |
| 175 | +- [ ] Flutter-to-SDUI conversion |
| 176 | +- [ ] Tests and sample data |
| 177 | +- [ ] Documentation |
| 178 | + |
| 179 | +--- |
| 180 | + |
| 181 | +**Tips & Best Practices:** |
| 182 | +- Follow the structure and naming conventions of existing widgets for consistency. |
| 183 | +- Keep your widget’s API as close as possible to the real Flutter widget for familiarity. |
| 184 | +- Only map properties that are supported by both SDUI and the underlying Flutter widget. |
| 185 | +- If your widget has complex properties (e.g., enums, nested objects), add helper methods for parsing/serialization. |
| 186 | +- If you’re unsure, look at how similar widgets are implemented in the codebase. |
| 187 | +- Use clear error messages for unsupported or unimplemented features. |
| 188 | +- Test with both minimal and maximal property sets. |
| 189 | + |
| 190 | +--- |
| 191 | + |
| 192 | +If you have questions, check the code for similar widgets or open an issue! |
0 commit comments