-
-
Notifications
You must be signed in to change notification settings - Fork 94
Added documentation for conditional rendering in Stac #411
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
830ca54
created a conditional rendering doc
Chidwan3578 7787a88
requested changes are made
Chidwan3578 c67c871
Merge branch 'dev' into dev
divyanshub024 e5889ec
required changes made
Chidwan3578 1e204bc
Merge branch 'dev' into dev
divyanshub024 a0b0428
Merge branch 'dev' into dev
divyanshub024 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,356 @@ | ||
| --- | ||
| title: "Conditional Rendering" | ||
| description: "Learn how to use conditional rendering in Stac to dynamically show or hide widgets based on runtime conditions" | ||
| --- | ||
|
|
||
| Conditional rendering allows you to display different UI components based on runtime conditions. Stac provides a powerful conditional widget that evaluates boolean expressions and renders different widgets accordingly. | ||
|
|
||
| ## Overview | ||
|
|
||
| The conditional widget (`conditional`) evaluates a boolean expression at runtime and renders one of two widgets: | ||
| - **`ifTrue`**: Rendered when the condition evaluates to `true` | ||
| - **`ifFalse`**: Rendered when the condition evaluates to `false` (optional - if omitted, nothing is rendered) | ||
|
|
||
| This enables dynamic UI behavior without requiring app updates, making it perfect for A/B testing, feature flags, user-specific content, and responsive layouts. | ||
|
|
||
| ## How It Works | ||
|
|
||
| ### Runtime Evaluation | ||
|
|
||
| Conditions are evaluated at runtime using Stac's `ExpressionResolver`, which parses and evaluates boolean expressions. The evaluation happens during widget parsing, ensuring that the correct widget is rendered based on the current state. | ||
|
|
||
| ### Evaluation Process | ||
|
|
||
| 1. The condition string is parsed by `ExpressionResolver.evaluate()` | ||
| 2. The expression is evaluated as a boolean value | ||
| 3. If `true`, the `ifTrue` widget is rendered | ||
| 4. If `false`, the `ifFalse` widget is rendered (or an empty widget if `ifFalse` is not provided) | ||
|
|
||
| ## Supported Conditions and Operators | ||
|
|
||
| Stac's expression resolver supports a wide range of operators and expression types: | ||
|
|
||
| ### Comparison Operators | ||
|
|
||
| | Operator | Description | Example | | ||
| |----------|-------------|---------| | ||
| | `==` | Equality | `"5 == 5"` → `true` | | ||
| | `!=` | Inequality | `"5 != 3"` → `true` | | ||
| | `>` | Greater than | `"10 > 5"` → `true` | | ||
| | `<` | Less than | `"3 < 5"` → `true` | | ||
| | `>=` | Greater than or equal | `"5 >= 5"` → `true` | | ||
| | `<=` | Less than or equal | `"3 <= 5"` → `true` | | ||
|
|
||
| ### Logical Operators | ||
|
|
||
| | Operator | Description | Example | | ||
| |----------|-------------|---------| | ||
| | `&&` | Logical AND | `"true && false"` → `false` | | ||
| | `\|\|` | Logical OR | `"true \|\| false"` → `true` | | ||
|
|
||
| ### Mathematical Operations | ||
|
|
||
| | Operator | Description | Example | | ||
| |----------|-------------|---------| | ||
| | `+` | Addition | `"5 + 3"` → `8` | | ||
| | `-` | Subtraction | `"10 - 3"` → `7` | | ||
| | `*` | Multiplication | `"5 * 2"` → `10` | | ||
| | `/` | Division | `"10 / 2"` → `5` | | ||
| | `%` | Modulo | `"10 % 3"` → `1` | | ||
|
|
||
| ### Supported Values | ||
|
|
||
| - **Numbers**: Integers (`5`) and decimals (`3.14`) | ||
| - **Booleans**: `true` and `false` | ||
| - **Strings**: String literals (`"hello"` or `'world'`) | ||
| - **Null**: `null` for null checks | ||
|
|
||
| ### Expression Features | ||
|
|
||
| - **Parentheses**: Group operations `(10 + 5) * 2` | ||
| - **Nested expressions**: Complex boolean logic `true && (false || true)` | ||
| - **String comparison**: `"Flutter" == "Flutter"` → `true` | ||
| - **Null checks**: `value == null` or `value != null` | ||
|
|
||
| ## Basic Usage | ||
|
|
||
| ### Simple Boolean Comparison | ||
|
|
||
| ```json | ||
| { | ||
| "type": "conditional", | ||
| "condition": "5 > 3", | ||
| "ifTrue": { | ||
| "type": "text", | ||
| "data": "5 is greater than 3" | ||
| }, | ||
| "ifFalse": { | ||
| "type": "text", | ||
| "data": "5 is not greater than 3" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### String Comparison | ||
|
|
||
| ```json | ||
| { | ||
| "type": "conditional", | ||
| "condition": "Flutter == Flutter", | ||
divyanshub024 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "ifTrue": { | ||
| "type": "container", | ||
| "padding": 16, | ||
| "decoration": { | ||
| "color": "#E8F5E9", | ||
| "borderRadius": 8 | ||
| }, | ||
| "child": { | ||
| "type": "text", | ||
| "data": "Strings are equal" | ||
| } | ||
| }, | ||
| "ifFalse": { | ||
| "type": "text", | ||
| "data": "Strings are not equal" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Mathematical Expression | ||
|
|
||
| ```json | ||
| { | ||
| "type": "conditional", | ||
| "condition": "(10 + 5) * 2 == 30", | ||
| "ifTrue": { | ||
| "type": "text", | ||
| "data": "The calculation is correct" | ||
| }, | ||
| "ifFalse": { | ||
| "type": "text", | ||
| "data": "The calculation is incorrect" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Logical Operators | ||
|
|
||
| ```json | ||
| { | ||
| "type": "conditional", | ||
| "condition": "true && (false || true)", | ||
| "ifTrue": { | ||
| "type": "text", | ||
| "data": "Logical expression is TRUE" | ||
| }, | ||
| "ifFalse": { | ||
| "type": "text", | ||
| "data": "Logical expression is FALSE" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Conditional Without ifFalse | ||
|
|
||
| If `ifFalse` is not provided and the condition evaluates to `false`, an empty widget (`SizedBox`) is rendered: | ||
|
|
||
| ```json | ||
| { | ||
| "type": "conditional", | ||
| "condition": "false", | ||
| "ifTrue": { | ||
| "type": "text", | ||
| "data": "This will not be shown" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Advanced Usage | ||
|
|
||
| ### Nested Conditionals | ||
|
|
||
| You can nest conditional widgets to create complex decision trees: | ||
|
|
||
| ```json | ||
| { | ||
| "type": "conditional", | ||
| "condition": "3 < 5", | ||
| "ifTrue": { | ||
| "type": "conditional", | ||
| "condition": "10 > 8", | ||
| "ifTrue": { | ||
| "type": "text", | ||
| "data": "Both conditions are TRUE" | ||
| }, | ||
| "ifFalse": { | ||
| "type": "text", | ||
| "data": "First condition is TRUE, but second is FALSE" | ||
| } | ||
| }, | ||
| "ifFalse": { | ||
| "type": "text", | ||
| "data": "First condition is FALSE" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Real-World Example: User Authentication | ||
|
|
||
| ```json | ||
| { | ||
| "type": "scaffold", | ||
| "appBar": { | ||
| "type": "appBar", | ||
| "title": { | ||
| "type": "text", | ||
| "data": "Dashboard" | ||
| } | ||
| }, | ||
| "body": { | ||
| "type": "conditional", | ||
| "condition": "{{user.isLoggedIn}} == true", | ||
| "ifTrue": { | ||
| "type": "column", | ||
| "children": [ | ||
| { | ||
| "type": "text", | ||
| "data": "Welcome back!" | ||
| }, | ||
| { | ||
| "type": "elevatedButton", | ||
| "onPressed": { | ||
| "actionType": "navigate", | ||
| "routeName": "profile" | ||
| }, | ||
| "child": { | ||
| "type": "text", | ||
| "data": "View Profile" | ||
| } | ||
| } | ||
| ] | ||
| }, | ||
| "ifFalse": { | ||
| "type": "column", | ||
| "children": [ | ||
| { | ||
| "type": "text", | ||
| "data": "Please sign in" | ||
| }, | ||
| { | ||
| "type": "elevatedButton", | ||
| "onPressed": { | ||
| "actionType": "navigate", | ||
| "routeName": "login" | ||
| }, | ||
| "child": { | ||
| "type": "text", | ||
| "data": "Sign In" | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Feature Flag Example | ||
|
|
||
| ```json | ||
| { | ||
| "type": "conditional", | ||
| "condition": "featureFlags.newUI == true", | ||
| "ifTrue": { | ||
| "type": "container", | ||
| "decoration": { | ||
| "color": "#4CAF50" | ||
| }, | ||
| "child": { | ||
| "type": "text", | ||
| "data": "New UI Enabled" | ||
| } | ||
| }, | ||
| "ifFalse": { | ||
| "type": "container", | ||
| "decoration": { | ||
| "color": "#FF9800" | ||
| }, | ||
| "child": { | ||
| "type": "text", | ||
| "data": "Legacy UI" | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| ### Conditional Rendering using Stac DSL (Dart) | ||
|
|
||
| Stac also provides a Dart-based DSL which is the recommended approach for defining UI programmatically. | ||
|
|
||
| ```dart | ||
| StacConditional( | ||
| condition: '{{user.isLoggedIn}}', | ||
| ifTrue: StacText( | ||
| data: 'Welcome back!', | ||
| ), | ||
| ifFalse: StacText( | ||
| data: 'Please log in', | ||
| ), | ||
| ); | ||
|
|
||
|
|
||
| ## JSON Schema | ||
|
|
||
| ### Conditional Widget Properties | ||
|
|
||
| | Property | Type | Required | Description | | ||
| |----------|------|----------|-------------| | ||
| | `type` | `String` | Yes | Must be `"conditional"` | | ||
| | `condition` | `String` | Yes | Boolean expression to evaluate | | ||
| | `ifTrue` | `Map<String, dynamic>` | Yes | Widget to render when condition is `true` | | ||
| | `ifFalse` | `Map<String, dynamic>` | No | Widget to render when condition is `false` | | ||
|
|
||
| ## Best Practices | ||
|
|
||
| 1. **Keep conditions simple**: Complex expressions can be hard to debug. Consider breaking them into nested conditionals for clarity. | ||
|
|
||
| 2. **Always provide `ifFalse`**: While optional, providing an `ifFalse` widget improves user experience and makes your UI more predictable. | ||
|
|
||
| 3. **Use parentheses**: When combining multiple operators, use parentheses to ensure correct evaluation order. | ||
|
|
||
| 4. **Test your conditions**: Verify that your conditions evaluate correctly before deploying to production. | ||
|
|
||
| 5. **Document complex logic**: If using complex nested conditionals, consider adding comments or documentation explaining the logic. | ||
|
|
||
| ## Limitations | ||
|
|
||
| **Variable access limitations**: | ||
| Conditions do not have access to local widget state or arbitrary Dart variables. | ||
| However, they *can* evaluate variables coming from resolved data sources such as: | ||
| - Network responses | ||
| - Initial data payloads | ||
| - Context variables injected into the Stac tree | ||
|
|
||
| Variables must be referenced using interpolation syntax (`{{variable}}`). | ||
|
|
||
| **No function calls** | ||
| Custom Dart functions cannot be invoked inside conditions. Only built-in | ||
| expression operators and basic math comparisons are supported. | ||
|
|
||
| **String operations** | ||
| Basic string concatenation is supported, but advanced string manipulation | ||
| (e.g. regex, substring, replace) is not available within conditional expressions. | ||
|
|
||
| ## Error Handling | ||
|
|
||
| If a condition cannot be evaluated (due to syntax errors or unsupported operations), the expression resolver will: | ||
| 1. Attempt to evaluate the expression | ||
| 2. If evaluation fails, return the original expression string | ||
| 3. The conditional widget will treat non-boolean values as `false` | ||
|
|
||
| To avoid errors: | ||
| - Ensure your expressions use supported operators | ||
| - Test expressions before deploying | ||
| - Use simple, clear conditions | ||
|
|
||
| ## Examples | ||
|
|
||
| For more examples, see the [conditional example](https://github.com/StacDev/stac/tree/dev/examples/stac_gallery/assets/json/conditional_example.json) in the Stac Gallery app. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,6 +43,29 @@ This approach separates your app's presentation layer from its business logic, e | |
| - 💾 Caching: Intelligent screen caching with configurable strategies. | ||
| - 🔌 Extensible: Add custom widgets, actions, and native integrations. | ||
|
|
||
| ## Stac DSL/Dart Example | ||
|
|
||
| The Stac DSL allows you to define widgets directly in Dart, which is then compiled to JSON. Here's a simple example: | ||
|
|
||
| ```dart | ||
| import 'package:stac/stac.dart'; | ||
|
|
||
| @StacWidget() | ||
| class MyButton extends StatelessWidget { | ||
| const MyButton({super.key}); | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| return ElevatedButton( | ||
| onPressed: () {}, | ||
| child: const Text('Click me'), | ||
| ); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| For more details, see the [documentation](https://docs.stac.dev/). | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
45
to
67
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey, this change is not required. |
||
|
|
||
| ## Documentation | ||
|
|
||
| - 📚 **[Full Documentation](https://docs.stac.dev/)** – Complete guides and API reference | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also add the Stac DSL/Dart example as well. Since it's the recommended way for Stac.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added an example for DSL/Dart.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add the Dart example for all JSONs?